Deployment guide

OpenVPN

Videos

Overview: OpenVPN Community on Ubuntu 24.04 LTS on Azure: overview (watch on YouTube)

Launch and log in: OpenVPN Community on Ubuntu 24.04 LTS on Azure: launch and log in (watch on YouTube)

Deep Dive: OpenVPN Community on Ubuntu 24.04 LTS on Azure: deep dive (watch on YouTube)

Overview

OpenVPN Community Edition is the classic open source SSL/TLS VPN daemon. It runs a routed, certificate-authenticated VPN server that gives remote clients a secure, encrypted tunnel into your Azure network and out to the internet.

This SecureAnyCloud image ships only the GPL 2.0 Community Edition, never the proprietary Access Server. It installs the current stable OpenVPN 2.6 line from the Ubuntu 24.04 archive. Ubuntu actively patches this package, and unattended security upgrades keep the daemon patched on your running VM.

What you get:

  • OpenVPN 2.6 (current stable line) from the Ubuntu 24.04 archive, run under systemd as the packaged template unit openvpn-server@server, dropping to the unprivileged nobody user after reading its keys
  • A routed UDP VPN on port 1194 with modern crypto: data-ciphers AES-256-GCM, a tls-crypt protected control channel, TLS 1.2 minimum, and an elliptic curve (secp384r1) PKI
  • A per-instance PKI generated on first boot (elliptic curve CA, server and client certificates, tls-crypt key), with no shared or shipped keys, stored on a dedicated 20 GiB Azure data disk at /var/lib/openvpn-pki
  • A ready-to-use inline client config at /root/client1.ovpn (check its remote line in Step 6)
  • Internet egress for VPN clients built in as static configuration: IP forwarding plus an nftables masquerade rule, both persisted across reboot
  • A small nginx on port :80 that serves only an unauthenticated /healthz endpoint (HTTP 200) for Azure Load Balancer health probes. It is not a VPN management UI
  • An Ubuntu 24.04 LTS base with the latest security patches applied at build time and unattended security upgrades kept enabled
  • 24/7 SecureAnyCloud support with a guaranteed 24 hour response SLA

No shipped keys. A VPN image with baked-in private keys would let anyone who has the image impersonate the server or decrypt traffic, so this image never ships any key material. On the first boot of every VM, a fresh per-instance easy-rsa PKI is generated: an elliptic curve CA, a server certificate and key, a tls-crypt control channel key, and an initial client certificate. No two VMs share key material, and nothing private exists in the captured image. The certificate authority, the server and client certificates, and the tls-crypt key live on a dedicated data disk at /var/lib/openvpn-pki.

Ready to connect. First boot also assembles a complete, inline client configuration at /root/client1.ovpn (mode 0600, root only). Its remote line points at the address Azure reports at first boot. With a Standard SKU public IP, that is the private address and needs a one-line change (see Step 6). Copy the file to a client machine and connect immediately, then add your own per-client certificates for real users.

Prerequisites

You need an active Azure subscription, an SSH public key, a VNet with a subnet in the target region, and a subscription to the OpenVPN Community listing on Azure Marketplace.

Configure the network security group (NSG) to allow TCP 22 (admin) and UDP 1194 (the VPN) from the clients that will connect.

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) for a small remote access VPN. For higher throughput or many concurrent clients, use Standard_D2s_v5 or larger.

1

Deploy from the Azure Portal

In the Azure Portal, search Marketplace for OpenVPN Community, select the SecureAnyCloud publisher and click Create.

Configure the network security group to allow UDP 1194 (the VPN) from your client networks and TCP 22 for administration.

2

Deploy from the Azure CLI

RG="vpn-prod"; LOCATION="eastus"; VM_NAME="vpn1" IMAGE_URN="sac1647283583153:openvpn-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION"
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:openvpn-ubuntu-24-04:default:latest
az vm create \
  --resource-group "$RG" --name "$VM_NAME" \
  --image "$IMAGE_URN" \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values "$SSH_KEY" \
  --public-ip-sku Standard
# Open the VPN and admin ports on the VM NSG (OpenVPN uses UDP 1194 by default):
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 1194 --priority 1001
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 22 --priority 1002

OpenVPN uses UDP 1194 by default. Make sure the NSG rule uses the UDP protocol, not TCP.

3

Review the first boot

On first boot, the image does the following:

  • Looks up the VM's address. On a Standard SKU public IP, Azure reports the private address (see Step 6).
  • Generates a fresh per-instance easy-rsa PKI: the CA, the server and client certificates, and the tls-crypt key.
  • Brings up the openvpn-server@server service.
  • Proves that the tunnel works end to end.
  • Assembles the ready-to-use /root/client1.ovpn.
  • Writes /root/openvpn-credentials.txt.

First boot completes within a minute. SSH in as azureuser and read the summary:

sudo cat /root/openvpn-credentials.txt

The summary records that address, the VPN subnet (10.8.0.0/24), the CA fingerprint, and the location of the client config and PKI.

4

Confirm the VPN server is running

Verify three things: the openvpn-server@server service and the nginx health endpoint are both active, the server holds the VPN endpoint address 10.8.0.1 on tun0, and the daemon is listening on UDP 1194:

systemctl is-active openvpn-server@server nginx.service ip -4 addr show tun0 ss -lunp | grep ':1194 '

The unauthenticated health endpoint that Azure Load Balancer probes returns HTTP 200:

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/healthz
5

Inspect the per-instance PKI

The certificate authority, the server and client certificates, and the tls-crypt key were generated on this VM at first boot. They live on the dedicated data disk at /var/lib/openvpn-pki. Private keys are root-only (mode 0600). Nothing here was shipped in the image:

sudo findmnt -no SOURCE,TARGET,FSTYPE,SIZE /var/lib/openvpn-pki
sudo openssl x509 -in /var/lib/openvpn-pki/pki/ca.crt -noout -subject -dates -fingerprint -sha256 sudo stat -c '%n %a %U:%G' /var/lib/openvpn-pki/pki/private/server.key /var/lib/openvpn-pki/tls-crypt.key
6

Get the ready-to-use client config

A complete inline client configuration for this VM is waiting at /root/client1.ovpn. The CA, client certificate, client key and tls-crypt key are all embedded in the file.

First boot points its remote line at the address Azure reports. With a Standard SKU public IP, that is the VM's private address, for example remote 10.0.0.4 1194. Standard SKU is the Azure default, and it is the --public-ip-sku Standard setting in Step 2. A client outside the virtual network cannot reach a private address.

Check the line, and if it shows a private address, point it at your VM's public IP (or DNS name):

sudo grep '^remote ' /root/client1.ovpn
sudo sed -i 's/^remote .*/remote <vm-public-ip> 1194/' /root/client1.ovpn

Then display the file, copy it to a client machine and connect:

sudo cat /root/client1.ovpn

The <key> and <tls-crypt> blocks in the screenshot above are redacted for display only. The real file contains live per-instance private material, which is why it is stored root-only.

7

Prove the tunnel works

On any client that has client1.ovpn, connect with sudo openvpn --config client1.ovpn. In a second terminal, run ping 10.8.0.1 to reach the server across the tunnel. The client completes the TLS handshake, receives a 10.8.0.x address on its tun interface, and reaches the server VPN address 10.8.0.1 inside the encrypted tunnel.

To run the whole exchange on the VM itself, use the self-contained self test below. It connects a client to the server over loopback on a separate tun9 interface, confirms that the client received a VPN address, pings the server across the tunnel, and then disconnects:

sudo bash -c ' CONF=$(mktemp /tmp/selftest.XXXX.ovpn) sed -e "s/^remote .*/remote 127.0.0.1 1194/" /root/client1.ovpn > "$CONF"; echo route-nopull >> "$CONF" LOG=$(mktemp /tmp/selftest.XXXX.log) openvpn --config "$CONF" --dev tun9 --daemon --log "$LOG" --writepid /tmp/selftest.pid for i in $(seq 1 30); do ip -4 addr show tun9 2>/dev/null | grep -q 10.8.0. && break; sleep 1; done CIP=$(ip -4 addr show tun9 2>/dev/null | grep -oE "10\.8\.0\.[0-9]+" | head -1) echo "client connected: tun9 = $CIP" ping -c 3 10.8.0.1 kill "$(cat /tmp/selftest.pid 2>/dev/null)" 2>/dev/null; rm -f "$CONF" "$LOG" /tmp/selftest.pid '

The server pushes redirect-gateway, so a normally connected client also sends its internet traffic through the VPN and out via this VM. The image configures IP forwarding and an nftables masquerade for this.

8

Add and revoke clients

Sharing client1 is fine for a quick test, but give each real user their own certificate. Build a new client certificate with easy-rsa against the per-instance PKI, then assemble an inline .ovpn file for it:

export EASYRSA_PKI=/var/lib/openvpn-pki/pki
sudo -E /usr/share/easy-rsa/easyrsa --batch build-client-full alice nopass

The --batch flag answers easy-rsa's confirmation prompt. Without it, the command waits for you to type yes.

To turn the new certificate into a ready-to-use .ovpn file, copy client1.ovpn, which already carries the right remote line, CA and tls-crypt key. Then swap in alice's certificate and key:

sudo python3 - <<'EOF' import re pki = "/var/lib/openvpn-pki/pki" conf = open("/root/client1.ovpn").read() crt = open(f"{pki}/issued/alice.crt").read() crt = crt[crt.index("-----BEGIN CERTIFICATE-----"):].strip() key = open(f"{pki}/private/alice.key").read().strip() conf = re.sub(r"<cert>.*?</cert>", lambda m: f"<cert>\n{crt}\n</cert>", conf, flags=re.S) conf = re.sub(r"<key>.*?</key>", lambda m: f"<key>\n{key}\n</key>", conf, flags=re.S) open("/root/alice.ovpn", "w").write(conf) EOF
sudo chmod 600 /root/alice.ovpn

To revoke a client and stop it from connecting, revoke the certificate, regenerate the CRL, enable crl-verify in the server config (once), and restart the service:

export EASYRSA_PKI=/var/lib/openvpn-pki/pki
sudo -E /usr/share/easy-rsa/easyrsa --batch revoke alice
sudo -E /usr/share/easy-rsa/easyrsa gen-crl grep -q '^crl-verify ' /etc/openvpn/server/server.conf || echo "crl-verify /var/lib/openvpn-pki/pki/crl.pem" | sudo tee -a /etc/openvpn/server/server.conf
sudo systemctl restart openvpn-server@server

The server reads the CRL when it starts and then runs as nobody, which cannot read the root-only PKI directory. It logs Failed to stat CRL file, not reloading CRL and keeps the list it loaded. Restart the service after every later revoke and gen-crl, as above, for the revocation to take effect.

9

Harden for production

Give every user their own certificate (Step 8), and never share client1 beyond an initial test.

Scope what is routed. By default the server pushes redirect-gateway (full tunnel). For a split tunnel, remove that push and push only the routes your clients need in /etc/openvpn/server/server.conf.

Set your own DNS. The image pushes public resolvers. Push your own DNS server with push "dhcp-option DNS <ip>" for split-horizon name resolution.

Restrict the NSG. Allow UDP 1194 only from the client networks that need it, and limit TCP 22 to your admin range.

Keep the PKI safe. It lives on the data disk at /var/lib/openvpn-pki. Back up the CA and snapshot the disk as part of your disaster recovery plan.

Support

SecureAnyCloud provides 24/7 support with a guaranteed 24 hour response SLA. Contact sales@secureanycloud.com.

OpenVPN is a registered trademark of OpenVPN Inc. SecureAnyCloud is not affiliated with or endorsed by OpenVPN Inc. This image ships the open source OpenVPN Community Edition (GPL 2.0), not the proprietary Access Server. All product and company names are trademarks or registered trademarks of their respective holders and are used only to identify the software.

Deploy on Azure

Launch OpenVPN Community 2.6 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure