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.
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.
- Ubuntu Server 24.04 LTS
- OpenVPN 2.6.x
- Easy-RSA 3.x
- 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
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
- 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.
- 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.
- The client resolves the stable VPN hostname and sends a tls-crypt-protected OpenVPN control packet to the selected UDP port.
- The client verifies that the server certificate has TLS Web Server Authentication usage; the server requires a certificate with TLS Web Client Authentication usage.
- OpenVPN negotiates a mutually supported AEAD data cipher and assigns the client an address from the 10.8.0.0/24 example pool.
- The server pushes full-tunnel routes and a DNS resolver. Linux forwards accepted packets from tun0 to the public interface.
- 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.
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.
command
Diagnose Cannot resolve host address before rebuilding the VPN
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.
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.confIllustrative 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.
decision
Separate endpoint records, resolver selection and startup readiness
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.
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.
verification
Verify endpoint resolution, then the real OpenVPN handshake
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.
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 100Illustrative 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.
command
Install the supported packages
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.
sudo apt update && sudo apt install --yes openvpn easy-rsa nftables && openvpn --version | head -n 1 && nft --versionOpenVPN 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 --versionContinue 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.
cat /etc/os-releaseapt-cache policy openvpn easy-rsa nftablesgrep -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.
command
Create the CA and server identity
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.
make-cadir ~/openvpn-ca && cd ~/openvpn-ca && ./easyrsa init-pki && ./easyrsa build-ca && ./easyrsa build-server-full server nopassCertificate 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 -datesContinue 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.
cd ~/openvpn-ca && ./easyrsa show-cafind ~/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.
command
Generate and install server cryptographic material
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.
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/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 -nextupdateContinue 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.
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.
config
Write the routed server configuration
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.
/etc/openvpn/server/server.confValues 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 3Configuration 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.pemContinue 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.
openvpn --version | head -n 1sudo journalctl -u openvpn-server@server -b --no-pager -n 100sudo 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.
config
Enable forwarding deliberately
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.
/etc/sysctl.d/99-openvpn-forward.confnet.ipv4.ip_forward = 1sudo sysctl --system && sysctl net.ipv4.ip_forward* 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.confContinue 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.
grep -R --line-number 'net.ipv4.ip_forward' /etc/sysctl.conf /etc/sysctl.d /usr/lib/sysctl.djournalctl -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.
warning
Validate and activate the dedicated gateway firewall
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.
/etc/nftables.conf.onelinersValues 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
}
}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 nftablesnftables 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.
grep -n '{{\|}}' /etc/nftables.conf.onelinersip -brief linksudo 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.
ip -brief addressss -lntpsudo 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.
command
Start and inspect the VPN
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.
sudo systemctl enable --now openvpn-server@server && systemctl --no-pager --full status openvpn-server@serverActive: 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 30Continue 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.
ls -l /dev/net/tunsudo modprobe tunsystemd-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.
verification
Classify VERIFY ERROR and TLS handshake failed before changing anything
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.
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 -nextupdateSystem 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.
date -u; timedatectl statusopenssl x509 -in /etc/openvpn/server/server.crt -noout -subject -issuer -serial -datesopenssl 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 detailspeer 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.
openssl verify -purpose sslserver -CAfile /etc/openvpn/server/ca.crt /etc/openvpn/server/server.crtopenssl x509 -in /etc/openvpn/server/server.crt -noout -textgrep -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 logsTLS 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.
sudo ss -lunp | grep ':{{vpnPort}}'sudo tcpdump -ni any 'port {{vpnPort}}' -c 30grep -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 trafficSecurity 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.
command
Issue a unique client certificate and restricted profile
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.
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"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}}.ovpnContinue 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.
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.
verification
Test from an external network
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.
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 | headactive 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 | headContinue 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.
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.
instruction
Exercise certificate revocation
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.
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@serverRevoking 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@serverContinue 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.
grep -F '/CN={{clientName}}' ~/openvpn-ca/pki/index.txtopenssl x509 -in ~/openvpn-ca/pki/issued/{{clientName}}.crt -noout -issuer -serialopenssl 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
systemctl is-active openvpn-server@serverReturns active.ss -lunp | grep ':1194'Shows OpenVPN on the intended UDP address.ip 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.
sudo journalctl -u openvpn-server@server -b --no-pager -n 100sudo namei -l /etc/openvpn/server/server.key /etc/openvpn/server/crl.pemsudo 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.
sysctl net.ipv4.ip_forwardsudo nft list rulesetip route get 1.1.1.1sudo 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.
openssl crl -in /etc/openvpn/server/crl.pem -noout -lastupdate -nextupdateopenssl x509 -in pki/issued/{{clientName}}.crt -noout -serialsudo 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.
date -u; timedatectl statusopenssl x509 -in /etc/openvpn/server/server.crt -noout -subject -issuer -serial -datesopenssl 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 detailspeer 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.
openssl verify -purpose sslserver -CAfile /etc/openvpn/server/ca.crt /etc/openvpn/server/server.crtopenssl x509 -in /etc/openvpn/server/server.crt -noout -textgrep -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 logsTLS 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.
sudo ss -lunp | grep ':{{vpnPort}}'sudo tcpdump -ni any 'port {{vpnPort}}' -c 30grep -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 trafficRESOLVE: 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.
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.
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.
- Stop and disable openvpn-server@server.
- Restore, validate, and reload the saved firewall configuration.
- Remove the forwarding sysctl file and run sudo sysctl --system.
- Retain the PKI securely until every issued profile is retired.
Evidence