OpenVPN Server Hardening – OpenWRT TUN device

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 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


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.

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.


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, or as these nets are often seen to be used for default LAN ranges in home routers.


My assumption was for this tutorial a /29 net. For example any CIDR Net from a private “Class B” Address Range in between– 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:  
Subnet Mask: 
CIDR Address Range: - 
Useable Adresses:   6 (1 Server + 5 Clients) 
Clients:   -


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:  
Subnet Mask: 
CIDR Address Range: -
Useable Adresses:   2 (1 Server + 1 Client) 

OpenWRT OpenVPN Settings

OpenVPN config for a TLS based and hardened Setup in /etc/config/openvpn
config openvpn 'cyber'
        option enabled '1'
        option dev_type 'tun'
        option dev 'cyber_tun0'
        option topology 'subnet'
        option proto 'udp'
        option port '1194'

        option server ''
        option ifconfig ''
        list push 'route'

#Client Config
        option ccd_exclusive '1'
        option client_config_dir '/etc/openvpn/ccd/'
        option max_clients '5'
        option client_to_client '1' 

        option ca '/etc/ssl/certs/'
        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 reneg_sec '1800'
        option reneg_bytes '64000000'
        option remote_cert_tls 'client'
#        option verify_client_cert '1'

        option log_append '/var/log/openvpn/openvpn.log'
        option status '/var/log/openvpn-status.log'
        option mute '5'
        option verb '4'

        option keepalive '10 60'
        option compress 'lzo'
        option script_security '1'

#Connection Reliability
        option persist_key '1'
        option persist_tun '1'

       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'


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". 


option server ''
--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 ''
--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'
--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'
  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' 
  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. 


option ca '/etc/ssl/certs/'
--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.  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'
  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".
  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
--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.
  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. 


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. 


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'
  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'
  Don't close and reopen TUN/TAP device or run up/down scripts across SIGUSR1 or --ping-restart restarts. 


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. 

Comments are closed.