Hi @Sebastian Andaur Polanco , @Mike Murphy - MurphysMind and @Michael McCloy ,
Status: solved (with a workaround)
assumptions
You have already done the following:
- The VPN Gateway is configured in SKU 'BASIC' (Generation 1) with SSTP-authentication
- You have. Windows 11 ARM VM running in Parallels Desktop on MacOS
- Imported the PFX-file or P12-File containing the
VPN Public Root Certificate
,Client Certificate
andPrivateKey
in your Windows 11 ARM VM.
The problem
Unable to get a VPN-connection working on Windows 11 ARM (VM in Parallels) on a Macbook Pro with Apple Silicon to an Azure P2S Gateway with SSTP certificate authentication.
I was a bit saddened by the fact that Parallels and/ or OSX does something with the Windows 11 ARM that makes the cmstp.exe
unfindable for the amd64-installer, although it is part of the Windows 11 ARM OS and is stored in C:\Windows\SysWOW64
The solution (workaround)
Step 1: Download the VPN-client (see image below):
It will take a few minutes to generate and download the zip-file <vpn-gateway-name>.zip. Once downloaded open te file and extract it in full. it will contain the following directories:
- Generic
- WindowsAmd64
- WindowsPowershell
- WindowsX86
the directory of importance here is WindowsPowerShell
.
Step 2: Extract the powershell-script and run it
This directory contains a powershell-script named VpnProfileSetup.ps1
. This file contains the powershell-script to create the VPN-connection without help from executable cmstp.exe
and does the following:
- creates the VPN-connection with the name of your azure-vnet in which the Azure P2s VPN Gateway is deployed.
- Adds the respective network-routes and dns-servers to the VPN-connection created in [1]
you need to execute it with the following command to bypass the execution-policy 'disabled' that is set by default on Windows 11 ARM:
powershell -ExecutionPolicy Bypass -File .\VpnProfileSetup.ps1
this will generate the following output: Don't be scared of the error-message; it simply indicates it cant find the VPN-connection on your system and therefore will create it. With the following result:
Great!! we've created the VPN-connection with SSTP-authentication to the Azure P2S VPN Gateway (SKU BASIC - GEN 1).
We are almost done....cause Windows 11 ARM in combination with Parallels / MacOSX does things a bit differently on a networking-level than a regular Windows 11 installation.
Step 3: Test-case and routing tables
The test-case (ADLS GEN 2 /w private endpoints):
I have an azure storage account (ADLS gen2) deployed in the SPOKE-VNET with public_access_enabled = false
and attached a private endpoint that are registered in the respective private DNS zone privatelink.dfs.core.windows.net
in the HUB-vnet with following dns-entry:
Happy flow:
When the VPN-connection is connected and FQDN ffatitanrbkdev.dfs.core.windows.net
should resolve to IP-address 10.1.2.4
and network-traffic is flowing through the private endpoint attached to ADLS GEN 2 storage-account ffatitanrbkdev
in the SPOKE-VNET.
Sad flow:
The VPN-connection is connected and FQDN ffatitanrbkdev.dfs.core.windows.net
resolves to an Azure Public IP address and therefore we can not access the storage account as we've disabled public access on it by setting public_access_enabled = false
.
Inspecting the routing tables
For the VPN-client-network-interface (when connected) I expect the following values as I run a HUB-SPOKE-network-setup:
- HUB-VNET (Which has the Azure P2S VPN Gateway deployed)
- DNS-server = 10.0.6.4
- Network-route 1 = 192.168.76.10 (VPN-client IP) to 10.0.0.0/16 (VNET HUB CIDR)
- Network-route 2 = 192.168.76.10 (VPN-client IP) to 10.0.255.255/32 (automatically added)
- SPOKE-VNET (Which has my azure resource such as Azure Data Lake Gent with private endpoints)
- DNS-server = 10.1.6.4
- Network-route 1 = 192.168.76.10 (VPN-client IP) to 10.1.0.0/16 (VNET HUB CIDR)
- Network-route 2 = 192.168.76.10 (VPN-client IP) to 10.1.255.255/32 (automatically added)
the other network routes that are added for interface 192.168.76.10 (VPN-client IP) are not that relevant at this moment in time. hence, we will not elaborate on these.
VPN-client = disconnected
When the VPN-client is not connected yet. Lets inspect our routing table by executing the following command: route print
this results in the following routing table:
We only have the routing table for the following default Parallels VM Windows 11 ARM network-interfaces:
- Parallels VirtIO Ethernet Adapter (ip 192.168.2.80)
- Software Loopback Interface 1 (ip 127.0.0.1)
This is expected as the VPN-client is not connected and therefore not active.
VPN-client = connected
When the VPN-client is connected yet. Lets inspect our routing table by executing the following command: route print
this results in the following routing table:
As expected, the top 4 network-routes for interface interface 192.168.76.10 (VPN-client IP / ffatitanrbkntw-vnet)
NSLOOKUP - validating if the FQDN is resolved correctly:
Remember, we want the FQDN ffatitanrbkdev.dfs.core.windows.net
to resolve to the private ip-address 10.1.2.4
. Which is the IP-address attached to the virtual NIC which is attached to the private-endpoint that is in turn attached to our storage account ffatitanrbkdev
.
So we have the VPN-Client is connected to the Azure P2S VPN Gateway through SSTP and certificate-authorization and the network-routes are properly being created. lets perform nslookup with following command:
nslookup ffatitanrbkdev.dfs.core.windows.net
this results in the following output:
On a regular Windows 11 ARM or AMD64 installation we would already be done by now and the NSLOOKUP-command would have returned IP-address 10.1.2.4
by using the DNS-server 10.0.6.4
of the HUB-VNET.
Unfortunately we ended up in the sad flow (see section 'the test-case'). the VM did not use the HUB-VNET-DNS-server but the regular DNS-server provided by my Gateway at home. therefore, the FQDN got resolved to its public IP-address 20.209.230.44
.
This means that we cannot access our azure storage account via the DFS private dns zone and subsequently registered private endpoint.
In the next section i will elaborate on why this keeps happening and how you can resolve this.
Step 4: The order of FQDN-resolving with DNS-servers in Windows 11 ARM on Parallels / OSX
As mentioned, we ended up in the sad flow. But we will not be sad for long. we can actually solve this. I will try to explain the WHY, HOW and WHAT.
WHY am I getting the Azure Public IP and not Azure Private IP?
This requires a bit of understanding on how the order of DNS-server usage / DNS-resolving happens in Windows. Windows uses the first DNS records associated with the highest ranking network interface called InterfaceMetric
. Where a lower value means higher ranking of the network interface.
Lets get the ranking of our network-intfaces by the following command:
Get-NetIPInterface | Select-Object -Property InterfaceAlias, InterfaceMetric | Sort-Object -Property InterfaceMetric
This results in the following output on my Windows 11 ARM (VM on Parallels on MacOS):
If we take a closer look we see that
- network-interface 'Ethernet' (which corresponds to network-interface 'Parallels VirtIO Ethernet Adapter') has an InterfaceMetric-value of
15
- network-interface 'ffatitanrbkntw-vnet' (which corresponds to network-interface '192.168.176.10' - VPN Client) has an InterfaceMetric-value of
25
This implies that NSLOOKUP shows expected behavior as it would first look at the DNS-records abailable for FQDN ffatitanrbkdev.dfs.core.windows.net
and returns the Azure Public IP-address as this is the interface that is is connected to my ISP-gateway at home.
On a regular Windows 11 ARM or Windows 11 AMD64 installation the VPN-client would by default get a lower value for property InterFaceMetric than the regular network-interfaces (Ethernet or Wifi-network-interfaces)..
HOW can we solve the FQDN resolving issue?
Well the answer is quite simple. We can set the InterfaceMetric with a single powershell-command that we need to run with elevated permissions (run as administrator):
Set-NetIPInterface -InterfaceAlias "<your-vpn-connection-name>" -InterfaceMetric 10
This results in the following output on my Windows 11 ARM (VM on Parallels on MacOS):
If we take a closer look we see that
- network-interface 'ffatitanrbkntw-vnet' (which corresponds to network-interface '192.168.176.10' - VPN Client) has an InterfaceMetric-value of
10
- network-interface 'Ethernet' (which corresponds to network-interface 'Parallels VirtIO Ethernet Adapter') has an InterfaceMetric-value of
15
This implies that NSLOOKUP will show the following behavior: It would first look at the DNS-records available for FQDN ffatitanrbkdev.dfs.core.windows.net
in network-interface 'ffatitanrbkntw-vnet' and returns the Azure Private IP-address as this is the interface that is is connected to the Azure VPN P2S Gatweway in my HUB-VNET and uses DNS-server 10.0.6.4.
important: in order to set the value of the InterfaceMetric via powershell the VPN-client needs to be connected and it is only of a temporary nature. once you disconnect the VPN-client the InterfaceMetric is restored to its original higher value.
please note: You also have the option to set the InterfaceMetric through the GUI by following this documentation: https://www.geeksforgeeks.org/how-to-set-internet-priority-on-windows/
I still need to find a way to permanently set the InterfaceMetric value for the VPN-client and persist it in the VM.
WHAT is the result?
The result of setting the InterfaceMetric as described in the previous subsection is that we end up in our happy flow. If we now run the NSLOOKUP-command:
nslookup ffatitanrbkdev.dfs.core.windows.net
This results in the following output:
This means we can now safely access our azure storage account (ADLS Gen 2 service) through the private-endpoint and interact with it.
This also applies to any other resource that is using private endpoints in the SPOKE-VNET.
To Conclude
I hope this workaround helps people with a Macbook with Apple silicon that are running a Windows 11 ARM VM on Parallels and need to connect via SSTP to the Azure P2S VPN Gateway.
If I can find the time I will create a powershell-script that sets the InterfaceMetric for the VPN-client automatically and gets triggered when the VPN-client connects to the VPN-gateway.
Regards,
Sjors Otten