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.
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
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
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
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
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 requestsNow 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
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
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*
Topology of the network
Either subnet, or point to point
Some public wifi blocks non-web ports, workaround: use udp53 or tcp443
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 18.104.22.168 dhcp-option DNS 22.214.171.124 # 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 unitIn 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
I then create
[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 trafifcYou 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 126.96.36.199/32 -p tcp -m tcp --dport 53 -j RETURN iptables -t nat -A PREROUTING -d 188.8.131.52/32 -p tcp -m tcp --dport 53 -j RETURN iptables -t nat -A PREROUTING -d 184.108.40.206/32 -p udp -m udp --dport 53 -j RETURN iptables -t nat -A PREROUTING -d 220.127.116.11/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 18.104.22.168:53 iptables -t nat -A PREROUTING -p udp -m udp --dport 53 -j DNAT --to-destination 22.214.171.124:53
Connecting clientsWe 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 NetworkManager
Concatenating 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
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