Comprehensive OpenVPN Tutorial
In this tutorial, we will look at managing an openvpn server and certificate authority and will provide a detailed breakdown of the configuration options
Table of contents
- Setting up a CA
- Server Preparation
- Client preparation
- Signing requests on the CA
- OpenVPN configuration
- Example config
- A breakdown of the config directives
- Systemd unit
- Redirect DNS traffic
The Certificate Authority
We will be setting up our certificates using a utility known as easy-rsa
which is a wrapper for OpenSSL. For security reasons, we will have the CA on a seperate machine .
We have easy rsa in three separate environments.
1. CA
2. The server
3. The client
The CA is the most important component of the authentication infrastructure. As a simple analogy, think of the certificates as passports and the client and server as a passenger and officer; the CA is the government issuing the passports. In fact, most modern electronic passports contain cryptographic information signed by the manafacturer's CA.
If you don't want to use a separate machine for your CA, you can just build the CA in a different folder. Once your server is up and running, it's highly recommended you move it somewhere offline
Build the CA environment
root@CA # ca_path=/root/identity/vpn_ca
root@CA # mkdir --parent $ca_path && cd ca_path
root@CA # cp -/usr/share/easy-rsa/3.0.1/* .
# If you wish to change days till expiry etc
root@CA # nano easyrsa
Now we can generate the CA cert and key
The key is protected with a passphrase. It doesn't have to be a crazy complex random string. The critical part is making sure its completely confidential;
[root@CA]# ./easyrsa build-ca
Generating a 2048 bit RSA private key
.............+++
..........................................................................................+++
writing new private key to '/root/identity/vpn_ca/ca.key.xxx'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
[...]
A creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/root/identity/vpn_ca/ca.crt
The CA.crt file is required on both the server and all clients
Generating a certificate revocation cert
[root@CA ]# ./easyrsa gen-crl
Using configuration from /root/identity/vpn_ca/pki/openssl-1.0.cnf
Enter pass phrase for /root/identity/vpn_ca/pki/pki/private/ca.key:
An updated CRL has been created.
CRL file: /root/identity/vpn_ca/pki/crl.pem
Transfer this file to the server
Preparing the Server
Preparing the environment
[root@SERVER ]# cp /usr/share/easy-rsa/3.0.1/* /etc/openvpn/server
[root@SERVER ]# cd /etc/openvpn/server && ./easyrsa init-pki
[root@SERVER ]# mkdir /etc/openvpn/server/ccd
Generating Diffie-Hellman Parameters
Diffie-Hellman is used to ensure forward secrecy.
I explain what it works and how it works here (it's very awesome algorithm)
[root@SERVER ]# openssl dhparam --out dh.pem 4096
Generating DH parameters, 4096 bit long safe prime, generator 2
This is going to take a long time
.......................................................+........
Generating the certificate signing request for the server
We begin by generating a certificate signing request (CSR) for the server
This CSR will be sent to our certificate authority. We then log into the CA and sign the request (requires the CA key passphrase).
Before attempting to establish a VPN connection, clients need to be confident that the server they have connected to is actually the real deal. We only accept a server if its presents a certificate that has been signed by the CA .
This is why we use a private internal CA because (unless your passphrase is compromised) anything signed by the CA can be assumed to be trusted because you were the one who signed it. As opposed to a commercial third party CA signing thousands or millions of certs.
[root@SERVER ]# ./easyrsa gen-req vpn-example.etherarp.net
We then copy the CSR to our CA; we will use scp/ssh to do it over the network
[root@SERVER ]# scp pki/reqs/vpn-example.etherarp.net.req user@CA:/tmp
#
If you're really paranoid, compare the hashes side by-side by running openssl sha512
The gen-req
command creates a certificate signing request and a key. The key is by default encrypted. For client side this is a good security benefit, you don't want whoever finds your lost usb stick to access your network.
However, on server side, there are disadvantages. Each time the OpenVPN service is restarted, you will need to manually enter in the passphrase, so its less than ideal.
We will remove the passphrase on the servers key
[root@SERVER]# openssl rsa -in vpn-example.etherarp.net.key -out /etc/openvpn/server.key
Enter pass phrase for vpn-example.etherarp.net.key:
writing RSA key
Setting up the TLS Authentication static key (ta.key)
[root@SERVER ]# openvpn --gen-key --secret ta.key
Generating a template for clients
Let's make a template for the client configuration file. The only difference between clients is that every client has its own cert and key.
[root@SERVER ]# cat > template
client
remote vpn-example.etherarp.net 1194
tls-client
remote-cert-eku "TLS Web Server Authentication"
cipher AES-256-CBC
dev tun
proto udp
nobind
script-security 2
persist-key
persist-tun
auth SHA512
keepalive 10 120
All clients must possess the ta.key
and ca.crt
We can include files in-line in the client configuration file. So lets append our template to include them.
echo "<ca>" >>template && cat ca.crt> >template && echo -e "</ca>\n<tls-auth>">>template && cat ta.key >> template && echo -e "</tls-auth>\nkey-direction 1" >> template
We can generate this template on the server
Iptables Firewall
Assuming your vpn subnet is "10.84.84.0/24"
and your internet interface is named eth0 and has an address of 192.0.2.113
[root@SERVER ]# iptables -t nat -A POSTROUTING -s $VPN_SUBNET -o eth0 -j SNAT --to-source $PUBLIC_IP
[root@SERVER ]# iptables -I INPUT -p udp --dport 1194 -j ACCEPT
[root@SERVER ]# iptables -A FORWARD -i tun0 -o eth0 -j ACCEPT
[root@SERVER ]# iptables -A FORWARD -i eth0 -o tun0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
[root@SERVER ]# iptables -A FORWARD -j REJECT --reject-with icmp-port-unreachable
Client preparation
Building the client environment
[root@CLIENT ]# client_path=/root/openvpn-client
[root@CLIENT ]# mkdir $client-path
[root@CLIENT ]# cp /usr/share/easy-rsa/3.0.1/* $client_path
[root@CLIENT ]# cd $client_path && ./easyrsa init-pki
Generating the certificate signing request
[root@CLIENT ]# ./easyrsa gen-req desktop
[root@CLIENT ]# scp pki/reqs/desktop.req user@CA:/tmp
Signing certificate requests
Now we head back to our CA, and make sure the requests from the server and client have been transferred.Importing the request
[root@CA ]# ./easyrsa import-req /tmp/vpn-example.etherarp.net.req vpn-example
The request has been successfully imported with a short name of: vpn-example
You may now use this name to perform signing operations on this request.
Signing the requests
(I've cleaned the output a little bit).
We sign the requests, making sure to first specify whether it's a client or server. We use the short name we imported it under.
Remember: We need to sign the request for both the client and the server
After entering your CA keys passphrase, you should be good to go.
[root@CA]# ./easyrsa sign-req server vpn-example.etherarp.net
We now need to import and sign our client request
[root@CA]# ./import-req /tmp/desktop.vpn-example.etherarp.net.req desktop
[root@CA]# ./easyrsa sign-req client desktop
You are about to sign the following certificate.
subject=
commonName = desktop.vpn-example.etherarp.net
Confirm request details: yes
Enter pass phrase for private/ca.key:
Signature ok
The Subject's Distinguished Name is as follows
commonName :ASN.1 12:'desktop'
Certificate is to be certified until Nov 15 09:27:25 2018 GMT (365 days)
Certificate created at: pki/issued/desktop.crt
Now all you've got left to do is copy desktop.crt to your desktop and your server.crt to your server.
Put server.crt in /etc/openvpn/server.crt
OpenVPN configuration
- Example Server Example Client
Let's unpack the server options
We run as the openvpn user (in some systems, you run as 'nobody'
user nobody
group nogroup
dev : Specifies What type of virtual interface to use (tun/tap)
Only use tap if you want to transparently tunnel ethernet segments
dev tun
Persist the state of the key and the tunnel interface accross server reboots
Initializing these requires root and openvpn is no longer running as root*
persist-key
persist-tun
Topology of the network
Either subnet, or point to point
topology subnet
Listening port
Some public wifi blocks non-web ports, workaround: use udp53 or tcp443
port 1194
Protocol (tcp/udp)
If the connection between the sites is poor, performance sharply decreases if using tcp
proto udp
explicit-exit-notify 1 # only valid for udp
Connection keep-alive times.
If no traffic for 10s, send a ping, restart after 120s silence
keepalive 10 120
Address for the Virtual Subnet
You can generate a random one with $(ipcalc --random-private=24 -n | cut -d= -f2)
server 10.84.84.0 255.255.255.0
Cipher and protocol hardening
cipher AES-256-CBC
ncp-ciphers AES-256-GCM:AES-256-CBC
tls-version-min 1.2
tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256
TLS server options
tls-server
remote-cert-eku "TLS Web Client Authentication"
ca ca.crt
cert server.crt
key server.key
dh dh.pem
crl-verify crl.pem
TLS authentication (HMAC)
We generate the key with "openvpn --genkey --secret ta.key"
Both the client and the server need this key.
tls-auth ta.key 0
auth SHA512
Options to 'push' to clients
Similar concept to DHCP
# Push a single route
;push "route 192.168.1.0 255.255.255.0"
# Clients use VPN for all internet connections
push "redirect-gateway def1"
# Clients use this DNS
dhcp-option DNS 64.6.64.6
dhcp-option DNS 64.6.65.6
# Each client has a persistent ip
ifconfig-pool-persist ipp.txt
# Custom options for particular clients
client-config-dir ccd
Miscellanea (can be skipped)
# Logging Directories
status openvpn-status.log
log /var/log/openvpn.log
verb 3
# Client connection script
;client-connect /etc/openvpn/statuschange.sh
;client-disconnect /etc/openvpn/statuschange.sh
script-security 2
# Internally handle client-to-client connections
# By default, client-to-client connections are routed by the server OS
# and require iptables FORWARD rules
client-to-client
Creating a systemd unit
In older versions of GNU/Linux, as long as you have the file `/etc/openvpn/server.conf`You can start it on boot with
/etc/init.d/openvpn start && update-rc.d openvpn enable
For newer versions of GNU/Linux, we have to deal with systemd. Apparently on a lot of distors such as Fedora, OpenVPN uses a "template based systemd" service unit.
I was slightly confused by how this works, so decided to make my own systemd unit.
My configuration file is in /etc/openvpn/server/openvpn.conf
I then create /etc/systemd/system/openvpn.service
[Unit]
Description=OpenVPN Robust And Highly Flexible Tunneling Application On OpenVPN
After=syslog.target network.target
[Service]
Type=forking
PIDFile=/var/run/openvpn/<server>.pid
ExecStart=/usr/sbin/openvpn --daemon --writepid /var/run/openvpn/openvpn.pid --cd /etc/openvpn/ --config openvpn.conf
[Install]
WantedBy=multi-user.target
Then to enable it
[root@SERVER ]# systemctl enable openvpn.service
[root@SERVER ]# systemctl start openvpn.service
Redirecting DNS trafifc
You may wish to force your clients to use a particular DNS. While OpenVPN has an option to "push" DNS settings, the client is still ultimately responsible for this.We can use iptables to redirect all DNS traffic to your desired resolvers. This is useful for devices that may force you to use a particular server. Also, this technique is not just for VPN servers but will work on any iptables based router/gateway
We run the following commands on the server
Don't redirect traffic to Verisign DNS
iptables -t nat -A PREROUTING -d 64.6.64.6/32 -p tcp -m tcp --dport 53 -j RETURN
iptables -t nat -A PREROUTING -d 64.6.65.6/32 -p tcp -m tcp --dport 53 -j RETURN
iptables -t nat -A PREROUTING -d 64.6.64.6/32 -p udp -m udp --dport 53 -j RETURN
iptables -t nat -A PREROUTING -d 64.6.65.6/32 -p udp -m udp --dport 53 -j RETURN
Redirect all other DNS
iptables -t nat -A PREROUTING -p tcp -m tcp --dport 53 -j DNAT --to-destination 64.6.64.6:53
iptables -t nat -A PREROUTING -p udp -m udp --dport 53 -j DNAT --to-destination 64.6.64.6:53
Connecting clients
We can make a `systemd` unit in a fashion analogous to the server. More conveniently, we should be able to import our client `.ovpn` file directly into NetworkManagerConcatenating the client keys to the file
Remember the template file we created for clients?
We can now create a customized openvpn file for the client.
On the client machine (assuming desktop.crt
and template
have been copied) we can do the following.
Warning: removes encryption from the client key Replace the openssl command with cat
if you don't want this
cd $client_path
cat template > desktop.ovpn
echo <cert> >> desktop.ovpn
cat desktop.crt >> desktop.ovpn
echo </cert> >> desktop.ovpn
echo <key> >> desktop.ovpn
openssl rsa -in pki/private/desktop.key >> desktop.ovpn
echo </key> >> desktop.ovpn