This is a tutorial for a hardened OpenVPN Server with all important settings in a paranoid TLS based setup. I have read through different other tutorials, more or less good tutorials, but most of them lacked serious settings or had just set them wrong. So i decided to read through the complete ManPage from OpenVPN to find all the important settings by myself.
This Tutorial covers OpenVPN Version v2.4 (with compatibility to v2.3.3 clients). The next OpenVPN will cover the Client Settings for different scenarios.
Network Topology

My Home Router is on the LAN Network 192.168.68.0/24 and i want to access it from other devices from different networks.
Firewall Rule
First we need to make the OpenVPN available on the WAN Port. For this we add a Firewall Rule to /etc/config/firewall.

config 'rule'
option 'name' 'openvpn-udp'
option 'src' 'wan'
option 'target' 'ACCEPT'
option 'proto' 'udp'
option 'dest_port' '1194'
Generation of Certificates
Import From Future Blog Post to create your own Certificate Authority with a Root CA, different Intermediate CA’s, and Server + Client Certificates.
For this tutorial we need following Certificate and Key files:
- RootCA (CA)
- Intermediate CA (ICA)
- Server Certificate
- Server Cert & Key (+ Client Cert & Keys for Testing)
- CRL File
- TLS-Auth Key
- Diffie Hellman parameters
Keysize
Regarding an ENISA – Algorithms, Key Sizes and Parameters Report keys specified to be at least ten years in use, RSA keys of 3072 bits or more are recommended.
Creation of 4096 bit RSA keys is recommended by me.
https://community.openvpn.net/openvpn/wiki/Hardening#X.509keysize
CA-Chain File
The RootCA and the ICA Certificates should be bundled into a ca-chain.cert file.
cat RootCa.pem IntermediateCA.pem > ca-chain.pem
TLS-Auth Key
Generate TLS-Auth key
openvpn --genkey --secret openvpn/tls-auth.key
This key is available on all devices and should be kept secret.
DH Parameters
Generating DH keys takes substantial amounts of time.
openssl dhparam -out dhparam4096.pem 4096
The DH Parameters should exceed your Server Certificate size.
- Server Certificate 2048 bit => DH 4096 bit
- Server Certificate 4096 bit => DH 8192 bit
Your Server Certificate should have at least 4096 bit in size.
Subnet
The VPN will create a subnet. You should choose a Net which will not overlap with any other Subnet you will possibly encounter. Stay away from 192.168.0.0/24, 192.168.0.0/24 or 10.0.0.0/24 as these nets are often seen to be used for default LAN ranges in home routers.
Multiclient
My assumption was for this tutorial a /29 net. For example any CIDR Net from a private “Class B” Address Range in between 172.16.0.0–172.31.255.255 can be choosen. Class B has not been used that often, at least it seems to me. Just try to pick a “random” net private, which has a rare chance to be unused by others.
CIDR Net Notation: 172.16.10.0/29
Subnet Mask: 255.255.255.248
Broadcast: 172.16.10.7
CIDR Address Range: 172.16.10.0 - 172.16.10.7
Useable Adresses: 6 (1 Server + 5 Clients)
Server: 172.16.10.1
Clients: 172.16.10.2 - 172.16.10.6
Point-To-Point
If the VPN is only planned for a Point-To-Point connection between two Routers or for a single Client, a /30 Net should be chosen instead. It is still a MultiClient Net, but with only two Points.
CIDR Net Notation: 172.18.25.64/30
Subnet Mask: 255.255.255.252
Broadcast: 172.18.25.67
CIDR Address Range: 172.18.25.64 - 172.18.25.67
Useable Adresses: 2 (1 Server + 1 Client)
Server: 172.18.25.65
Client: 172.18.25.66

OpenWRT OpenVPN Settings
OpenVPN config for a TLS based and hardened Setup in /etc/config/openvpn
config openvpn 'cyber'
option enabled '1'
#Protocol
option dev_type 'tun'
option dev 'cyber_tun0'
option topology 'subnet'
option proto 'udp'
option port '1194'
#Routes
option server '172.16.10.0 255.255.255.248'
option ifconfig '172.16.10.1 255.255.255.248'
list push 'route 192.168.100.0 255.255.255.0'
#Client Config
option ccd_exclusive '1'
option client_config_dir '/etc/openvpn/ccd/'
option max_clients '5'
option client_to_client '1'
#Encryption
option ca '/etc/ssl/certs/vpn.cavebeat.lan.ca-chain.cert.pem'
option cert '/etc/ssl/certs/ptree.vpn.cavebeat.lan.cert.pem'
option key '/etc/ssl/private/ptree.vpn.cavebeat.lan.key.pem'
option dh '/etc/ssl/dh4096.pem'
option tls_crypt '/etc/ssl/tls-auth.key'
option cipher 'AES-256-CBC'
option ncp_ciphers 'AES-256-GCM:AES-128-GCM:AES-256-CBC:AES-128-CBC'
option auth 'SHA512'
option tls_server '1'
option tls_version_min '1.2'
option tls_cipher 'TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-AES-128-CBC-SHA256'
option reneg_sec '1800'
option reneg_bytes '64000000'
option remote_cert_tls 'client'
# option verify_client_cert '1'
#Logging
option log_append '/var/log/openvpn/openvpn.log'
option status '/var/log/openvpn-status.log'
option mute '5'
option verb '4'
#Connection
option keepalive '10 60'
option compress 'lzo'
option script_security '1'
#Connection Reliability
option persist_key '1'
option persist_tun '1'
#Permissions
option user 'nobody'
option group 'nogroup'
The parameter verify_client_cert / –verify-client-cert is new in OpenVPN 2.4 and is replacing the deprecated parameter –client-cert-not-required. For more information on deprecated parameters check Deprecated OpenVPN Settings
The default value should require to verify the client cert given to the server. I have placed a PullRequest with a change for OpenWRT to add this setting.
Description of used Settings and Parameters
option enabled '1'
Protocol
option dev_type 'tun'
--dev-type device-type
Which device type are we using? device-type should be tun (OSI Layer 3) or tap (OSI Layer 2).
Use this option only if the TUN/TAP device used with --dev does not begin with tun or tap.
option dev 'tun1'
--dev tunX | tapX | null
tun devices encapsulate IPv4 or IPv6 (OSI Layer 3) while tap devices encapsulate Ethernet 802.3 (OSI Layer 2).
option topology 'subnet'
--topology mode
Configure virtual addressing topology when running in --dev tun mode.
subnet -- Use a subnet rather than a point-to-point topology by configuring the tun interface with a local IP address and subnet mask, similar to the topology used in --dev tap and ethernet bridging mode.
This mode allocates a single IP address per connecting client and works on Windows as well.
Note: Using --topology subnet changes the interpretation of the arguments of --ifconfig to mean "address netmask", no longer "local remote".
Routes
option server '172.16.10.0 255.255.255.248'
--server network netmask ['nopool']
A helper directive designed to simplify the configuration of OpenVPN's server mode.
This directive will set up an OpenVPN server which will allocate addresses to clients out of the given network/netmask.
The server itself will take the ".1" address of the given network for use as the server-side endpoint of the local TUN/TAP interface.
option ifconfig '172.16.10.1 255.255.255.248'
--ifconfig l rn
Set TUN/TAP adapter parameters. l is the IP address of the local VPN endpoint.
For TAP devices, or TUN devices used with --topology subnet,rn is the subnet mask of the virtual network segment which is being created or connected to.
For TAP devices, which provide the ability to create virtual ethernet segments, or TUN devices in --topology subnet mode (which create virtual "multipoint networks"), --ifconfig is used to set an IP address and subnet mask just as a physical ethernet adapter would be similarly configured.
list push 'route 192.168.100.0 255.255.255.0'
--push option
Push a config file option back to the client for remote execution.
Note that option must be enclosed in double quotes ("").
Client Config
option client_config_dir '/etc/openvpn/ccd/'
--client-config-dir dir
Specify a directory dir for custom client config files.
After a connecting client has been authenticated, OpenVPN will look in this directory for a file having the same name as the client's X509 common name.
If a matching file exists, it will be opened and parsed for client-specific configuration options.
If no matching file is found, OpenVPN will instead try to open and parse a default file called "DEFAULT", which may be provided but is not required.
Note that the configuration files must be readable by the OpenVPN process after it has dropped it's root privileges.
option ccd_exclusive '1'
--ccd-exclusive
Require, as a condition of authentication, that a connecting client has a --client-config-dir file.
option max_clients '5'
--max-clients n
Limit server to a maximum of n concurrent clients.
option client_to_client '0'
--client-to-client
Because the OpenVPN server mode handles multiple clients through a single tun or tap interface, it is effectively a router.
The --client-to-client flag tells OpenVPN to internally route client-to-client traffic rather than pushing all client-originating traffic to the TUN/TAP interface.
Encryption
option ca '/etc/ssl/certs/vpn.cavebeat.lan.ca-chain.cert.pem'
--ca file
Certificate authority (CA) file in .pem format, also referred to as the root certificate.
option cert '/etc/ssl/certs/ptree.vpn.cavebeat.lan.cert.pem'
--cert file
Local peer's signed certificate in .pem format -- must be signed by a certificate authority whose certificate is in --ca file.
Each peer in an OpenVPN link running in TLS mode should have its own certificate and private key file.
In addition, each certificate should have been signed by the key of a certificate authority whose public key resides in the --ca certificate authority file.
option key '/etc/ssl/private/ptree.vpn.cavebeat.lan.key.pem'
--key file
Local peer's private key in .pem format.
option dh '/etc/ssl/dh4096.pem'
--dh file
File containing Diffie Hellman parameters in .pem format (required for --tls-server only).
option tls_crypt '/etc/ssl/tls-auth.key'
--tls-auth file [direction]
Add an additional layer of HMAC authentication on top of the TLS control channel to mitigate DoS attacks and attacks on the TLS stack.
In a nutshell, --tls-auth enables a kind of "HMAC firewall" on OpenVPN's TCP/UDP port, where TLS control channel packets bearing an incorrect HMAC signature can be dropped immediately without response.
file (required) is a file in OpenVPN static key format which can be generated by --genkey
Use --tls-crypt instead if you want to use the key file to not only authenticate, but also encrypt the TLS control channel.
--tls-crypt keyfile
Encrypt and authenticate all control channel packets with the key from keyfile. (See --tls-auth for more background.)
Encrypting (and authenticating) control channel packets:
+ provides more privacy by hiding the certificate used for the TLS connection,
+ makes it harder to identify OpenVPN traffic as such,
+ provides "poor-man's" post-quantum security, against attackers who will never know the pre-shared key (i.e. no forward secrecy).
In contrast to --tls-auth, --tls-crypt does *not* require the user to set --key-direction.
option cipher 'AES-256-CBC'
--cipher alg
Encrypt data channel packets with cipher algorithm alg.
https://community.openvpn.net/openvpn/wiki/SWEET32#a1.Changetoalargerblockcipher Of the currently supported ciphers, OpenVPN currently recommends using AES-256-CBC or AES-128-CBC.
OpenVPN 2.4 and newer will also support GCM.
For 2.4+, we recommend using AES-256-GCM or AES-128-GCM.
option ncp-ciphers 'AES-256-GCM:AES-128-GCM:AES-256-CBC:AES-128-CBC'
--ncp-ciphers cipher_list
Restrict the allowed ciphers to be negotiated to the ciphers in cipher_list.
cipher_list is a colon-separated list of ciphers, and defaults to "AES-256-GCM:AES-128-GCM".
For servers, the first cipher from cipher_list will be pushed to clients that support cipher negotiation.
If both peers support and do not disable NCP, the negotiated cipher will override the cipher specified by --cipher.
option auth 'SHA512'
--auth alg
Authenticate data channel packets and (if enabled) tls-auth control channel packets with HMAC using message digest algorithm alg.
If an AEAD cipher mode (e.g. GCM) is chosen, the specified --auth algorithm is ignored for the data channel, and the authentication method of the AEAD cipher is used instead.
Note that alg still specifies the digest used for tls-auth.
option tls_server '1'
--tls-server
Enable TLS and assume server role during TLS handshake.
option tls_version_min '1.2'
--tls-version-min version ['or-highest']
Sets the minimum TLS version we will accept from the peer (default is "1.0").
Examples for version include "1.0", "1.1", or "1.2".
https://community.openvpn.net/openvpn/wiki/Hardening#Useof--tls-version-min
Since OpenVPN 2.3.3, the --tls-version-min option is available to enforce a minimum TLS version.
Hardened setups should set --tls-version-min to 1.2 if possible.
But be aware that setting tls-version-min to 1.2 will make it impossible to connect for pre-2.3.3 clients
option tls_cipher 'TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-AES-128-CBC-SHA256'
--tls-cipher l
A list l of allowable TLS ciphers delimited by a colon (":").
This setting can be used to ensure that certain cipher suites are used (or not used) for the TLS connection.
You should use a DHE cipher-suite as well for forward-secrecy.
https://community.openvpn.net/openvpn/wiki/Hardening#Useof--tls-cipher
To use ECDH(E) or ECDSA cipher-suites, both client and server must be OpenVPN 2.4.0 or newer.
option reneg_sec '3600'
--reneg-sec n
Renegotiate data channel key after n seconds (default=3600).
option reneg_bytes '64000000'
--reneg-bytes n
Renegotiate data channel key after n bytes sent or received.
option remote_cert_tls 'server'
--remote-cert-tls client|server
Require that peer certificate was signed with an explicit key usage and extended key usage based on RFC3280 TLS rules.
option verify_client_cert '1'
--verify-client-cert none|optional|require
Specify whether the client is required to supply a valid certificate.
require : this is the default option. A client is required to present a certificate, otherwise VPN access is refused.
option crl_verify ''etc/ssl/crl.pem'
--crl-verify crl ['dir']
Check peer certificate against the file crl in PEM format.
A CRL (certificate revocation list) is used when a particular key is compromised but when the overall PKI is still intact.
Note: As the crl file (or directory) is read every time a peer connects, if you are dropping root privileges with --user, make sure that this user has sufficient privileges to read the file.
Logging
option log_append '/var/log/openvpn/openvpn.log'
--log-append file
Append logging messages to file. If file does not exist, it will be created.
This option behaves exactly like --log except that it appends to rather than truncating the log file.
--log file
Output logging messages to file, including output to stdout/stderr which is generated by called scripts.
If file already exists it will be truncated.
option status '/var/log/openvpn-status.log'
--status file [n]
Write operational status to file every n seconds.
option mute '4'
--mute n
Log at most n consecutive messages in the same category.
This is useful to limit repetitive logging of similar message types.
option verb '4'
--verb n
Set output verbosity to n (default=1).
Each level shows all info from the previous levels.
Level 3 is recommended if you want a good summary of what's happening without being swamped by output.
- 0 -- No output except fatal errors.
- 1 to 4 -- Normal usage range.
Connection
option keepalive '10 60'
--keepalive interval timeout
A helper directive designed to simplify the expression of --ping and --ping-restart.
This option can be used on both client and server side, but it is in enough to add this on the server side as it will push appropriate --ping and --ping-restart options to the client.
--ping n
Ping remote over the TCP/UDP control channel if no packets have been sent for at least n seconds
--ping-restart n
Restart after n seconds pass without reception of a ping or other packet from remote.
In server mode, --ping-restart, --inactive, or any other type of internally generated signal will always be applied to individual client instance objects, never to whole server itself.
Note also in server mode that any internally generated signal which would normally cause a restart, will cause the deletion of the client instance object instead.
option compress 'lzo'
--compress [algorithm] Enable a compression algorithm.
The algorithm parameter may be "lzo", "lz4", or empty.
LZO and LZ4 are different compression algorithms, with LZ4 generally offering the best performance with least CPU usage.
For backwards compatibility with OpenVPN versions before v2.4, use "lzo" (which is identical to the older option "--comp-lzo yes").
option script_security '1'
--script-security level
This directive offers policy-level control over OpenVPN's usage of external programs and scripts.
Lower level values are more restrictive, higher values are more permissive.
Settings for level:
0 -- Strictly no calling of external programs.
1 -- (Default) Only call built-in executables such as ifconfig, ip, route, or netsh.
2 -- Allow calling of built-in executables and user-defined scripts.
3 -- Allow passwords to be passed to scripts via environmental variables (potentially unsafe).
Connection Reliability
option persist_key '1'
--persist-key
Don't re-read key files across SIGUSR1 or --ping-restart.
This option can be combined with --user nobody to allow restarts triggered by the SIGUSR1 signal.
Normally if you drop root privileges in OpenVPN, the daemon cannot be restarted since it will now be unable to re-read protected key files.
option persist_tun '1'
--persist-tun
Don't close and reopen TUN/TAP device or run up/down scripts across SIGUSR1 or --ping-restart restarts.
Permissions
option user 'nobody'
--user user
Change the user ID of the OpenVPN process to user after initialization, dropping privileges in the process.
option group 'nogroup'
--group group
Similar to the --user option, this option changes the group ID of the OpenVPN process to group after initialization.