OpenVPN Server Configuration¶
Part of the OpenVPN Comprehensive Guide.
Assumes OpenVPN and EasyRSA are already installed — see
openvpn-installation.md. Hardening options
referenced here (tls-crypt, cipher choice, CRL) are detailed in
openvpn-security-hardening.md rather than
repeated in full below.
1. Build the PKI with EasyRSA¶
This replaces the old manual openssl req / openssl ca workflow. EasyRSA
handles the CA, serials, and index bookkeeping for you.
cd /etc/openvpn
git clone https://github.com/OpenVPN/easy-rsa.git
cd easy-rsa/easyrsa3
./easyrsa init-pki
./easyrsa build-ca nopass # creates pki/ca.crt + pki/private/ca.key
./easyrsa gen-req vpn.example.net nopass # server cert request
./easyrsa sign-req server vpn.example.net # CA signs it
./easyrsa gen-crl # initial (empty) CRL
Keep pki/private/ca.key offline or on a system that isn't the VPN server
itself if at all practical — anyone who gets it can mint valid client certs.
DH parameters (or skip them with ECDHE)¶
The classic setup uses a Diffie-Hellman params file:
./easyrsa gen-dh # writes pki/dh.pem — this is slow, expect a few minutes
If your server cert uses ECDSA instead of RSA, you can skip dh.pem
entirely and let OpenVPN negotiate ECDHE, which is faster to set up and to
handshake. RSA certs (the EasyRSA default) still need dh.pem.
Control-channel key¶
Generate a tls-crypt key (preferred over the older tls-auth; see
openvpn-security-hardening.md for why):
openvpn --genkey secret /etc/openvpn/tls-crypt.key
This key gets copied to every client — it's shared, not client-specific.
2. Server config file¶
# /etc/openvpn/server/server.conf
local 192.168.0.250
port 1194
proto udp
dev tun
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
client-config-dir ccd
# Route(s) pushed to every client
push "route 192.168.13.0 255.255.255.0"
keepalive 10 120
ca /etc/openvpn/easy-rsa/easyrsa3/pki/ca.crt
cert /etc/openvpn/easy-rsa/easyrsa3/pki/issued/vpn.example.net.crt
key /etc/openvpn/easy-rsa/easyrsa3/pki/private/vpn.example.net.key
dh /etc/openvpn/easy-rsa/easyrsa3/pki/dh.pem # omit if using ECDSA/ECDHE
crl-verify /etc/openvpn/easy-rsa/easyrsa3/pki/crl.pem
tls-crypt /etc/openvpn/tls-crypt.key
data-ciphers AES-256-GCM
tls-version-min 1.2
user openvpn
group openvpn
persist-key
persist-tun
status /var/log/openvpn/openvpn-status.log
log-append /var/log/openvpn/openvpn.log
verb 3
What changed from the old config and why:
comp-lzoremoved — see the compression note in
openvpn-security-hardening.md.cipher AES-256-CBC/auth SHA256replaced withdata-ciphers AES-256-GCM, an AEAD cipher (single-pass authenticated encryption, no
separate HMAC directive needed).tls-auth ... 0replaced withtls-crypt(no direction key parameter
needed —tls-cryptis symmetric between client and server by design).user/group openvpnandcrl-verifymoved from being an optional
hardening add-on to on-by-default — there's no good reason to run the
daemon as root once it's up, or to skip revocation checking.- Explicit log paths under
/var/log/openvpn/instead of relative paths in
/etc/openvpn— keeps config and logs separated and avoids permission
headaches once the process drops to theopenvpnuser.
Start it with your init system rather than symlinking config files by hand
(most packaged installs already wire up openvpn-server@<name>.service for
/etc/openvpn/server/<name>.conf):
sudo systemctl enable --now [email protected]
3. Static IP assignment (per-client)¶
The original notes flagged this as broken/unclear ("CCD or ipp.txt?? Neither
appears to be working"). Both mechanisms are real, but they do different
things — that's the likely source of the confusion:
ipp.txt(set viaifconfig-pool-persist ipp.txt) is the server
remembering which pooled address a client got last time, so reconnects
tend to land on the same IP. It's a cache, not a guarantee, and you don't
edit it by hand in normal operation.client-config-dir(ccd) is how you force a specific IP (or push
extra per-client routes) for a given client, keyed by the client
certificate's CN. This is the mechanism to use for "this client always
gets this address."
mkdir -p /etc/openvpn/server/ccd
# /etc/openvpn/server/ccd/amercer-pc1 (filename == client cert CN, exact match)
ifconfig-push 10.8.0.4 255.255.255.0
Make sure client-config-dir ccd in server.conf points at this directory,
and that the filename matches the client's certificate CN exactly — a
mismatch here (trailing space, wrong case) is the most common reason this
silently doesn't apply.
4. Static routes¶
Already shown above via push "route ..." in server.conf — that's the
straightforward way to add a route for all clients. The old notes also
mention a separate route.txt; that's not a standard OpenVPN mechanism, and
the push "route ..." line in the main config is sufficient for this case.
For a route that should apply to only one client, put it in that client's
ccd file instead:
# /etc/openvpn/server/ccd/amercer-pc1
push "route 192.168.20.0 255.255.255.0"
Next: openvpn-client-configuration.md to
issue a client certificate and build a client config.