Setting Up an OpenVPN Server with Two-Factor Authentication

An OpenVPN server can provide controlled remote access to office systems, home labs, and private cloud networks without exposing every internal service to the internet. Adding two-factor authentication (2FA) gives the connection an additional identity check: a client certificate proves the device is authorised, while a password and one-time code help prove that the person connecting is legitimate.

For Australian businesses and home administrators, this arrangement is useful when staff work between Sydney, Melbourne, Brisbane, or regional locations. It also suits environments where an NBN connection, a small office firewall, or a cloud instance in an Australian availability zone needs secure access without the cost and complexity of a large remote-access platform.

Plan The Access Model

Start by deciding what the VPN must reach. A full-tunnel configuration sends all client traffic through the server, which can simplify web filtering and logging but increases bandwidth usage. A split tunnel sends only private network traffic through OpenVPN, preserving local internet access and reducing load on an office connection.

A small business might use a cloud server in Sydney or Melbourne to reach internal administration systems, while a home lab could terminate the VPN on a Linux host behind a Telstra, Optus, or Aussie Broadband connection. Check whether the internet service has a public IPv4 address. Carrier-grade NAT can prevent inbound connections, so a static business service, port forwarding through a reachable router, or a hosted server may be required.

Treat certificates as the first factor and the one-time password as the second. Create a separate certificate for every laptop, phone, or tablet instead of sharing one client profile. When a device is lost, revoke its certificate without disrupting everyone else. A short access register should record the user, device, certificate expiry, and reason for access.

The choice between a hosted endpoint and an in-house appliance should include support effort, resilience, and data location. A practical comparison of those trade-offs appears in this cloud and in-house guide, which is useful when deciding whether the VPN belongs in a Sydney cloud region or on a local server.

Build The OpenVPN Server

The following example assumes Ubuntu Server 22.04 or 24.04. Update the system, install OpenVPN, Easy-RSA, and the PAM module used for time-based codes:

sudo apt update
sudo apt install openvpn easy-rsa libpam-google-authenticator

Create a certificate authority with Easy-RSA in a protected administrative directory. Generate the server certificate, a certificate for each client, Diffie-Hellman parameters if required by the selected OpenVPN version, and a tls-crypt key. Keep the certificate authority’s private key offline; it should not remain on the VPN host after certificates have been issued.

The server configuration normally lives at /etc/openvpn/server/server.conf. A modern baseline includes dev tun, a private VPN subnet such as 10.8.0.0/24, proto udp, port 1194, topology subnet, server 10.8.0.0 255.255.255.0, and tls-crypt ta.key. Include user nobody, group nogroup, persist-key, persist-tun, and a suitable keepalive value.

Enable certificate verification and password authentication with directives such as verify-client-cert require, username-as-common-name, and auth-user-pass-verify through the OpenVPN PAM plugin. On Ubuntu, the plugin path is commonly /usr/lib/x86_64-linux-gnu/openvpn/plugins/openvpn-plugin-auth-pam.so, although it should be checked on the installed system rather than copied blindly from another host.

Enable IP forwarding with net.ipv4.ip_forward=1, then add a firewall rule that masquerades VPN traffic as it leaves the server’s internet interface. UFW, nftables, or iptables can perform this task. Restrict inbound access to UDP 1194, SSH from a management address or bastion, and any monitoring endpoints that are genuinely needed.

Add Time-Based Two-Factor Authentication

Google Authenticator’s PAM module provides time-based one-time passwords (TOTP), which work with common applications such as Aegis, Microsoft Authenticator, and Google Authenticator. Create a dedicated Unix account for each VPN user, then run google-authenticator as that account. Store the generated secret securely and scan the QR code with the authenticator app.

The PAM policy should require the one-time code rather than allow users without a token. A basic /etc/pam.d/openvpn file may contain:

auth required pam_google_authenticator.so
account required pam_permit.so

Use the OpenVPN PAM plugin with the service name openvpn, and configure the client to send a username and password. The password field can contain the account password followed by the current six-digit token if the PAM setup expects a combined value; verify the exact behaviour for the distribution and module version in use. Some deployments use a separate password prompt, so test this with a non-privileged account before rolling it out.

Time synchronisation is essential. TOTP codes are short-lived, and a clock that drifts by more than a small amount can cause valid logins to fail. Run systemd-timesyncd or chrony, confirm the server uses UTC, and document that users should set their phones to automatic network time. This is especially important when staff move between AEST and AEDT during daylight-saving changes in states such as New South Wales and Victoria.

Protect recovery codes as carefully as passwords. Keep them in an approved password manager, not in a shared Teams message or an unencrypted spreadsheet. A security-focused operational perspective, such as this security resource, can help reinforce sensible practices around secrets, account recovery, and access review.

Configure Clients And Routing

Each client needs an .ovpn profile containing the server address, CA certificate, client certificate, private key, and tls-crypt key. A minimal profile references client, dev tun, proto udp, the public hostname, remote 1194, nobind, persist-key, persist-tun, remote-cert-tls server, and auth-user-pass.

Use a DNS name rather than embedding a changing residential IP address in every profile. Dynamic DNS can help with some NBN services, but first confirm that the router receives a publicly reachable address. If the endpoint is hosted in AWS Sydney, Azure Australia East, or another provider, apply security-group rules as well as the operating system firewall.

For split tunnelling, push only the required routes, such as push "route 10.20.0.0 255.255.0.0". For full tunnelling, push a default gateway and appropriate DNS settings, then confirm that internal name resolution works. Avoid pushing a public DNS resolver that conflicts with corporate policy or exposes private lookup names.

Test from a genuinely external network, such as a mobile hotspot, rather than from the same office Wi-Fi. Confirm that the client receives a VPN address, can reach approved hosts, resolves internal names, and cannot reach systems outside its role. Check latency from different locations; a user in Perth connecting to a server in Sydney will usually experience a different round-trip time from someone in Brisbane.

Operate And Monitor The Service

Start and enable the service with systemctl enable --now openvpn-server@server. Review logs with journalctl -u openvpn-server@server, and configure log rotation so repeated authentication failures cannot fill the disk. Record successful logins, failed MFA attempts, certificate subjects, assigned VPN addresses, and connection times while respecting Australian privacy obligations and the organisation’s retention policy.

Certificates should have defined lifetimes, and the certificate authority should be backed up offline and tested through a restoration exercise. Revoke devices when staff leave, laptops are replaced, or a private key may have been copied. Keep OpenVPN, the operating system, PAM modules, and authenticator applications patched.

Treat the VPN as production infrastructure rather than a one-off configuration. Infrastructure changes should be reviewed and tested before deployment, just as application changes are. Teams maintaining PHP tooling may find a PHP Jenkins workflow useful as a model for repeatable validation, even when the same principles are applied to firewall rules and server configuration.

Run a quarterly access review, test certificate revocation, and confirm that a lost phone can be replaced without bypassing MFA. With documented certificates, reliable time synchronisation, restricted routes, and monitored authentication, OpenVPN becomes a manageable remote-access service rather than an unknown doorway into the network.

Build the server in a disposable test environment first, then issue one certificate and one MFA token for a pilot user. Validate access from an external Australian network, document the recovery process, and move to wider deployment only after logs, firewall rules, backups, and revocation procedures have been checked.

Experience

Information Technology Consulting

Independent Practice

Provides IT consulting services focused on infrastructure planning, cloud migration strategy, and systems architecture. Engagements draw on years of hands-on sysadmin and development experience across Linux, Windows, and hybrid environments.

K9 Search & Rescue Volunteer

Ongoing

Active participant in K9 Search & Rescue operations, combining technical logistics skills with field support for canine search teams.

Karl Katzke's Blog

October 2006 – May 2014

Published a long-running personal technology blog covering cloud vs. in-house infrastructure, F# and Mono on OSX, hardware vendor critiques, RAID card performance analysis, and sysadmin storytelling. Notable posts include "When Sysadmins Ruled the Earth" (May 15, 2014) and "Getting Started with F# and Mono on OSX" (December 22, 2012).

Credentials

A small badge icon with a shield shape in muted blue tones on a light background

Systems Administration

Deep experience with Linux (RHEL, SLES, CentOS), high-availability clusters, and STONITH configurations.

A small badge icon with a gear shape in muted blue tones on a light background

Cloud Infrastructure

Practical knowledge of AWS EC2, reserved instances, and cost analysis for cloud vs. on-premises deployments.

A small badge icon with a code symbol in muted blue tones on a light background

Development

Proficient in F#, PHP (Symfony), and cross-platform tooling including Mono and MonoDevelop on OSX.

Studies

F# & Functional Programming

Self-directed, 2012

Explored strongly typed functional programming with F# on OSX using the Mono runtime. Published a detailed getting-started guide covering toolchain setup and cross-platform game development research.

High-Availability & Cluster Management

Professional Development, 2009

Configured and documented crm_mon email alerting for STONITH events on SLES11-HAE clusters, integrating with Nagios monitoring for production environments.

Hardware & Storage Performance

Ongoing

Conducted hands-on benchmarking of SATA/SAS RAID controllers including HighPoint RocketRaid 2740 and LSI/SuperMicro AOC-USASLP2-H8iR, comparing against software RAID configurations.

Skills

A small icon representing a server with clean geometric lines in slate blue

Linux Administration

RHEL, SLES, CentOS — package management, kernel tuning, HA clustering, and monitoring integration.

A small icon representing a cloud shape with clean geometric lines in slate blue

Cloud Architecture

AWS EC2, reserved-instance planning, cost modeling, and hybrid infrastructure strategy.

A small icon representing code brackets with clean geometric lines in slate blue

F# & .NET/Mono

Functional programming on OSX, MonoDevelop toolchain, and cross-platform game-dev exploration.

A small icon representing a database cylinder with clean geometric lines in slate blue

PHP & Symfony

Web application development with the Symfony framework and the broader PHP ecosystem.

A small icon representing a storage drive with clean geometric lines in slate blue

Storage & RAID

SATA/SAS controller evaluation, md RAID configuration, and performance benchmarking.

A small icon representing a shield with clean geometric lines in slate blue

High Availability

Pacemaker, STONITH, crm_mon alerting, and Nagios integration for production cluster monitoring.