Jump box part 2: Azure VNet connection with a point-to-site VPN Gateway

Azure IoT devices send telemetry to the cloud in a secure and reliable way. Although these devices are designed to be robust and probably are configurable in a zero-touch manner, sometimes it is still needed to log in to these devices using e.g. an RDP or SSH session.

For this, we do not want to create an inbound port in the firewall offering a public inbound session; this makes the devices vulnerable to attacks from the outside world.

This blog is part two of a series about setting up a jump box in Azure to access local devices from the cloud. Part one ended with a private VM in the cloud which was accessible using a secure Azure Bastion connection:

In this blog post, we will discover how to connect local devices (both running Windows or Ubuntu) from the cloud in a secure way, making use of the already existing VNet and the Bastion connection:

We will dive into creating a point-to-site VPN connection, generating certificates to secure the communication, and attaching various VPN clients, including one running on OpenVPN.

Note: We will expand the solution we created already in part one. If you have not created the Bastion solution yet, please first check out what is created in the previous blog post.

Note: although this solution is known to be secure, these series of posts only show the minimal viable solution, likely not to be fit for every occasion. This post must be seen as an introduction to explain the concepts of a VPN connection together with a jump box. At the end of this blog post, we will discuss some considerations. Please check everything with e.g. your security officer, company policies, penetration testers, or your mom 🙂

Why securing the connection to local client devices, the basics

From the Jump box, we want to connect to one or more local client devices.

We do not want to expose our local device to the cloud in a public way. Local devices should not expose inbound ports (like opening an SSH port) because this would be an open invitation to hackers.

Instead, devices should be designed to do outbound calls only (still supporting two-way communication once the communication channel is established).

A possible solution is bringing local devices to the Cloud VNet and its subnet!

Yes, an Azure VNet, subnets included, can be expanded to the edge, the location where our local device lives:

To do this, we need to add an Azure VPN connection to the VNet.

A VPN gateway is a specific type of virtual network gateway that is used to send encrypted traffic between an Azure virtual network and an on-premises location over the public Internet

Note: There are more ways to make a secure connection between on-prem and cloud, e.g. using an ExpressRoute, typically used by larger enterprises.

This VPN connection is set up outbound and is provided right through the firewall.

Azure supports both a site-to-site VPN and a point-to-side VPN. We will use that second solution in this post to keep it ‘small’.

A Point-to-Site (P2S) VPN gateway connection lets you create a secure connection to your virtual network from an individual client computer

Let’s see how a local device can be made part of a VNet subnet in the cloud.

Our goal for today is:

  • Set up a secure VPN Gateway tunnel in the cloud
  • Generate the correct certificates for secure communication per device
  • Provide a secure connection using a Windows client
  • Provide a secure connection using a OpenVPN client

Set up a VPN Gateway

We start with setting up a point-to-site VPN Gateway:

Connect from a computer to an Azure VNet - point-to-site connection diagram.

At this moment, we already have a VNet (called ‘JumpboxVNet’) and we have a VM living in it (only accessible by the secure Azure Bastion connection). Within the VNet, a subnet called ‘FrontEnd’ is created.

If you have not set up this architecture yet, please check out part 1.

Add a Virtual Network Gateway resource to the VNet resource group:

Give it a proper name (I use ‘JumpboxGW’), a simple SKU (I’m not interested in an Availability Zone now, which is also cheaper), and make it part of the ‘JumpboxVNet’.

The Gateway needs its own subnet range. Here, I select a specific private ‘10.1.255.0/27’ range (this range is more than enough for this gateway).

The gateway needs its own public IP address to be reachable over the internet:

We keep the other (default) Public IP address settings; we review it and we create the VNet gateway.

Note: Creating the Gateway will take some time, half an hour is no exception (you can use your time to read about certificates as seen below).

Once the Virtual Network Gateway is created, these resources are added:

  • The gateway named ‘JumpboxGW’
  • The public IP address named ‘JumpboxGWpip’
  • The subnet named ‘GatewaySubnet’

The gateway is added to the list of connected devices in the VNet.

Note: by following this scenario, the Public IP address is not static but dynamic assigned.

Certificates

At this point, we have set up a perfect but useless VPN gateway with a public IP address…

We are not yet able to make a secure connection with any VPN client because we have not yet generated any certificate to secure the connection. Certificates are needed to encrypt the VPN ‘tunnel’, the connection between clients and the gateway.

Certificates work with a Root certificate and one or more Child certificates. Normally, each remote device gets its own child certificate to identify itself. These Child certificates are all derived from the same root certificate. So, typically each client device needs one private Child certificate and (a copy of) the public certificate of the Root to communicate. This way, it can prove the certificate chain is sound.

Note: Below, we are going to create and use a ‘self-signed’ certificate because this is a cheap solution. Also, these certificates are not bound to a specific machine. Therefore, the same certificate chain can be used on multiple clients (which we will see later on). This is OK for testing purposes. In production though, this is hard to manage. More information about creating certificates is documented here.

Creating self-signed Host and Client certificates

Certificates, especially Host certificates must be stored in a secure place. Here, certificates are generated on my laptop. In production, reserve specific ‘air-gapped’ resources to keep your certificate generation sound and secure, especially the private host certificate.

I use PowerShell for generating both a Root certificate and a Child certificate.

Here, I create a Root certificate:

$cert = New-SelfSignedCertificate -Type Custom -KeySpec Signature `
-Subject "CN=GatewayRootCert" -KeyExportPolicy Exportable `
-HashAlgorithm sha256 -KeyLength 2048 `
-CertStoreLocation "Cert:\CurrentUser\My" -KeyUsageProperty Sign -KeyUsage CertSign

Then, I create this Child certificate, the signer is the Root certificate (using that $cert variable):

New-SelfSignedCertificate -Type Custom -DnsName GatewayChildCert -KeySpec Signature `
-Subject "CN=GatewayChildCert" -KeyExportPolicy Exportable `
-HashAlgorithm sha256 -KeyLength 2048 `
-CertStoreLocation "Cert:\CurrentUser\My" `
-Signer $cert -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.2")

I get this thumbprint as the output of the second script, which tells me creation was successful:

In Windows, I can see both certificates in the Certification Manager:

Note: With ‘Get-ChildItem -Path “Cert:\CurrentUser\My’ you can verify the thumbprints of both Root and Child certificates.

That was not that hard. We have these certificates put in the personal store. Now, we need to access the actual certificates and export certain information from them before we can make that VPN connection.

Exporting the public Root certificate

Al least, we need the root certificate to be attached to the gateway, so we export the public Root certificate:

We only need the public key (yes, we keep the private key secure in some private location, it’s actually only needed when generating new client certificates).

So, do not export the private key of the root certificate:

The public Root certificate must be exported in Base64 format:

Now, give the file containing the public certificate a proper name:

Note: the ‘.cer’ extension is typically for files holding public keys.

Open the new file created in notepad or any other text editor and check the content (do not change it):

In Notepad, you can clearly see the Base64 text of the public certificate:

Now comes the tricky part, copy the base64 text, leave out those two ‘guardian’ lines (those lines with BEGIN CERTIFICATE and END CERTIFICATE ).

Soo, the string to copy should start with ‘MI’ and end with ‘Q=’!

We need this string representing the public root certificate in the next part because it’s time to add this certificate to the gateway.

Add a point-to-site configuration

As said, the Gateway is not active yet, we need to configure the tunnel.

We need to add a point-to-site configuration:

First, we fill in a (public) address pool for addresses which clients can use to connect e.g. ‘82.168.54.0/28’:

Later on, we will use OpenVPN but it’s easy to select multiple types of tunnels so I also added this IKEv2.

Regarding the authentication type, we go for Azure certificates. Thus, we have to upload the root certificate (notice how this page changes depending on the drop-down list selections).

Regarding the rood certificate, paste the certificate text (as seen in the previous paragraph) in the ‘Root certificates’ dialog and give it a random but proper name (you are free to choose a name you like):

Note: Ignore the ‘Revoke certificates’ dialog.

Note: You can add up to twenty root certificates and later on revoke them if needed.

Save the configuration.

Once saved, the button ‘Download VPN client’ is made available.

Download it.

A zip file named ‘JumboxGW.zip’ is downloaded:

This file contains several helpful files for multiple types of VPN clients to connect to the gateway.

Note, VPN clients are supposed to have a private client certificate and the public root certificate too to prove who they are. This zip file does not contain these certificates. We will look at how to fix that later on.

First, let’s connect a VPN client the easy way.

Connecting a Windows client, the easy way

We start with a simple and easy VPN connection; we connect our development laptop to the gateway.

Why is this easy?

On this particular laptop, use to generate certificates, the private client certificate and related public root certificate are already available in the store. So, we only have to concentrate at the VPN connection configuration.

Just unzip the JumpboxGW.zip file:

In there, find and start the 64 bits client app:

For some reason, Microsoft Windows (11) wants to protect me but I trust this application:

That’s because I know the source. So, I run it anyway.

I then get this question to install the connection configuration. I accept the VPN client:

Once accepted, navigate to the ‘Network & internet’ page in Windows Settings. There, go to the VPN page.

Here, a VPN connection is available in the list of all configured VPN connections (this is the Windows 11 dialog):

Connect that one connection.

This connection start this dialog:

Again, select Connect.

You get the message your routing table is updated.

Select Continue (with elevated rights).

At last, the VPN connection is now in a ‘Connected’ state:

Finally, you can see that your laptop now has an extra network connection if you run the ‘ipconfig’ command on the Dos prompt:

The point-to-site configuration screen In the Azure portal also shows the allocation of that same IP address:

Accessing the cloud VM from the local client

Because my laptop is not exposing SSH so I cannot set up a cloud-to-edge connection, at least I can try to prove my laptop and the jump box VM are part of the same network by making a call to the private cloud network.

Remember, that cloud VM was only accessible using a private IP address, thus using Bastion:

The VM is already running in the cloud, let’s try to access it:

ssh adminvm@10.1.0.4

Yes, access to the (private) VM is provided:

Again, this session can be monitored or even disconnected by checking the point-to-site connections in the Virtual Network Gateway pane:

At this moment, we are able to connect my laptop to the cloud because the (client and root) certificates were already installed on the client. How about other local devices?

Extracting the private client certificate for another edge device

I arranged a second Windows device. It will act as a second edge device, so I need a client certificate derived from the same root certificate.

Actually, I am just going to reuse the same client certificate! This is because I can demonstrate how to work with certificates put in the certificate store.

Regarding the private ‘GatewayChildCert’, seen in same the store as the root certificate, again I start the export wizard. This time, I export the private key, from this child certificate:

Because this is a self-signed certificate, I include the full chain (here, I include the Public Root certificate):

And I am asked to provide a password to secure the file:

I also have to provide a filename for this private certificate:

Note: The ‘.pfx’ extension is used for private certificates.

I copy that VPN client executable to the other laptop and I also import the private certificate in the certificate store of the other laptop.

Just right-click on the .pfx file:

The certificate extension is recognized so installation is easy.

We select the right store, it’s the ‘Current User’ store:

Provide the password to unlock the file:

Accept the right store choice:

After a security warning concerning adding new certificates (which I accepted), the private certificate is finally added:

Note: This private certificate is valid for 1 year.

Next to the private client key, the public root key is also added:

This second key was already part of the key chain inside the private key so that was easy too.

Now, we can start setting up the same VPN connection. Again, that application executable is run so the connection is added to the list of VPN connections (this is the Windows 10 dialog):

Again, Connect.

Now, two separate devices both make their own connection, as seen in the list of point-to-site connections:

Be warned, the two clients can see each other too! These are both part of the same virtual network.

Because the client certificate was not bound to a single machine, multiple clients can use it. That’s ok for now (I hope you like this simple demonstration) but this is not manageable in the real world!

Adding an OpenVPN client using Ubuntu

Ok, using Windows makes it very easy with all this nice visual tooling. We just connected two devices.

How do we roll out a VPN connection on a Linux command line, like Ubuntu 20.04 LTS?

For that, I found out this OpenVPN was a reliable solution.

I already installed Ubuntu 20.04LTS on an Industrial PC from Weidmueller, connected to the regular internet behind a normal firewall:

2665820000 UV20-B-IPC-3031.01 | Weidmüller Product Catalogue

First, login into Ubuntu running on the edge device, and install the much-needed OpenVPN tooling from the command line with ‘apt-get’:

sudo apt-get install openvpn
sudo apt-get -y install network-manager-openvpn
sudo service network-manager restart

Then, because we enabled OpenVPN on the point-to-site configuration and we downloaded that configuration zip file, we have this OpenVPN configuration file already:

So, we have this configuration, we can FTP that file to our Ubuntu environment.

But wait, how about the certificates?

How do tell Ubuntu about the private and public keys? Where do we store them on disk or in some kind of store?

Well, we do not…

OpenVPN provides a little help with that because the OpenVPN configuration can take care of the certificates.

We need to adjust that configuration file.

If we look into that configuration file we got from Azure (do not change it yet!) we see some placeholders for certificates:

All we have to do is copy some text representation of the certificate and key in there and then, the connection can be established by OpenVPN.

How do we get those text representations?

For that, I use this OpenSSL command on the Windows Subsystem on my PC against the private key I already exported:

openssl pkcs12 -in "gateway-child-certificate.pfx" -nodes -out "profileinfo.txt"

This provides me this ‘profileinfo.txt’ right after I enter the password of the private certificate.

When I open this text file, I find the text representative of the private certificate and key (more precisely, it contains both the private key and the thumbprint for the CA, and the Client certificate):

Now, I have to copy the right certificate and key in the right placeholder.

Notice this time I have to include the ‘guardian’ lines:

Copy these two values:

  1. Replace ‘$CLIENTCERTIFICATE’ with the full “—–BEGIN CERTIFICATE—– …… —–END CERTIFICATE—–” for the text file which is annotated with “subject=CN = GatewayChildCert”
  2. Replace ‘$PRIVATEKEY’ with the full “—–BEGIN PRIVATE KEY—– …… —–END PRIVATE KEY—–” seen at the beginning of the text file

Save the OpenVPN file.

Move this updated OpenVPN file over to the Ubuntu client (I rename the file to make it more explicit):

Now, on the Ubuntu device, start the VPN client:

sudo openvpn --config gateway.client.vpnconfig.ovpn&

Notice, there is a & at the end of the line. This is not a mistake!

By adding the ‘&’ sign, the OpenVPN connection is set up as a background process, thus keeping the current window open for other command-line activities. (you can play with this by starting a second prompt and seeing how the OpenVPN call blocks the page).

If I run this command using the ‘&’, a process ID is returned:

Note: this is common behavior to check the existence of the process using its ID or actually killing the process again using its ID when needed.

Now, run ‘ifconfig’ to check all networks running on the Ubuntu device:

We see a ‘tunnel’ is set up, nice.

Notice, we are provided an IP address in the range of the VPN gateway subnet.

Again, in the Azure portal, this IP address will pop up as an active connection.

From the edge, I can ping the private VM in the cloud.

Using Bastion, from the jump box, I can ping also my local Ubuntu client:

My edge device is running an SSH server, normally not available outside my local edge network.

Again, using that Bastion connection, I can start an SSH session from the jump box to my Ubuntu client:

Success!

Note: this is an SSH connection over the private tunnel, secured by the certificates. My SSH connection is not publicly available for the outside world!

We now have brought two Windows devices and one Ubuntu device to the same virtual network as the VM is living in.

Note: the tunnel will not survive a reboot on the local Weidmueller IPC. If you want to support a full-time tunnel on the client, you need to make the OpenVPN command part of the crontab. The tunnel must also be robust so it tries to reconnect if something fails…

Reflection

Keep in mind there are a few points of attention here:

  • We are using a self-signed certificate chain.
  • Client certificates are not bound to a single machine. We are able to reusable them for multiple clients.
  • Communication within the subnet works two-way: the jump box can see all clients in the network, each client can see the jump box and all other clients. So, at least a strong and unique password for both the jump box and each client is needed. Note: adding a Network Security Group could help. Because each device connects to the gateway while communication to another device, a NSG could filter traffic and therefor connections.
  • We use a VPN client application for each separate client device. Adding a network router supporting the VPN connection makes setting up a VPN connection easier and more robust.
  • There is no DHCP setup. We need to know the IP addresses of all actors by heart.

Again, this is a demonstration regarding what technology to use and which steps to perform. Use this guide only as a first stepping stone towards a production-ready solution.

Conclusion

Together with the previous blog, we are now able to provide a secure and measurable connection from the cloud for each local device able to set up the VPN connection, just like the Weidmueller IPC and the Windows PCs in this post:

This extra communication service does not come for free, there are 24/7 Azure resources running and IP ranges reserved. Please check out the billing page for more details.

On MS Learn, there is a nice module with more background information about VPN connections.

A (fifteen hours of training) learning path named “Configure and manage virtual networks for Azure administrators” is available here.

Een gedachte over “Jump box part 2: Azure VNet connection with a point-to-site VPN Gateway

Reacties zijn gesloten.