This post is part of my Explaining My Configs series where I explain the configuration files (and options) I use in detail.
This post could either be read as a whole, or as a reference (click on a line to jump to its explanation).
Usually I prefer explaining the client and the server configurations in separate posts, however, with OpenVPN, there is significant overlap between the two, and they need to match in order for it to work. Therefore, this post will explain both.
I have my configuration files in the system's OpenVpn directory (/etc/openvpn/server
for me), and foreach configuration file, I have a directory with its keys, so for example, thisis the layout for the server's config:
server.confserver/dh.pemserver/ippserver/ta.keyserver/key.keyserver/cert.crtserver/ca.crt
I use my VPN to have remote access into my network, and sometimes also to route all traffic through it, in order to escape some forms of connection filtering. While my base configuration is hardened (strong encryption and secure settings), my route-all-traffic configuration is not. That requires some settings that are annoying to use and setting up a firewall to block mistakes.
Click on a line to jump to its explanation.
/etc/openvpn/server.conf:
server 192.168.87.0 255.255.255.0port 1194proto udp6dev tun0client-to-clientcipher AES-256-CBCauth SHA512tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-CBC-SHA256comp-lzo nokeepalive 15 60ping-timer-remifconfig-pool-persist server/ippverb 3persist-tunpersist-key# Drop privsuser nobodygroup nobody# Keystls-auth server/ta.key 0cert server/cert.crtkey server/key.keyca server/ca.crtdh server/dh.pem
/etc/openvpn/client.conf:
clientremote vpn.stosb.com 1194 udpdev tun# Uncomment the next line to redirect all traffic through the VPN# redirect-gateway def1remote-cert-tls servercipher AES-256-CBCauth SHA512tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-CBC-SHA256comp-lzo noverb 3persist-tunpersist-key# Keyskey-direction 1tls-auth client/ta.keycert client/cert.crtkey client/key.key<ca>-----BEGIN CERTIFICATE-----... SNIP ...-----END CERTIFICATE-----</ca>
server 192.168.87.0 255.255.255.0
The server
puts OpenVPN in server mode, and supplies it with a subnet of IPs toallocate by specifying an address and a netmask. In the example above, OpenVPNwill take 192.186.87.1
for itself, and allocate the rest of the subnet forclients.
Choose a subnet that's unlikely to create clashes with your other networks.
port 1194
This directive sets which port the server should listen on. I chose the standardOpenVPN port.
proto udp6
This sets the transport protocol to use. UDP is more efficient and will performmuch better while using less data. However, if for some reason you can't useUDP, you can set this to TCP instead.
On the server I set it to udp6
which tells it to listen to both IPv4 and IPv6connections.
On the client I set it to udp
, because udp6
will force it to only try IPv6,and made OpenVPN not work for me (I don't always have IPv6).
dev tun0
Sets the name of the virtual network device to use. On the server, I forced itto tun0
, so I can more easily set firewall rules knowing it'll always be thesame device. On the client I let it choose the exact device on its own.
Since the device name starts with tun
, OpenVPN automatically sets the devicetype to it. Otherwise I would have had to set dev-type
explicitly.
client-to-client
Normally, OpenVPN would pass all packets to the tun device on the server. Thismeans that all packets, even between clients in the VPN network will be handledby the server's firewall, so if you want client to client traffic, you need toexplicitly enable it in the firewall and add all the rules to do it.
Alternatively, you can set this directive that automatically does all of thatfor you. Be advised that with this enabled, the server's firewall will neverget the packets, so don't enable it if you want fine-grained control.
cipher AES-256-CBCauth SHA512tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-CBC-SHA256
These statements harden the server with stronger crypto. However, some of themare only available from OpenVpn 2.3.3. If you are having trouble, choosedifferent values.
The cipher
directive controls which cipher would be used for the data channel,that is, all the data transferred through the VPN.
The auth
directive controls the HMAC algorithm used for the control channel.More on that in the keys section.
The tls-cipher
directive controls the cipher suite used by the VPNs controlchannel.
comp-lzo no
Disable compression. Most, if not all, of my traffic is either encrypted (e.g.SSH and HTTPS) or already compressed (e.g. HTTP in most cases), so addingcompression on top will not add any benefits. If you think you may wantcompression after all, set it to adaptive
which automatically decides if toput compression on or not, though even when not compressing, this is not free.
keepalive 15 60
This is a helper directive that automatically sets the ping
and ping-restart
values on both the client and the server (when set on the server). This is whywe don't have it in the client.
With this setting, a ping will be sent every 15 seconds (if no other data hasbeen sent). This is useful to keep stateful firewalls will not drop or UDPconnection after some time of inactivity.
The second part of this directive will try to restart the connection after 60seconds of inactivity (on the client) and 60 * 2 = 120 seconds(on the server). This ensures that the client will detect the timeout before theserver.
Note: be aware, that this setting can cause increased battery usage on mobiledevices due to the radio being woken up to send data all the time.
ping-timer-rem
This only runs the ping-restart
(previous) timeout timer when there is a clientconnected to the server, so to prevent the server from timing out all the timewhen there isn't even a client connected.
ifconfig-pool-persist server/ipp
This makes the IP addresses given to clients persistent, and the persistencefile to be saved in the location server/ipp
relative to the main config file.
verb 3
Increase the verbosity of OpenVPN. The default is 1, but 3 is the recommendedvalue. Shows a bit more information in the logs.
persist-tunpersist-key
These options persist the tun device and the authentication keys across restarts(either caused by user or ping-restarts). This makes it possible to dropprivileges (see the next section) and regardless, I couldn't think of a goodreason why not to have these.
# Drop privsuser nobodygroup nobody
This drop the privileges of the daemon to the user and group nobody
, so ifthere's ever a bug in OpenVPN, at least the hacker won't get root privileges.You could further harden it as explained on the OpenVPN website,however, it was too much of a hassle for not a lot of benefit.
Because of the lower privileges, the server can no longer clean up after itselfby for example closing the tun device, or removing routes. It's a non-issue fora server config because OpenVPN should never be stopped, but it is for a client,and that's why I only have this setting on the client machine.
# Keystls-auth server/ta.key 0cert server/cert.crtkey server/key.keyca server/ca.crtdh server/dh.pem
These tell OpenVPN to look for the keys (and dh params) in the noted locations.Please note that the number at the end of tls-auth
is the key-direction
, and needs to be 0 for server and 1 for client.
You can also embed the keys/certificates in the file itself if you prefer (demonstrated in the next section), and if you do it for the tls-auth
directive too, you'd need to specify the key-direction
separately using the key-direction
directive.
Please look online to see how to manage your own PKI for OpenVPN, this is beyond the scope of this post. However, one note on tls-auth
as promised earlier. This is the key used for HMAC. While this is not essential, it adds another layer of security by adding an easy to verify authentication code to all the control messages. This protects against some kinds of DoS and could even protect against issues with TLS, like for example, the recently notorious heartbleed
.
<ca>-----BEGIN CERTIFICATE-----... SNIP ...-----END CERTIFICATE-----</ca>
I redacted my actual certificate for brevity, but this is how you embed a key or certificate in the config file. I found it very useful when providing the OpenVPN Android client with a config. When I did that, I embedded all of the keys.
client
This is a helper indicating that this is a client. This means that the client assumes the position of "client" in the TLS negotiation. It also sets the pull
directive, which allows the server to push some (white-listed) configurations to the client like setting the IP address and routing.
remote vpn.stosb.com 1194 udp
This tells OpenVPN where to connect to. In this example it's connecting to vpn.stosb.com
and port 1194
using UDP, which is what we have set on the server.
You can put multiple remote directives for redundancy. For example, you could have OpenVPN running on a list of ports you expect to be white-listed in networks, like udp:53 (DNS) or tcp:993 (POP3) and then have OpenVPN automatically try them. Alternatively you can use it for real redundancy, so if oneserver fails, the others are automatically tried.
# Uncomment the next line to redirect all traffic through the VPN# redirect-gateway def1
This directive redirects all of the client's traffic through the VPN by adding the needed routing rules. This is the bare minimum that's needed, but you could also append other flags to this command, for example block-local
to block traffic to the local (non-VPN) LAN in order to make sure no traffic leaks. This is another level of hardening, but as I said before, it's better handled by proper firewall rules.
One thing to remember when using block-local
, is that in many cases your DNS server would be in the local network and with block-local
it will no longer be reachable. To solve it you should either set an alternative DNS in the client, by using for example dhcp-option DNS 8.8.8.8
, or pushing it from the server.
remote-cert-tls server
This forces the certificate of the other end to be a server certificate. Before explaining what it does, I need to explain a bit about PKI and OpenVPN. When a client connects to the server using TLS, it checks to see if the certificate of the server is signed by the certificate authority and not revoked, and if everything is OK, it allows the connection. This sounds good, but because of how OpenVPN works client certificates are also signed by the certificate authority, so a client could potentially impersonate the server, which is unacceptable.
One way to solve it, is to use the tls-verify
directive. This directive lets you run a command to verify the server is who it says it is by checking the public key (public key pinning), CN and whatever else you may feel like. However, t's annoying to set-up because you need to create and deploy and extra script.
Another way to solve it, which is less secure but much simple, and can be used in addition to the first method is to mark server certificates as such and verify it in the client. When creating the server certificate you just need to mark it as a server certificate, and then in the client add the remote-cert-tls server
directive to enforce that. I find this solution sufficient, and it's what I use.
Please let me know if you spotted any mistakes or have any suggestions, and follow me on Twitter or RSS for updates.