OneLinersCommand workbench
Guides
Networking & DNS / Security

Fix OpenVPN Cannot resolve host address, VERIFY ERROR and TLS handshake failures

Deploy or repair an OpenVPN 2.6 gateway with certificate checks intact. Start Cannot resolve host address with the client's pre-tunnel DNS and actual profile; investigate VERIFY ERROR, peer certificate verification failure and TLS handshake failed only at their demonstrated transport or TLS layer.

90 min14 stepsChanges system stateRevision 4
Save or explore
Save to collectionCreate a collection in the sidebar first.
0 of 14 steps completed
Goal

Restore an authenticated OpenVPN connection by separating pre-tunnel endpoint resolution from transport, certificate trust, identity and revocation failures, preserving the existing gateway configuration and proving the repair from the original client context.

Supported environments
  • Ubuntu Server 24.04 LTS
  • OpenVPN 2.6.x
  • Easy-RSA 3.x
Prerequisites
  • Recovery access Keep console access while changing forwarding and firewall rules.
  • Network plan Reserve a non-overlapping VPN subnet, public UDP port, and allowed private routes.
  • Stable endpoint Point a DNS name at the server public address.
  • Current-state backup Export firewall and sysctl configuration before the change.sudo nft list ruleset
Operating boundary

OneLiners never runs these steps or stores secrets. Review placeholders, versions, current state, and change-control requirements before using a command.

Full guide

What you will build

System
  • A certificate-authenticated OpenVPN 2.6 server on a dedicated Ubuntu 24.04 LTS host. The server listens on one public UDP port, assigns addresses from a private tunnel subnet, and rejects peers that do not present a client certificate signed by your certificate authority.
  • A deliberately owned nftables policy that permits management access from a named source network, accepts the OpenVPN listener, forwards authenticated tunnel traffic, and performs IPv4 masquerading for full-tunnel internet access.
  • A repeatable client lifecycle: issue one encrypted private key per person or device, assemble a restricted inline profile, test it from an external network, and revoke it by publishing a new certificate revocation list.
Observable outcome
  • The OpenVPN systemd instance is active, UDP port 1194 or your selected replacement is listening, and the journal contains an Initialization Sequence Completed message without certificate, cipher, route, or permission errors.
  • A test client receives an address from the planned VPN subnet, installs two def1 routes through the tunnel, resolves DNS through the selected resolver, and reaches the server-side tunnel address.
  • After a disposable certificate is revoked and the new CRL is installed, that profile cannot establish a new session while certificates that remain valid continue to connect normally.

Architecture

How the parts fit together

This guide uses routed TUN mode rather than Ethernet bridging. A client first reaches the server's public UDP socket and authenticates the server certificate. The server then validates the client's certificate against the same CA and its current CRL. Only after the TLS control channel succeeds does OpenVPN create encrypted data-channel keys and assign a tunnel address. Linux forwards that authenticated traffic from tun0 to the public interface, while nftables limits the allowed path and rewrites the private source address for internet egress.

Offline-capable certificate authoritySigns server and client identities. Its private key is the trust root and is not copied into OpenVPN profiles or the server configuration directory.
Ubuntu OpenVPN gatewayTerminates the encrypted tunnel, validates certificate purpose and revocation, assigns tunnel addresses, and exposes the tun0 routed interface.
nftables policyProtects the host input path, permits only intended forwarding from the VPN subnet, and applies source NAT for full-tunnel IPv4 egress.
External clientHolds one unique encrypted private key and profile, verifies that it reached the designated server, and installs routes supplied by the server.
  1. The client resolves the stable VPN hostname and sends a tls-crypt-protected OpenVPN control packet to the selected UDP port.
  2. The client verifies that the server certificate has TLS Web Server Authentication usage; the server requires a certificate with TLS Web Client Authentication usage.
  3. OpenVPN negotiates a mutually supported AEAD data cipher and assigns the client an address from the 10.8.0.0/24 example pool.
  4. The server pushes full-tunnel routes and a DNS resolver. Linux forwards accepted packets from tun0 to the public interface.
  5. nftables allows established replies and masquerades the private tunnel source address before the packet leaves the gateway.

Assumptions

  • The target is a dedicated or carefully isolated VPN gateway. Do not apply the example ruleset unchanged to a Docker host, Kubernetes node, hypervisor, router, or machine whose existing firewall is managed by UFW, firewalld, configuration management, or another nftables owner.
  • You have provider console, hypervisor console, or physical access throughout the firewall change. A second SSH session is open from the exact management network that will be placed in {{managementCidr}}.
  • The public interface has stable connectivity and the provider or upstream firewall forwards {{vpnPort}}/UDP to this host. The hostname in {{vpnHost}} already resolves to the public address clients will use.
  • The VPN address pool does not overlap the client's home network, corporate networks, container networks, cloud VPCs, or another VPN. The examples use 10.8.0.0/24 only after that collision check.
  • This procedure provides an IPv4 full tunnel. IPv6 requires an explicit address plan, OpenVPN IPv6 directives, forwarding policy, leak testing, and either routed IPv6 or a deliberate decision to disable it on the client.

Key concepts

Certificate authority (CA)
The trust root that signs every server and client certificate. Possession of the CA private key allows an operator or attacker to mint identities trusted by the VPN, so the key should be passphrase-protected and preferably kept away from the internet-facing gateway.
Control channel
The TLS-protected conversation used for peer authentication, capability negotiation, and creation of short-lived data-channel keys. tls-crypt additionally encrypts and authenticates the outer control packets before the TLS stack processes them.
Data channel
The encrypted path carrying user packets after authentication. OpenVPN 2.6 negotiates a cipher from data-ciphers; the server's first cipher also supported by the client wins.
TUN routed mode
A layer-3 virtual interface that transports IP packets. It avoids stretching Ethernet broadcasts across the internet and makes access policy, routes, and address ownership explicit.
Certificate revocation list (CRL)
A signed list of certificates that the CA no longer trusts. Revocation has no effect until a newly generated CRL is copied to the server path referenced by crl-verify and the running service reads it.
Masquerading
A form of source NAT used when the egress address may change. nftables replaces a packet's 10.8.0.0/24 source with the gateway's public-interface address so internet replies return through the VPN server.
Set guide values0/10 ready

Fill these once. Every matching command and configuration block updates immediately; values stay in this page only.

Security and production boundaries

  • The CA private key is more powerful than the server key. Keep it encrypted and back it up independently; for a production deployment, perform signing on an administrative workstation or offline CA rather than leaving the CA on the public gateway.
  • The example client profile contains its private key. File mode 0600 reduces accidental disclosure but does not protect a copied file, so the Easy-RSA client key should remain passphrase-protected and the profile must never be sent through public chat, tickets, or source control.
  • Replacing the complete nftables ruleset can disconnect administrators or break unrelated forwarding. This guide deliberately limits that action to a dedicated gateway and requires console access, a saved ruleset, syntax validation, and a second SSH session.
  • A full tunnel changes the client's effective network path and DNS. Test for DNS, IPv6, and application leaks from a disposable client before claiming that the VPN supplies a privacy or compliance boundary.
  • Revocation prevents future authentication but does not remotely erase a profile. Treat suspected theft as an incident: revoke, publish the CRL, inspect active sessions and logs, and rotate broader secrets if the CA or tls-crypt key may also be exposed.

Stop before continuing if

  • Stop before changing nftables if you cannot identify the current firewall owner, cannot obtain console access, or cannot explain every existing input and forwarding exception that would be replaced.
  • Stop if {{vpnNetworkCidr}} overlaps any route shown on the server or the external test client. Renumbering after issuing profiles is disruptive, while an overlap can silently route production traffic to the wrong network.
  • Stop if the server certificate and key do not match, if certificate purposes are wrong, or if the server key is readable by untrusted users. Do not weaken verification to make a broken PKI start.
  • Stop after any failed checkpoint. Preserve the journal and current ruleset, restore the previous known-good state, and diagnose the failed layer before proceeding to client distribution.
  • Stop when VERIFY ERROR or peer certificate verification failure has not been tied to a specific certificate, CA, purpose, identity, revocation, or clock check. Do not guess by removing verification directives.
  • Stop when TLS Error: TLS handshake failed is the only retained line. Collect the preceding client and server logs plus packet evidence before changing TLS policy.
01

command

Diagnose Cannot resolve host address before rebuilding the VPN

read-only

If OpenVPN is already installed, start with this read-only diagnosis and the later TLS checks; do not rerun PKI creation, firewall replacement or certificate issuance merely because a connection fails. Cannot resolve host address occurs while obtaining the remote endpoint address, before a successful tunnel can supply its pushed DNS settings.

Why this step matters

The remote endpoint must be located before tunnel establishment, so preserve the failing client's ordinary network and resolver context first.

What to understand

Retain the first RESOLVE error and the remote hostname, port and protocol from the active profile or client application's connection details. Profiles can contain inline keys, certificates, passwords or control-channel secrets; never paste the complete profile into a public issue. Confirm that the GUI selected the intended profile and that managed configuration has not replaced it.

Run the lookup on the affected client network before the tunnel exists. This Guide's client uses udp4, so the initial command requests IPv4 results. Another profile may use a different family or several remote endpoints; test the actual configured family and chosen name rather than assuming the server's own resolver proves client connectivity.

System changes

  • Performs one bounded lookup and reads resolver ownership/settings. It does not start, stop or reconfigure OpenVPN.

Syntax explained

getent ahostsv4
Checks the Linux client's configured name-service path for IPv4 results, matching this Guide's udp4 profile.
timeout 10 and lookup_exit
Bounds the lookup and retains its exit status; timeout status 124 is not an authoritative negative DNS answer.
Command
Fill variables0/1 ready

Values stay on this page and are never sent or saved.

date --iso-8601=seconds; timeout 10 getent ahostsv4 '{{vpnHost}}'; printf 'lookup_exit=%s\n' "$?"; readlink -f /etc/resolv.conf; sed -n -E '/^(nameserver|search|options)[[:space:]]/p' /etc/resolv.conf
Example output / evidence
Illustrative OpenVPN client error:
RESOLVE: Cannot resolve host address: vpn.example.net:1194 (Temporary failure in name resolution)
lookup_exit=2
A DNS failure here is not evidence that the server certificate, password or VPN firewall is wrong.

Checkpoint: Pre-tunnel lookup failure retained

Continue whenThe active profile's endpoint, address family, client network and initial lookup result are recorded without exposing profile secrets.

Stop whenThe test runs only on the VPN server, a different client network or an already established tunnel.

02

decision

Separate endpoint records, resolver selection and startup readiness

read-only

Compare the endpoint's expected DNS record with the client's approved resolver path. If systemd-resolved is the owner, inspect its per-link servers and route domains with resolvectl. For another owner, use that platform's documented diagnostics. Keep an authoritative negative answer, a temporary upstream error and a timeout as separate branches.

Why this step matters

Resolver ownership and startup dependencies determine whether the client can discover its server before any tunnel policy becomes available.

What to understand

A DNS server pushed through the tunnel cannot solve a circular dependency where that same tunnel is required to discover its endpoint. The network owner must provide an approved bootstrap-resolvable name or resolver route. Do not leak internal names to an arbitrary public resolver, add a permanent hosts-file bypass or copy a stale IP from an old log.

If failure occurs only at boot, compare the client's start timestamp with actual link and resolver readiness. network-online.target alone is not a guarantee that the required DNS server answers. Repair the authoritative network manager or documented startup/readiness integration; an arbitrary long sleep can hide a race while still failing during reconnects.

OpenVPN's resolv-retry controls how long name resolution is retried, and this Guide's profile uses infinite. That can keep the client waiting, but it cannot create a missing record, repair blocked DNS or make a private zone reachable. Record retry duration as an operational observation and alert on a connection that never progresses.

System changes

  • Classifies resolver and startup evidence. No DNS, retry, route or service settings are changed automatically.
Example output / evidence
Branch record example:
Before tunnel: the active client resolver cannot reach the private endpoint zone.
After a different VPN connects: the same name resolves.
Conclusion: endpoint discovery depends on a network that is not available for this connection's bootstrap; changing certificates does not repair it.
03

verification

Verify endpoint resolution, then the real OpenVPN handshake

read-only

After the owner corrects the demonstrated record, resolver route or startup problem, repeat the original lookup from the same client context. Use the normal client connection flow and preserve the subsequent log. Successful name resolution is one checkpoint; authenticated tunnel establishment and intended traffic checks remain separate.

Why this step matters

Recovery requires the same client to progress through each connection layer without turning a DNS repair into an insecure VPN bypass.

What to understand

The journal command applies to the packaged Linux client unit shown in this Guide. A GUI, OpenVPN Connect, NetworkManager or another service manager stores logs elsewhere; inspect its actual connection log instead of treating an empty systemd unit log as success. Retain client and server timestamps when the failure changes from DNS to transport or TLS.

If an address is resolved but the handshake now fails, continue at the specific first transport or certificate error. A UDP port probe without a response does not prove that OpenVPN is down or reachable. Use the normal authenticated client exchange and owner-approved bounded network evidence; do not open every firewall port or disable remote-cert-tls to force progress.

Verify the correction after a controlled reconnect or the startup condition that originally failed, with recovery access available. Restore only the changed profile or network-manager setting if unrelated paths regress. Keep approved internal DNS and management access working; a result that succeeds only with a temporary hosts entry is not the completed repair.

System changes

  • Repeats a bounded lookup and reads recent logs. The normal reconnect is separately approved and may interrupt client traffic.

Syntax explained

openvpn-client@{{clientName}}
Selects the known packaged client instance; use the actual manager when a GUI or other integration owns the connection.
--since and tail -n 100
Limits displayed recent log evidence without changing logging or claiming a complete historical audit.
Command
Fill variables0/2 ready

Values stay on this page and are never sent or saved.

timeout 10 getent ahostsv4 '{{vpnHost}}'; printf 'lookup_exit=%s\n' "$?"; sudo journalctl -u 'openvpn-client@{{clientName}}' --since '-15 minutes' --no-pager | tail -n 100
Example output / evidence
Illustrative acceptance progression:
The active profile's endpoint resolves before the tunnel starts.
The OpenVPN log proceeds beyond RESOLVE and selects the intended remote endpoint.
The normal authenticated connection completes and intended routes/DNS work.
A controlled reconnect retains the correction; certificate and revocation checks remain enabled.

Checkpoint: Original connection verified

Continue whenThe endpoint resolves before tunnel establishment and the original client completes the intended authenticated connection after the scoped repair.

Stop whenOnly another client works, the log changes to an unresolved transport/TLS failure, or any identity or revocation check was removed.

04

command

Install the supported packages

caution

Install OpenVPN, Easy-RSA, and nftables from Ubuntu repositories.

Why this step matters

Using Ubuntu's signed packages keeps the OpenVPN binary, systemd templates, Easy-RSA tooling, and nftables integration within the distribution's security-update path. Confirming the actual version immediately prevents the rest of the tutorial from silently relying on OpenVPN 2.6 behavior when an older repository, container image, or manually installed binary is taking precedence.

What to understand

apt update refreshes repository metadata but does not upgrade unrelated packages. apt install resolves the supported OpenVPN, Easy-RSA, and nftables packages for Ubuntu 24.04. The final version commands are evidence: save them with the change record because service names, available directives, and cipher behavior are version-sensitive.

Inspect existing packages and listeners before installation on a reused host. A previous OpenVPN package, snap, container, or hand-built /usr/local/sbin/openvpn can make the shell and systemd run different binaries. Resolve that ambiguity now instead of debugging it after certificates and firewall policy have changed.

System changes

  • Installs the OpenVPN daemon, Easy-RSA PKI scripts, nftables userspace tools, systemd service templates, package documentation, and their dependencies.
  • Does not start a configured VPN instance because /etc/openvpn/server/server.conf does not exist yet.

Syntax explained

apt update
Downloads current package indexes from configured repositories without installing an upgrade.
apt install --yes
Installs the named packages and accepts the package transaction after apt has displayed it.
openvpn --version
Prints the binary version and linked crypto capabilities used to validate the OpenVPN 2.6 baseline.
Command
sudo apt update && sudo apt install --yes openvpn easy-rsa nftables && openvpn --version | head -n 1 && nft --version
Example output / evidence
OpenVPN 2.6.15 x86_64-pc-linux-gnu
nftables v1.0.9 (Old Doc Yak #3)

Checkpoint: Supported tools are present

openvpn --version | head -n 1 && test -x /usr/share/easy-rsa/easyrsa && nft --version

Continue whenOpenVPN reports 2.6.x, the Easy-RSA executable exists, and nftables prints a version without an error.

Stop whenOpenVPN is older than 2.6, comes from an unexpected path, or apt reports a broken or partially configured package transaction.

If this step fails

apt cannot locate openvpn or easy-rsa.

Likely causeRepository metadata is stale, the standard Ubuntu repositories are disabled, or the machine is not actually running the declared Ubuntu release.

Safe checks
  • cat /etc/os-release
  • apt-cache policy openvpn easy-rsa nftables
  • grep -R --line-number '^Types\|^URIs\|^Suites' /etc/apt/sources.list.d

ResolutionRestore the appropriate official Ubuntu repository configuration and rerun apt update. Do not add an unreviewed third-party repository merely to continue.

Security notes

  • Review the apt transaction before accepting it on a production host and record whether a reboot is already pending from earlier maintenance.

Alternatives

  • If organizational policy requires an internal mirror, verify that it mirrors Ubuntu Noble security updates and does not pin OpenVPN below the documented baseline.

Stop conditions

  • Do not continue when the installed binary or package origin does not match the reviewed Ubuntu 24.04 environment.
05

command

Create the CA and server identity

caution

Build an isolated PKI. Protect the CA private key and never embed it in a client profile.

Why this step matters

Mutual certificate authentication gives every server and client a distinct identity and makes individual revocation possible. The CA is created before the server identity because the server certificate must be signed by a trust root that clients can verify. Easy-RSA assigns the server certificate the extended key usage expected by remote-cert-tls verification.

What to understand

make-cadir creates a writable Easy-RSA workspace owned by the administrator rather than modifying package files under /usr/share. init-pki starts a new certificate database. build-ca should prompt for a strong passphrase and a recognizable CA name; that passphrase protects the trust root at rest.

build-server-full server nopass creates a server certificate and an unencrypted server private key. An unattended systemd service cannot type a passphrase during boot, so the server key relies on strict file permissions and host security. This convenience must not be copied to the CA key.

System changes

  • Creates ~/openvpn-ca with the CA database, CA certificate, encrypted CA private key, server request, signed server certificate, and unencrypted server private key.
  • Does not yet expose PKI material to /etc/openvpn/server or start a network listener.

Syntax explained

make-cadir ~/openvpn-ca
Copies the distribution's Easy-RSA scripts and variables into an administrator-owned working directory.
./easyrsa init-pki
Creates a fresh PKI database. Running it against an existing PKI can be destructive and requires a separate migration plan.
./easyrsa build-ca
Creates the self-signed CA certificate and prompts for protection of the CA private key.
build-server-full server nopass
Generates and signs a certificate with server purpose; nopass leaves only the server key unencrypted for unattended startup.
Command
make-cadir ~/openvpn-ca && cd ~/openvpn-ca && ./easyrsa init-pki && ./easyrsa build-ca && ./easyrsa build-server-full server nopass
Example output / evidence
Certificate created at: pki/issued/server.crt

Checkpoint: CA and server certificate form one valid chain

cd ~/openvpn-ca && openssl verify -CAfile pki/ca.crt pki/issued/server.crt && openssl x509 -in pki/issued/server.crt -noout -subject -issuer -dates

Continue whenopenssl verify prints pki/issued/server.crt: OK, and the issuer matches the new CA with a validity period acceptable to your policy.

Stop whenVerification fails, an unexpected CA signed the certificate, the certificate is already near expiry, or the CA private key was created without the intended protection.

If this step fails

Easy-RSA reports that a PKI already exists.

Likely causeThe selected directory contains an earlier CA or an interrupted attempt. Reinitializing it could destroy issuance and revocation history.

Safe checks
  • cd ~/openvpn-ca && ./easyrsa show-ca
  • find ~/openvpn-ca/pki -maxdepth 2 -type f -printf '%M %p\n'

ResolutionIdentify and back up the existing PKI. Reuse it only if it is the intended authority; otherwise create a new, clearly named working directory rather than running init-pki over unknown state.

Security notes

  • Back up pki/ca.crt, pki/private/ca.key, index.txt, serial, and the revocation database together. A CA key without its database makes lifecycle management unreliable.

Alternatives

  • For production, generate the server request on the gateway, import and sign it on an offline CA, then return only the signed certificate and CA certificate.

Stop conditions

  • Stop if the directory contains an unidentified existing CA or if the CA key is accidentally disclosed in terminal output, shell history, backup logs, or a shared location.
06

command

Generate and install server cryptographic material

caution

Create DH parameters, the initial revocation list, and a tls-crypt key, then install only the gateway runtime material under restrictive permissions.

Why this step matters

OpenVPN needs the server identity, a trusted CA, revocation evidence, and control-channel protection in the runtime directory. Generating and installing them as one reviewed stage avoids a service that starts with only part of the intended policy. The CRL exists before the first client is issued, so revocation is not an afterthought.

What to understand

Easy-RSA gen-dh creates Diffie-Hellman parameters used by this TLS server configuration. gen-crl signs the initial empty revocation list. openvpn --genkey secret creates the shared tls-crypt key that clients will also embed in their profiles.

Public certificates, DH parameters, and the CRL are installed read-only at mode 0644. The server private key and tls-crypt key are installed at mode 0600. Absolute paths in server.conf make the runtime location explicit and independent of the service's working directory.

System changes

  • Creates pki/dh.pem, pki/crl.pem, and pki/tls-crypt.key in the CA workspace.
  • Creates /etc/openvpn/server and installs only the files required by the public gateway; the CA private key remains outside that directory.

Syntax explained

./easyrsa gen-dh
Generates DH parameters for the server configuration and may take noticeable time on a small VM.
./easyrsa gen-crl
Publishes the CA's current revocation state as a signed PEM CRL.
openvpn --genkey secret
Creates a static key in OpenVPN format for tls-crypt control-channel protection.
install -m 0600
Copies secret material with owner-only permissions in one operation instead of relying on a later chmod.
Command
cd ~/openvpn-ca && ./easyrsa gen-dh && ./easyrsa gen-crl && openvpn --genkey secret pki/tls-crypt.key && sudo install -d -m 0750 /etc/openvpn/server && sudo install -m 0644 pki/ca.crt pki/issued/server.crt pki/dh.pem pki/crl.pem /etc/openvpn/server/ && sudo install -m 0600 pki/private/server.key pki/tls-crypt.key /etc/openvpn/server/
Example output / evidence
DH parameters of size 2048 created at pki/dh.pem
CRL file: pki/crl.pem
600 server.key
600 tls-crypt.key

Checkpoint: Runtime material is complete and internally consistent

sudo find /etc/openvpn/server -maxdepth 1 -type f -printf '%m %f\n' | sort && sudo openssl verify -CAfile /etc/openvpn/server/ca.crt /etc/openvpn/server/server.crt && sudo openssl crl -in /etc/openvpn/server/crl.pem -noout -lastupdate -nextupdate

Continue whenAll six required files are present, secret keys show mode 600, certificate verification is OK, and the CRL has a current update window.

Stop whenA file is missing, a secret is group/world-readable, certificate verification fails, or the CRL cannot be parsed.

If this step fails

gen-dh appears to hang or is terminated.

Likely causeThe operation is CPU-intensive on a small VM, or the process was interrupted before pki/dh.pem was completed.

Safe checks
  • pgrep -af 'easyrsa|openssl.*dh'
  • test -s ~/openvpn-ca/pki/dh.pem && openssl dhparam -in ~/openvpn-ca/pki/dh.pem -check -noout

ResolutionAllow the active process to finish. If it was interrupted, remove only the incomplete dh.pem after confirming no process uses it, then rerun gen-dh.

Security notes

  • The tls-crypt key is shared by every profile in this design. Exposure does not reveal the CA key, but it removes the outer control-channel protection and requires profile-wide rotation.

Alternatives

  • OpenVPN 2.6 supports tls-crypt-v2 for client-specific wrapped keys; adopt it when per-client control-key lifecycle is worth the additional provisioning complexity.

Stop conditions

  • Do not copy pki/private/ca.key into /etc/openvpn/server or any client profile.
07

config

Write the routed server configuration

caution

Use modern subnet topology, mutual certificate-purpose verification, explicit revocation, AEAD negotiation, and full-tunnel IPv4 routes.

Why this step matters

The server configuration turns the PKI and address plan into enforceable runtime behavior. It explicitly selects modern routed topology, requires client-purpose certificates, checks the CRL, limits TLS versions, negotiates modern AEAD ciphers, and drops privileges after initialization rather than relying on historical defaults.

What to understand

topology subnet makes the 10.8.0.0/24 example behave like an ordinary routed subnet for modern clients. The server helper assigns 10.8.0.1 to the server and allocates the remaining pool. redirect-gateway def1 installs two more-specific half-default routes, which preserve the client's direct route to the VPN endpoint while sending ordinary IPv4 traffic through the tunnel.

data-ciphers is an ordered negotiation list, not a command to use every cipher. remote-cert-tls client checks client certificate purpose. crl-verify rejects newly connecting certificates listed in the installed CRL. user and group reduce runtime privilege after OpenVPN has opened protected files and configured the tunnel.

System changes

  • Creates /etc/openvpn/server/server.conf, defining the public listener, tunnel pool, pushed routes and DNS, certificate paths, cipher policy, keepalive behavior, and runtime privilege.

Syntax explained

proto udp4
Listens with IPv4 UDP transport, matching this guide's explicit IPv4 firewall and forwarding design.
topology subnet
Uses modern subnet addressing instead of the legacy net30 allocation model.
server {{vpnNetwork}} {{vpnNetmask}}
Enables multi-client server mode and allocates tunnel addresses from the reviewed pool.
push "redirect-gateway def1 bypass-dhcp"
Sends full-tunnel IPv4 routing to clients while preserving their local DHCP path.
remote-cert-tls client
Requires a connecting certificate with TLS Web Client Authentication purpose.
data-ciphers
Orders acceptable AEAD data ciphers; the first mutually supported value is selected.
tls-crypt
Encrypts and authenticates control-channel packets before the TLS exchange is exposed.
user nobody / group nogroup
Drops the daemon's user and group privilege after privileged initialization.
File /etc/openvpn/server/server.conf
Configuration
Fill variables0/4 ready

Values stay on this page and are never sent or saved.

port {{vpnPort}}
proto udp4
dev tun
topology subnet
server {{vpnNetwork}} {{vpnNetmask}}
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS {{dnsServer}}"
ca /etc/openvpn/server/ca.crt
cert /etc/openvpn/server/server.crt
key /etc/openvpn/server/server.key
dh /etc/openvpn/server/dh.pem
crl-verify /etc/openvpn/server/crl.pem
verify-client-cert require
remote-cert-tls client
tls-version-min 1.2
tls-crypt /etc/openvpn/server/tls-crypt.key
data-ciphers AES-256-GCM:AES-128-GCM:?CHACHA20-POLY1305
auth SHA256
user nobody
group nogroup
persist-key
persist-tun
keepalive 10 120
explicit-exit-notify 1
verb 3
Example output / evidence
Configuration saved at /etc/openvpn/server/server.conf with unresolved placeholders replaced.

Checkpoint: Configuration references the intended material and network

sudo grep -Ev '^(#|$)' /etc/openvpn/server/server.conf && sudo test -r /etc/openvpn/server/server.key && sudo test -r /etc/openvpn/server/crl.pem

Continue whenThe rendered configuration contains the planned values and every referenced credential file is readable by the initializing service.

Stop whenAny placeholder remains unresolved, the VPN pool overlaps a real route, or the configuration points at credentials from another PKI.

If this step fails

OpenVPN later reports an unrecognized option.

Likely causeThe running binary is older than the documented baseline, a directive was misspelled, or a configuration line was copied with typographic quotes.

Safe checks
  • openvpn --version | head -n 1
  • sudo journalctl -u openvpn-server@server -b --no-pager -n 100
  • sudo sed -n '=' /etc/openvpn/server/server.conf

ResolutionCorrect the exact directive against the OpenVPN 2.6 manual or restore the supported package. Do not use ignore-unknown-option to hide a security-relevant mistake.

Security notes

  • Do not add compression for ordinary VPN traffic. Compression can expose secrets when attacker-controlled and sensitive data share a compressed encrypted stream.

Alternatives

  • For split tunneling, remove redirect-gateway and push only the specific network prefixes clients are authorized to reach.

Stop conditions

  • Do not proceed while unresolved {{placeholders}} or an unreviewed redirect, script, plugin, management socket, or compatibility cipher remains in the file.
08

config

Enable forwarding deliberately

caution

Persist forwarding only when clients must reach networks beyond the VPN server.

Why this step matters

A working tunnel interface does not automatically make the host a router. Linux must be allowed to forward IPv4 packets between tun0 and the public interface. Keeping this setting in a dedicated sysctl file makes the change visible, reviewable, and removable during rollback.

What to understand

net.ipv4.ip_forward controls forwarding globally for the network namespace. The value 1 permits the kernel to consider routing a packet; it does not bypass nftables. The forward chain still decides which traffic is allowed.

sysctl --system reloads all system configuration files, so review its complete output for an unrelated invalid setting. Query the final key explicitly because the last loaded assignment wins.

System changes

  • Creates /etc/sysctl.d/99-openvpn-forward.conf and changes the live net.ipv4.ip_forward value to 1.

Syntax explained

net.ipv4.ip_forward = 1
Enables IPv4 routing in the current network namespace after sysctl applies the file.
sysctl --system
Loads sysctl configuration from the standard system directories in precedence order.
File /etc/sysctl.d/99-openvpn-forward.conf
Configuration
net.ipv4.ip_forward = 1
Command
sudo sysctl --system && sysctl net.ipv4.ip_forward
Example output / evidence
* Applying /etc/sysctl.d/99-openvpn-forward.conf ...
net.ipv4.ip_forward = 1

Checkpoint: IPv4 forwarding is active and persistent

sysctl net.ipv4.ip_forward && cat /etc/sysctl.d/99-openvpn-forward.conf

Continue whenBoth the live value and the dedicated file show net.ipv4.ip_forward = 1.

Stop whensysctl reports an error, a later file forces the value back to 0, or forwarding is prohibited by the host's security design.

If this step fails

The setting returns to 0 after sysctl --system or reboot.

Likely causeAnother later-loading sysctl file overrides it, or configuration management restores a conflicting policy.

Safe checks
  • grep -R --line-number 'net.ipv4.ip_forward' /etc/sysctl.conf /etc/sysctl.d /usr/lib/sysctl.d
  • journalctl -b -u systemd-sysctl --no-pager

ResolutionRemove the conflict from the authoritative configuration source or choose a filename and policy ordering approved for the environment. Do not repeatedly overwrite a managed file by hand.

Security notes

  • Forwarding expands the host's role from endpoint to router. The following firewall policy must be applied before untrusted clients are allowed to connect.

Alternatives

  • Use a dedicated network namespace or separate VPN appliance when the base host must not become a general-purpose router.

Stop conditions

  • Stop if another platform owns sysctl networking policy or if enabling forwarding would violate the host's isolation assumptions.
09

warning

Validate and activate the dedicated gateway firewall

danger

Stage a complete default-drop policy, preserve the previous file, validate before installation, and keep console plus a second SSH session open.

Why this step matters

The firewall is the enforcement boundary between an authenticated VPN identity and the rest of the host or internet. This example deliberately owns the complete nftables ruleset on a dedicated gateway: it preserves loopback and established traffic, limits SSH to one management CIDR, opens only the VPN UDP port, allows forwarding only from the tunnel pool to the public interface, and performs IPv4 masquerading.

What to understand

Write the proposed policy to /etc/nftables.conf.oneliners first. The apply command saves the existing persistent file, validates the complete candidate without changing live rules, installs it only after that check succeeds, and then enables nftables. Keep the console and a second SSH session open throughout.

flush ruleset makes this file authoritative. That is appropriate only under the dedicated-host assumptions stated above. The input and forward chains default to drop, while output defaults to accept. Established replies are accepted before narrow new-connection rules. NAT occurs only for packets whose source belongs to {{vpnNetworkCidr}} and whose egress interface is {{publicInterface}}.

System changes

  • Replaces the persistent /etc/nftables.conf after creating /etc/nftables.conf.before-openvpn.
  • Flushes the live nftables ruleset when the service loads the new file, establishes default-drop input and forward chains, and creates an IPv4 postrouting masquerade rule.

Syntax explained

nft --check --file
Parses and validates the complete candidate ruleset without applying it.
policy drop
Drops packets that reach the end of the input or forward chain without an explicit accept rule.
ct state established,related accept
Allows reply traffic for connections that the policy already permitted and related protocol flows tracked by conntrack.
ip saddr $management_net
Restricts new SSH connections to the reviewed IPv4 management source range.
masquerade
Rewrites VPN client source addresses to the current address of the public egress interface.
File /etc/nftables.conf.oneliners
Configuration
Fill variables0/5 ready

Values stay on this page and are never sent or saved.

#!/usr/sbin/nft -f

flush ruleset

define public_if = "{{publicInterface}}"
define management_net = {{managementCidr}}
define vpn_net = {{vpnNetworkCidr}}
define ssh_port = {{sshPort}}
define vpn_port = {{vpnPort}}

table inet oneliners_openvpn_filter {
  chain input {
    type filter hook input priority filter; policy drop;
    iifname "lo" accept
    ct state invalid drop
    ct state established,related accept
    ip protocol icmp accept
    ip6 nexthdr icmpv6 accept
    iifname $public_if ip saddr $management_net tcp dport $ssh_port accept
    iifname $public_if udp dport $vpn_port accept
  }

  chain forward {
    type filter hook forward priority filter; policy drop;
    ct state invalid drop
    ct state established,related accept
    iifname "tun0" oifname $public_if ip saddr $vpn_net accept
  }

  chain output {
    type filter hook output priority filter; policy accept;
  }
}

table ip oneliners_openvpn_nat {
  chain postrouting {
    type nat hook postrouting priority srcnat; policy accept;
    oifname $public_if ip saddr $vpn_net masquerade
  }
}
Command
sudo cp --archive /etc/nftables.conf /etc/nftables.conf.before-openvpn && sudo nft --check --file /etc/nftables.conf.oneliners && echo 'nftables syntax: valid' && sudo install -o root -g root -m 0644 /etc/nftables.conf.oneliners /etc/nftables.conf && sudo systemctl enable --now nftables && systemctl is-active nftables
Example output / evidence
nftables syntax: valid
active

Checkpoint: Policy is active without losing recovery access

sudo nft list ruleset && systemctl is-active nftables && ss -lnt | grep ':{{sshPort}}'

Continue whenThe named OpenVPN tables and rules are present, nftables is active, the SSH listener remains present, and a second management session still works.

Stop whenThe candidate fails validation, the interface or CIDR is wrong, the second SSH connection fails, or unrelated production forwarding disappears.

If this step fails

nft --check reports a syntax error.

Likely causeA placeholder was not replaced, an interface name contains an unexpected value, or the file was damaged while copying.

Safe checks
  • grep -n '{{\|}}' /etc/nftables.conf.oneliners
  • ip -brief link
  • sudo nft --check --debug=parser --file /etc/nftables.conf.oneliners

ResolutionCorrect the candidate and rerun the check. Because validation happens before install, leave the current live and persistent policy untouched until the candidate is clean.

SSH disconnects or a new management session cannot connect after activation.

Likely cause{{managementCidr}}, {{sshPort}}, or {{publicInterface}} does not match the real management path, or an upstream firewall uses a different source after NAT.

Safe checks
  • ip -brief address
  • ss -lntp
  • sudo nft monitor trace

ResolutionUse console access to restore /etc/nftables.conf.before-openvpn and restart nftables. Determine the actual observed source and listener before preparing a corrected candidate.

Security notes

  • Do not broaden SSH to 0.0.0.0/0 as a quick repair. Restore the prior ruleset through the console and correct the management path deliberately.

Alternatives

  • If an existing firewall manager owns the host, translate these narrow input, forward, and NAT requirements into that manager instead of loading this complete ruleset.

Stop conditions

  • Do not activate the candidate without a successful syntax check, console recovery, and a verified second SSH session from the intended management source.
10

command

Start and inspect the VPN

caution

Enable the instance and inspect first startup before distributing profiles.

Why this step matters

Starting the service is the first point where configuration, protected files, kernel TUN support, socket binding, and privilege dropping are exercised together. Inspect the journal immediately rather than treating systemd's active state alone as proof that the VPN is usable.

What to understand

openvpn-server@server maps to /etc/openvpn/server/server.conf in Ubuntu's packaged systemd template. enable records the intended boot behavior; --now starts the instance in the current boot. The status and journal provide separate evidence of process state and OpenVPN initialization.

The phrase Initialization Sequence Completed indicates that the daemon passed configuration and tunnel initialization. It does not yet prove external reachability, client authentication, forwarding, DNS, or revocation; those are tested later.

System changes

  • Enables openvpn-server@server for future boots, binds {{vpnPort}}/UDP, creates tun0, assigns the server tunnel address, and starts journal logging.

Syntax explained

systemctl enable --now
Enables the instance for boot and starts it immediately as one explicit operation.
openvpn-server@server
Selects the packaged server template and the server.conf instance name.
journalctl -u
Restricts diagnostic output to the exact OpenVPN systemd instance.
Command
sudo systemctl enable --now openvpn-server@server && systemctl --no-pager --full status openvpn-server@server
Example output / evidence
Active: active (running)

Checkpoint: The server initialized and is listening

systemctl is-active openvpn-server@server && sudo ss -lunp | grep ':{{vpnPort}}' && sudo journalctl -u openvpn-server@server -b --no-pager | tail -n 30

Continue whenThe unit is active, OpenVPN owns the intended UDP socket, tun0 has 10.8.0.1/24, and the journal reports Initialization Sequence Completed.

Stop whenThe unit restarts repeatedly, another process owns the port, tun0 is absent, or the journal contains certificate, CRL, cipher, permission, or route errors.

If this step fails

The unit fails with Cannot open TUN/TAP dev /dev/net/tun.

Likely causeThe TUN kernel device is unavailable in the VM or container, the module is not loaded, or the hosting platform forbids it.

Safe checks
  • ls -l /dev/net/tun
  • sudo modprobe tun
  • systemd-detect-virt

ResolutionEnable TUN support in the hypervisor or hosting platform and verify /dev/net/tun. Do not run a privileged container as a shortcut without reviewing the expanded host access.

Security notes

  • Do not distribute client profiles until the service, firewall, and journal all pass their checkpoints.

Alternatives

  • Keep the instance disabled during a change window by starting without enable; enable it only after the external client test succeeds.

Stop conditions

  • Stop on any repeating systemd restart or OpenVPN initialization warning instead of increasing restart limits or log verbosity indefinitely.
11

verification

Classify VERIFY ERROR and TLS handshake failed before changing anything

read-only

Collect read-only evidence from both peers first. VERIFY ERROR and peer certificate verification failure mean the peers exchanged enough TLS data to reject a certificate; TLS Error: TLS handshake failed is the final summary and may instead follow a tls-crypt mismatch, blocked transport, incompatible TLS policy, or a preceding certificate error. Preserve the first error in the log and do not remove remote-cert-tls, verify-x509-name, the CA, or CRL checks to force a connection.

Why this step matters

The first concrete log line identifies the failed layer. A certificate rejection requires different remediation from a packet that never reaches the TLS stack. Checking time, chain, purpose, identity, validity, and revocation before changing configuration prevents an unsafe workaround from hiding an expired, wrongly issued, revoked, or impersonating peer.

What to understand

Compare client and server timestamps for one attempt. OpenVPN often emits TLS Error: TLS handshake failed after a more specific VERIFY ERROR; keep the earlier line, certificate depth, OpenSSL reason, subject, issuer, serial, and remote address together.

A depth=0 failure refers to the peer certificate. Higher depths point into the issuing chain. certificate has expired and certificate is not yet valid require checking both the certificate dates and both system clocks. unable to get local issuer certificate usually means the profile carries the wrong CA or an incomplete trust path.

A certificate can have a valid signature and still be the wrong identity. The client should require remote-cert-tls server and, when the deployment owns a stable name, verify-x509-name. The server should require a client-purpose certificate. Do not convert a server certificate into a client certificate or weaken EKU checks.

If no certificate error appears and the server sees no authenticated control packet, compare the exact remote host, port, protocol, tls-crypt or tls-auth material, key direction where tls-auth is used, firewall counters, and packet capture on both sides.

System changes

  • None. The commands read time synchronization, recent service logs, certificate metadata, chain verification, and CRL validity.

Syntax explained

journalctl --since '-15 minutes'
Limits evidence to the current connection attempt so the first causal TLS line is not confused with older failures.
openssl verify -purpose sslserver
Checks the server certificate chain and server-authentication purpose against the exact CA installed for OpenVPN.
openssl x509 -noout -subject -issuer -serial -dates
Prints identity, issuer, serial number, and validity without exposing a private key.
-ext extendedKeyUsage -ext subjectAltName
Shows certificate purpose and names used by remote-cert-tls and identity verification.
openssl crl -noout -lastupdate -nextupdate
Confirms that the server's revocation evidence parses and is within its publication window.
Uses verified command record: openvpn-logs
Command
timedatectl status; sudo journalctl -u openvpn-server@server --since '-15 minutes' --no-pager; sudo openssl verify -purpose sslserver -CAfile /etc/openvpn/server/ca.crt /etc/openvpn/server/server.crt; sudo openssl x509 -in /etc/openvpn/server/server.crt -noout -subject -issuer -serial -dates -ext extendedKeyUsage -ext subjectAltName; sudo openssl crl -in /etc/openvpn/server/crl.pem -noout -lastupdate -nextupdate
Example output / evidence
System clock synchronized: yes
Aug 23 16:42:08 vpn01 openvpn[1842]: VERIFY ERROR: depth=0, error=certificate has expired: CN=vpn.example.net, serial=4A21
Aug 23 16:42:08 vpn01 openvpn[1842]: peer certificate verification failure
Aug 23 16:42:08 vpn01 openvpn[1842]: TLS Error: TLS handshake failed
error /etc/openvpn/server/server.crt: verification failed
notAfter=Aug 22 23:59:59 2026 GMT

Checkpoint: The failed TLS layer is identified

sudo journalctl -u openvpn-server@server --since '-15 minutes' --no-pager | grep -E 'VERIFY ERROR|peer certificate verification failure|TLS Error|Authenticate/Decrypt packet error|Initialization Sequence Completed'

Continue whenThe evidence contains either a specific certificate reason, a control-key or transport error, or a successful Initialization Sequence Completed line. Record the first failure, not only the final handshake summary.

Stop whenThe clocks are unsynchronized, logs from both peers are unavailable, the certificate or CA origin is unknown, or a proposed workaround disables verification.

If this step fails

VERIFY ERROR: depth=0, error=certificate has expired, certificate is not yet valid, or unable to get local issuer certificate.

Likely causeThe peer certificate is outside its validity window, a clock is wrong, the profile trusts another CA, or the issuing chain does not match the deployed PKI.

Safe checks
  • date -u; timedatectl status
  • openssl x509 -in /etc/openvpn/server/server.crt -noout -subject -issuer -serial -dates
  • openssl verify -purpose sslserver -CAfile /etc/openvpn/server/ca.crt /etc/openvpn/server/server.crt

ResolutionCorrect time synchronization first. Otherwise issue a replacement certificate with the intended purpose from the intended CA, install it with its matching private key, distribute a reviewed profile when trust material changed, and keep the rejected certificate for audit until the repair is verified.

Inspect TLS certificate details
peer certificate verification failure appears after the peers exchange packets.

Likely causeThe certificate signature, purpose, expected name, revocation state, or local trust anchor failed policy even though the endpoint was reachable.

Safe checks
  • openssl verify -purpose sslserver -CAfile /etc/openvpn/server/ca.crt /etc/openvpn/server/server.crt
  • openssl x509 -in /etc/openvpn/server/server.crt -noout -text
  • grep -E '^(ca|remote-cert-tls|verify-x509-name|peer-fingerprint|crl-verify)' client.ovpn

ResolutionRepair the mismatched certificate, CA, purpose, expected identity, or CRL. Keep remote-cert-tls and identity verification enabled; do not replace the CA with an unrelated certificate merely because it makes the error disappear.

Inspect recent OpenVPN logs
TLS Error: TLS handshake failed appears without a preceding VERIFY ERROR.

Likely causeThe tls-crypt or tls-auth material differs, packets reach the wrong OpenVPN listener, the transport is blocked or translated incorrectly, or client and server TLS policies do not overlap.

Safe checks
  • sudo ss -lunp | grep ':{{vpnPort}}'
  • sudo tcpdump -ni any 'port {{vpnPort}}' -c 30
  • grep -E '^(remote|proto|tls-crypt|tls-auth|key-direction|tls-version-min|data-ciphers)' client.ovpn /etc/openvpn/server/server.conf

ResolutionCompare the profile with the deployed listener and control-channel key. Restore the correct tls-crypt material or endpoint, then retest. Upgrade obsolete peers before considering a documented compatibility exception.

Check whether the VPN port accepts traffic

Security notes

  • Never fix certificate verification by adding an insecure flag, deleting remote-cert-tls, accepting any X.509 name, replacing the CA with an unverified file, or disabling CRL checks.
  • Do not paste private keys, inline profiles, tls-crypt keys, or authentication tokens into tickets or diagnostic output.

Alternatives

  • For a small, intentionally pinned deployment, OpenVPN 2.6 supports peer-fingerprint as an alternative trust model; document and rotate the SHA-256 fingerprint deliberately rather than mixing it accidentally with an unknown CA.
  • Use tls-crypt-v2 when per-client control-channel keys and revocation metadata are required.

Stop conditions

  • Stop if the certificate and private key do not match, the issuing CA is unknown, or replacing trust material would affect clients that have not been inventoried.
  • Stop if the only proposed repair is to weaken peer verification or allow an expired, revoked, wrongly purposed, or unexpected identity.
12

command

Issue a unique client certificate and restricted profile

caution

Generate one passphrase-protected identity per person or device and assemble a self-contained profile with only client-side material.

Why this step matters

A unique certificate per person or device creates an identity that can be audited and revoked without affecting every user. The inline profile packages the public CA, client certificate, encrypted client private key, and tls-crypt key into one restricted file while preserving server-purpose verification and modern cipher negotiation.

What to understand

build-client-full prompts for the CA passphrase to sign and for a client-key passphrase unless local Easy-RSA policy says otherwise. The profile intentionally omits nopass so copying the file alone is not sufficient to use the private key. Use a client name that maps to an asset or operator record, not a shared department identity.

The shell block writes fixed client directives and then appends reviewed PEM sections. sed starts the certificate at BEGIN CERTIFICATE so Easy-RSA's human-readable preamble is not embedded. The resulting file is mode 0600 and should move to the client over an authenticated, confidential administrative channel.

System changes

  • Adds one client request, encrypted private key, signed certificate, and issuance record to the CA workspace.
  • Creates ~/client-configs/{{clientName}}.ovpn containing the client's complete credential bundle and connection policy.

Syntax explained

build-client-full {{clientName}}
Generates a client-purpose key and certificate and records the issuance in the CA database.
remote-cert-tls server
Prevents a different valid client certificate from impersonating the VPN server.
auth-nocache
Requests that OpenVPN not retain interactive authentication material in virtual memory after use.
<ca>, <cert>, <key>, <tls-crypt>
Embeds the four referenced PEM or OpenVPN key files in a portable profile without external file paths.
chmod 0600
Limits local profile access to its owner because the file contains a private key.
Command
Fill variables0/3 ready

Values stay on this page and are never sent or saved.

cd ~/openvpn-ca && ./easyrsa build-client-full {{clientName}} && install -d -m 0700 "$HOME/client-configs" && { printf '%s\n' 'client' 'dev tun' 'proto udp4' 'remote {{vpnHost}} {{vpnPort}}' 'resolv-retry infinite' 'nobind' 'persist-key' 'persist-tun' 'remote-cert-tls server' 'auth-nocache' 'data-ciphers AES-256-GCM:AES-128-GCM:?CHACHA20-POLY1305' 'verb 3' '<ca>'; cat pki/ca.crt; printf '%s\n' '</ca>' '<cert>'; sed -ne '/-----BEGIN CERTIFICATE-----/,$p' "pki/issued/{{clientName}}.crt"; printf '%s\n' '</cert>' '<key>'; cat "pki/private/{{clientName}}.key"; printf '%s\n' '</key>' '<tls-crypt>'; cat pki/tls-crypt.key; printf '%s\n' '</tls-crypt>'; } > "$HOME/client-configs/{{clientName}}.ovpn" && chmod 0600 "$HOME/client-configs/{{clientName}}.ovpn" && stat -c '%a %n' "$HOME/client-configs/{{clientName}}.ovpn"
Example output / evidence
Certificate created at: pki/issued/laptop-alice.crt
600 /home/admin/client-configs/laptop-alice.ovpn

Checkpoint: Profile contains one matching client identity

cd ~/openvpn-ca && openssl verify -CAfile pki/ca.crt pki/issued/{{clientName}}.crt && openssl x509 -in pki/issued/{{clientName}}.crt -noout -subject -issuer -dates && stat -c '%a %n' ~/client-configs/{{clientName}}.ovpn

Continue whenCertificate verification is OK, the subject matches {{clientName}}, validity is acceptable, and the profile mode is 600.

Stop whenThe certificate name is shared, the key was created without the intended passphrase, verification fails, or the profile is readable by another user.

If this step fails

Easy-RSA says a certificate with this name already exists.

Likely causeThe identity was previously issued. Reusing the name would make audit and revocation ambiguous.

Safe checks
  • cd ~/openvpn-ca && ./easyrsa show-cert {{clientName}}
  • grep -F '/CN={{clientName}}' ~/openvpn-ca/pki/index.txt

ResolutionDetermine whether the existing certificate belongs to the same asset. Renew or revoke through the documented lifecycle, or choose a new unique identity; do not delete CA database rows by hand.

Security notes

  • Treat the generated .ovpn file as a secret even when its private key has a passphrase. Do not paste it into the OneLiners site, tickets, chat, or source control.

Alternatives

  • Use separate certificate and key files when the client platform can protect them in an OS keychain, TPM, smart card, or managed configuration profile.

Stop conditions

  • Do not distribute a profile until its certificate purpose, chain, name, validity, file permissions, and destination owner are verified.
13

verification

Test from an external network

read-only

Confirm tunnel address, intended routes, and connectivity from outside the server network.

Why this step matters

Testing from outside the server network proves the layers that local checks cannot: public DNS, upstream firewall, UDP reachability, mutual certificate authentication, pushed routes, DNS, forwarding, and NAT. A successful process on the server alone is not evidence that a remote operator can recover access.

What to understand

Install the test profile on a disposable Ubuntu client connected through a different network, such as a mobile hotspot. The packaged openvpn-client@ template reads /etc/openvpn/client/{{clientName}}.conf. Keep the client journal open during the first connection.

The route lookup should select tun0 for an internet address after redirect-gateway def1. The tunnel address should be inside {{vpnNetworkCidr}}, and 10.8.0.1 should respond as the server endpoint. DNS resolution must succeed after the pushed resolver is active.

System changes

  • Installs the restricted profile on the external test client, enables one OpenVPN client instance, creates tun0, and changes that client's IPv4 routing and DNS behavior while connected.

Syntax explained

openvpn-client@{{clientName}}
Maps the instance name to /etc/openvpn/client/{{clientName}}.conf on Ubuntu.
ip route get 1.1.1.1
Shows which interface and next hop the kernel would use without sending application data to that address.
getent ahosts example.com
Exercises the client's configured resolver path through the standard name-service switch.
Command
Fill variables0/1 ready

Values stay on this page and are never sent or saved.

sudo install -m 0600 "$HOME/client-configs/{{clientName}}.ovpn" "/etc/openvpn/client/{{clientName}}.conf" && sudo systemctl enable --now "openvpn-client@{{clientName}}" && systemctl is-active "openvpn-client@{{clientName}}" && ip -brief address show dev tun0 && ip route get 1.1.1.1 && ping -c 3 10.8.0.1 && getent ahosts example.com | head
Example output / evidence
active
tun0  UP  10.8.0.2/24
1.1.1.1 via 10.8.0.1 dev tun0 src 10.8.0.2
3 packets transmitted, 3 received
93.184.216.34 STREAM example.com

Checkpoint: Remote client uses the intended tunnel

systemctl is-active openvpn-client@{{clientName}} && ip -brief address show dev tun0 && ip route get 1.1.1.1 && ping -c 3 10.8.0.1 && getent ahosts example.com | head

Continue whenThe unit is active, tun0 has an address in {{vpnNetworkCidr}}, route lookup selects tun0, all three server-tunnel pings return, and DNS produces addresses.

Stop whenThe route still uses the local gateway, DNS stops working, IPv6 bypasses the intended policy, or access succeeds only from the server's own network.

If this step fails

The client times out without any corresponding server journal entry.

Likely causeThe hostname resolves incorrectly, the upstream or provider firewall blocks UDP, the port is not forwarded, or the client network blocks the selected transport.

Safe checks
  • getent ahosts {{vpnHost}}
  • sudo tcpdump -ni {{publicInterface}} udp port {{vpnPort}}
  • sudo ss -lunp | grep ':{{vpnPort}}'

ResolutionCorrect DNS or the upstream firewall and confirm packets arrive at the public interface. Do not weaken certificate verification when the TLS handshake never reached the server.

Security notes

  • A connected client now sends ordinary IPv4 traffic through the gateway. Do not perform the first test from a workstation whose critical access depends on an untested local route.

Alternatives

  • For cross-platform acceptance, repeat the same evidence collection on every supported client family after the Ubuntu command-line canary passes.

Stop conditions

  • Stop deployment if any traffic family or DNS path bypasses the intended tunnel and the security requirement is full-tunnel enforcement.
14

instruction

Exercise certificate revocation

caution

Revoke a disposable client, regenerate and deploy the CRL, then confirm the profile cannot reconnect.

Why this step matters

Issuance without a tested revocation path leaves the operator unable to remove a stolen or retired device safely. Revoking a disposable canary proves that the CA database, CRL generation, server installation path, service restart, and client rejection behavior work together before a real incident.

What to understand

revoke changes the certificate status in the CA database; gen-crl creates a newly signed list. Installing that CRL at /etc/openvpn/server/crl.pem publishes the decision to the gateway. The service restart ensures the running instance reads the new file.

The client certificate and key files may remain on disk for evidence, but they must not be reissued as valid. Compare the revoked certificate serial with the CRL and then attempt a new connection. An already established session should be disconnected during an actual compromise.

System changes

  • Marks {{clientName}} revoked in the CA database, replaces the generated and installed CRL, and restarts the OpenVPN server instance.
  • Prevents new TLS authentication by the revoked certificate while leaving other certificates under the same CA valid.

Syntax explained

./easyrsa revoke {{clientName}}
Records the certificate as revoked in the authoritative CA database.
./easyrsa gen-crl
Generates a signed CRL containing every currently revoked certificate serial.
install -m 0644 pki/crl.pem
Publishes the reviewed CRL atomically at the server path without exposing private key material.
systemctl restart
Forces the server instance to reopen configuration and revocation material.
Command
Fill variables0/1 ready

Values stay on this page and are never sent or saved.

cd ~/openvpn-ca && ./easyrsa revoke {{clientName}} && ./easyrsa gen-crl && sudo install -o root -g root -m 0644 pki/crl.pem /etc/openvpn/server/crl.pem && sudo systemctl restart openvpn-server@server && systemctl is-active openvpn-server@server
Example output / evidence
Revoking Certificate ...
CRL file: pki/crl.pem
active

Checkpoint: Revoked serial is rejected while service remains healthy

openssl x509 -in ~/openvpn-ca/pki/issued/{{clientName}}.crt -noout -serial && openssl crl -in /etc/openvpn/server/crl.pem -noout -text | grep -A2 'Revoked Certificates' && systemctl is-active openvpn-server@server

Continue whenThe client serial appears in the CRL, the server remains active, and a fresh connection using that profile fails with a revocation-related verification message.

Stop whenThe serial is absent, the wrong CA produced the CRL, the server fails to restart, or other valid canary clients can no longer connect.

If this step fails

The CRL shows no revoked certificates after revoke.

Likely causegen-crl ran in a different Easy-RSA workspace or the revoke operation did not update the intended CA database.

Safe checks
  • grep -F '/CN={{clientName}}' ~/openvpn-ca/pki/index.txt
  • openssl x509 -in ~/openvpn-ca/pki/issued/{{clientName}}.crt -noout -issuer -serial
  • openssl crl -in ~/openvpn-ca/pki/crl.pem -noout -issuer -lastupdate

ResolutionReturn to the authoritative CA workspace, confirm the certificate row is marked revoked, regenerate the CRL, and verify issuer and serial before installation.

Security notes

  • If the CA private key is suspected compromised, individual revocation is insufficient. Build a new PKI and rotate every server and client identity.

Alternatives

  • For routine retirement, revoke after a replacement identity is proven. For active compromise, revoke immediately and terminate the current session before issuing replacement credentials.

Stop conditions

  • Do not declare revocation complete until a real new handshake with the revoked profile is rejected and a separate valid canary still succeeds.

Finish line

Verification checklist

Service healthsystemctl is-active openvpn-server@serverReturns active.
Listening socketss -lunp | grep ':1194'Shows OpenVPN on the intended UDP address.
Client tunnelip address show dev tun0Client receives an address from the planned subnet.

Recovery guidance

Common problems and safe checks

The systemd instance exits immediately and the journal reports that a certificate, key, DH file, CRL, or tls-crypt file cannot be opened.

Likely causeA path in server.conf does not match the installed PKI layout, a file was copied under the wrong name, or permissions prevent the process from opening material during initialization.

Safe checks
  • sudo journalctl -u openvpn-server@server -b --no-pager -n 100
  • sudo namei -l /etc/openvpn/server/server.key /etc/openvpn/server/crl.pem
  • sudo openssl x509 -in /etc/openvpn/server/server.crt -noout -subject -issuer -dates

ResolutionCorrect the path or reinstall the intended file from the reviewed PKI. Keep the private key at mode 0600 and public certificates or the CRL readable as required. Do not replace a missing file with material from an unrelated CA.

The tunnel connects and receives 10.8.0.x, but internet traffic or DNS does not work.

Likely causeIPv4 forwarding is disabled, the forward chain does not match tun0 and the real egress interface, masquerading is absent, the provider blocks forwarding, or the pushed resolver is not reachable through the selected route.

Safe checks
  • sysctl net.ipv4.ip_forward
  • sudo nft list ruleset
  • ip route get 1.1.1.1
  • sudo tcpdump -ni tun0 -c 20

ResolutionFix the first failing layer rather than adding broad accept rules. Confirm packets enter tun0, match the intended forward rule, leave the actual public interface, and receive replies. Select a resolver that is reachable after the full-tunnel routes are installed.

A revoked client still reconnects successfully.

Likely causeThe certificate was revoked in a different PKI, gen-crl was not run, the new crl.pem was not installed at the server's crl-verify path, or an already active session was never terminated.

Safe checks
  • openssl crl -in /etc/openvpn/server/crl.pem -noout -lastupdate -nextupdate
  • openssl x509 -in pki/issued/{{clientName}}.crt -noout -serial
  • sudo journalctl -u openvpn-server@server --since '-10 min' --no-pager

ResolutionGenerate the CRL from the CA that issued the certificate, install it atomically at the configured path, and restart the instance. Disconnect an existing session during the incident and confirm a new handshake is rejected for the revoked serial.

VERIFY ERROR: depth=0, error=certificate has expired, certificate is not yet valid, or unable to get local issuer certificate.

Likely causeThe peer certificate is outside its validity window, a clock is wrong, the profile trusts another CA, or the issuing chain does not match the deployed PKI.

Safe checks
  • date -u; timedatectl status
  • openssl x509 -in /etc/openvpn/server/server.crt -noout -subject -issuer -serial -dates
  • openssl verify -purpose sslserver -CAfile /etc/openvpn/server/ca.crt /etc/openvpn/server/server.crt

ResolutionCorrect time synchronization first. Otherwise issue a replacement certificate with the intended purpose from the intended CA, install it with its matching private key, distribute a reviewed profile when trust material changed, and keep the rejected certificate for audit until the repair is verified.

Inspect TLS certificate details
peer certificate verification failure appears after the peers exchange packets.

Likely causeThe certificate signature, purpose, expected name, revocation state, or local trust anchor failed policy even though the endpoint was reachable.

Safe checks
  • openssl verify -purpose sslserver -CAfile /etc/openvpn/server/ca.crt /etc/openvpn/server/server.crt
  • openssl x509 -in /etc/openvpn/server/server.crt -noout -text
  • grep -E '^(ca|remote-cert-tls|verify-x509-name|peer-fingerprint|crl-verify)' client.ovpn

ResolutionRepair the mismatched certificate, CA, purpose, expected identity, or CRL. Keep remote-cert-tls and identity verification enabled; do not replace the CA with an unrelated certificate merely because it makes the error disappear.

Inspect recent OpenVPN logs
TLS Error: TLS handshake failed appears without a preceding VERIFY ERROR.

Likely causeThe tls-crypt or tls-auth material differs, packets reach the wrong OpenVPN listener, the transport is blocked or translated incorrectly, or client and server TLS policies do not overlap.

Safe checks
  • sudo ss -lunp | grep ':{{vpnPort}}'
  • sudo tcpdump -ni any 'port {{vpnPort}}' -c 30
  • grep -E '^(remote|proto|tls-crypt|tls-auth|key-direction|tls-version-min|data-ciphers)' client.ovpn /etc/openvpn/server/server.conf

ResolutionCompare the profile with the deployed listener and control-channel key. Restore the correct tls-crypt material or endpoint, then retest. Upgrade obsolete peers before considering a documented compatibility exception.

Check whether the VPN port accepts traffic
RESOLVE: Cannot resolve host address

Likely causeThe active profile's endpoint is misspelled, absent from the appropriate zone, unreachable through the pre-tunnel resolver, or unavailable in the client's startup or address-family context.

Safe checks
  • Retain the first RESOLVE line and active profile endpoint without sharing inline secrets.
  • Repeat a bounded lookup in the original client context and configured address family.
  • Inspect resolver ownership and pre-tunnel dependencies before changing retry policy.

ResolutionRepair the owned endpoint or resolver/startup boundary, then verify the original authenticated client and a controlled reconnect.

After the procedure

Alternatives and next steps

Consider these alternatives

  • Use split tunneling by removing redirect-gateway and pushing only reviewed private routes when clients should keep ordinary internet traffic on their local connection.
  • Use WireGuard when a smaller static-key configuration and explicit peer routing better match the environment. OpenVPN remains useful when certificate lifecycle, mature cross-platform clients, or TLS-based policy is required.
  • Use a managed or distribution-integrated firewall policy instead of replacing /etc/nftables.conf when the host already belongs to UFW, firewalld, cloud-init, or configuration management. Translate the same allowlist and forwarding intent into that single authoritative system.

Operate it safely

  • Move CA signing away from the public gateway, maintain encrypted offline backups of the CA database and keys, and document a tested recovery procedure before issuing production identities.
  • Monitor service state, certificate and CRL expiry, repeated authentication failures, connected-client count, and unexpected changes to the listening socket or nftables ruleset.
  • Schedule certificate renewal and tls-crypt key rotation. Test the overlap period with a canary client so rotation does not create a simultaneous outage for every remote administrator.
  • Build a second recovery path before making the VPN the only route to critical infrastructure. A VPN cannot repair itself when the gateway, DNS, upstream firewall, or certificate authority is unavailable.
  • Link monitoring for VERIFY ERROR, peer certificate verification failure, and repeated TLS Error: TLS handshake failed to the TLS validation runbook and retain certificate serial, issuer, and expiry evidence.
  • For deeper resolver and transport isolation, continue with [Temporary failure in name resolution and Linux network paths](/guides/diagnose-packet-loss-mtu-routing-dns-linux).

Reference

Frequently asked questions

Why use UDP instead of TCP?

OpenVPN carries application traffic that may already use TCP. UDP avoids nesting one congestion-controlled TCP connection inside another, which can amplify retransmission and head-of-line blocking. TCP transport remains useful where a network permits only TCP, but it should be an intentional compatibility choice.

Why does the client profile verify remote-cert-tls server?

Trusting the CA alone proves only that a certificate was signed by that CA. Certificate-purpose verification prevents another valid client certificate from impersonating the server and is an important part of mutual TLS identity separation.

Should the CA be created with nopass?

Not for a production trust root. The server key usually needs unattended startup and is commonly generated without an interactive passphrase, but the CA key is used only during issuance and revocation and should be encrypted. Protect the CA passphrase separately from its key backup.

Can this ruleset be added to a Docker or Kubernetes host?

Not unchanged. Those platforms create and depend on forwarding, NAT, and bridge rules of their own. Use a dedicated VPN gateway or merge the intended OpenVPN rules into the platform's authoritative firewall design after testing every workload and recovery path.

How do I fix OpenVPN VERIFY ERROR and peer certificate verification failure?

Read the first VERIFY ERROR line and keep its depth and OpenSSL reason. Check UTC time on both peers, the peer certificate dates, issuer, serial, chain against the configured CA, extended key usage, expected X.509 identity, and CRL. Replace or renew only the invalid object, then repeat the same checks. Do not disable remote-cert-tls, verify-x509-name, CA validation, or revocation.

Why does OpenVPN print TLS Error: TLS handshake failed after a certificate error?

TLS Error: TLS handshake failed is a final summary. The causal line is often an earlier VERIFY ERROR or peer certificate verification failure. If no certificate error precedes it, compare the endpoint, protocol, firewall path, TLS minimum, and tls-crypt or tls-auth material on both peers.

Can I remove remote-cert-tls or certificate verification to make OpenVPN connect?

No. That changes an identity failure into an insecure connection and can allow the wrong peer to impersonate the VPN server or client. Repair the CA, certificate purpose, expected identity, validity, revocation state, or control-channel configuration instead.

How do I fix OpenVPN Cannot resolve host address?

Inspect the active profile's remote name and protocol, then test lookup from the actual client network before the tunnel exists. Repair the demonstrated record, approved resolver path or startup context. A certificate reissue does not repair endpoint DNS.

Why does resolv-retry infinite keep OpenVPN waiting?

It retries a failing name lookup; it cannot repair the underlying DNS or bootstrap dependency. Retain the first RESOLVE error and alert on a connection that never reaches the next layer.

Can pushed VPN DNS resolve the VPN endpoint itself?

Not when the client needs that same tunnel before it can reach the resolver. The network owner must provide an approved pre-tunnel discovery path without weakening server authentication.

Recovery

Rollback

Disable the VPN and restore the previous network policy.

  1. Stop and disable openvpn-server@server.
  2. Restore, validate, and reload the saved firewall configuration.
  3. Remove the forwarding sysctl file and run sudo sysctl --system.
  4. Retain the PKI securely until every issued profile is retired.

Evidence

Sources and review

Verified 2026-09-09Review due 2026-12-09
Ubuntu: install OpenVPNofficialOpenVPN 2.6 manualofficialEasy-RSA documentationofficialnftables NAT documentationofficialOpenVPN connection troubleshootingofficialLinux getaddrinfo transient lookup errorsofficialUbuntu resolvectl per-link resolver diagnosticsofficial