Last Updated: 01 Apr 2018
Setting up OpenVPN Server on CentOS 7 using EasyRSA 3
If you travel frequently, it can be handy to use a VPN service with an endpoint back home, particularly if you don't want somebody spying on you in an Internet cafe or airport. Properly setup, a VPN will encrypt all traffic originating from your machine, send it out over the Internet to the VPN server, and then it will go from there to the wider Internet.
There are a number of commercially available VPN services, but if you're technically inclined, you might want to setup your own. This document tells you how, using CentOS 7, OpenVPN, EasyRSA 3, and Viscosity, an excellent VPN client for Mac & Windows.
Basic CentOS Setup
First, you'll need a CentOS 7 server. You can use one from almost anywhere – a machine with Digital Ocean, AWS or Azure, or one in your friend's apartment. Assuming you're starting from scratch, you'll need to get the software updated and the EPEL repo installed.
# sudo yum update -y # sudo yum install epel-release -y # sudo yum update -y
Then you'll want to install openvpn, easyrsa, iptables and (recommended) a few network troubleshooting tools.
# sudo yum install -y openvpn easy-rsa iptables iptables-services wget yum-cron net-tools bind-utils nc mtr
Setting up OpenVPN
Now, you'll want to configure the OpenVPN server. To do this, copy the following file to
/etc/openvpn/server.conf. Thanks to the hardened config from Tristor, which I used as a starting point.
# Secure OpenVPN Server Config # Basic Connection Config dev tun proto udp port 1194 keepalive 10 120 max-clients 5 # Certs ca ca.crt cert server.crt key server.key dh dh.pem tls-auth ta.key 0 # Ciphers and Hardening reneg-sec 0 remote-cert-tls client crl-verify crl.pem tls-version-min 1.2 cipher AES-256-CBC auth SHA512 tls-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 # Drop Privs user nobody group nobody # IP pool server 172.31.100.0 255.255.255.0 topology subnet ifconfig-pool-persist ipp.txt client-config-dir ccd # Misc persist-key persist-tun comp-lzo # DHCP Push options force all traffic through VPN and sets DNS servers push "redirect-gateway def1 bypass-dhcp" push "dhcp-option DNS 220.127.116.11" push "dhcp-option DNS 18.104.22.168" # Logging log-append /var/log/openvpn.log verb 3
Make the Client Config Directory
This directory can hold client-specific configs, if desired:
# mkdir /etc/openvpn/ccd
Generating the Keys and Certificates
OpenVPN provides a tool called EasyRSA, which lets you generate the keys you need. You installed it above, so time to get going:
First, Init PKI:
# cd ~ # /usr/share/easy-rsa/3/easyrsa init-pki
Now, build the certificate authority. You'll be asked for a common name; I use 'VPN'. If you want to be extra secure, you can set this to be the FQDN of your host, and then turn on an option to make sure that the CN of the certificate matches the FQDN of the server, but I prefer not to do this, so my certificates are portable:
# /usr/share/easy-rsa/3/easyrsa build-ca nopass
Generate the Diffie-Helllman parameters:
# /usr/share/easy-rsa/3/easyrsa gen-dh
Generate the server keys (vpn-server should be the name of your server):
# /usr/share/easy-rsa/3/easyrsa build-server-full vpn-server nopass
Generate one or more client keys (vpn-client-01 should be the name of your client machine):
# /usr/share/easy-rsa/3/easyrsa build-client-full vpn-client-01 nopass
Generate the certificate revocation list:
# /usr/share/easy-rsa/3/easyrsa gen-crl
Generate a pre-shared key. This helps harden your VPN; for details see the details about
–tls-auth on the OpenVPN Hardening page.
# openvpn --genkey --secret pki/ta.key
Copying the Keys
Now you need to copy your keys to the OpenVPN config directory. Everything should be in
# sudo cp pki/ca.crt /etc/openvpn/ca.crt # sudo cp pki/dh.pem /etc/openvpn/dh.pem # sudo cp pki/issued/vpn-server.crt /etc/openvpn/server.crt # sudo cp pki/private/vpn-server.key /etc/openvpn/server.key # sudo cp pki/ta.key /etc/openvpn/ta.key # sudo cp pki/crl.pem /etc/openvpn/crl.pem
Setup OpenVPN to Start Automatically
Next up, you'll want to startup OpenVPN. To do so:
# sudo systemctl -f enable email@example.com # sudo systemctl start firstname.lastname@example.org
If it doesn't start, check the log for clues:
# sudo tail -f /var/log/openvpn.log
Optionally, you may want to skip ahead to Setting up the Client, so you can test things out. At this point you should be able to connect to your VPN server, and ping it from your client (
172.31.100.1 if you've followed the steps above ). You won't be able to pass live traffic through the VPN until you complete the routing steps below, but this can be a good point to make sure that everything is working.
Setting up Routing w/ iptables
Since your server will be a VPN server, you probably want to block most inbound connections, except over SSH and the OpenVPN port. You'll also want to allow packet forwarding from VPN clients to the wider internet, or else your client could only talk to your VPN server itself. We do that with iptables.
Enable IPv4 Forwarding
/etc/sysctl.conf and add the following lines:
# Packet forwarding net.ipv4.ip_forward = 1
Save the file, and then run
sysctl -p to load the changes. You can verify forwarding is enabled by doing
more /proc/sys/net/ipv4/ip_forward which should return 1 if its enabled.
Now, we run a series of commands to configure
iptables. Thanks again to Tristor for his work here, which forms the basis of this. Note that you MUST do this as part of a bash script which gets run all at once, if you type these commands line by line you'll cut yourself off from SSH access before you can enter the commands to open it up. Also, if you're not using
172.31.100.0/24 for your VPN subnet, you'll need to change that below.
#!/bin/bash # REMEMBER: Run this as a single bash script or you'll lock yourself out of your machine. # Flushing all rules iptables -F FORWARD iptables -F INPUT iptables -F OUTPUT iptables -X # Setting default filter policy iptables -P INPUT DROP iptables -P OUTPUT DROP iptables -P FORWARD DROP # Allow unlimited traffic on loopback iptables -A INPUT -i lo -j ACCEPT iptables -A OUTPUT -o lo -j ACCEPT # Accept outbound on the primary interface iptables -I OUTPUT -o eth0 -d 0.0.0.0/0 -j ACCEPT # Accept inbound TCP packets iptables -I INPUT -i eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT # Allow incoming SSH iptables -A INPUT -p tcp --dport 22 -m state --state NEW -s 0.0.0.0/0 -j ACCEPT # Allow incoming OpenVPN iptables -A INPUT -p udp --dport 1194 -m state --state NEW -s 0.0.0.0/0 -j ACCEPT # Enable NAT for the VPN iptables -t nat -A POSTROUTING -s 172.31.100.0/24 -o eth0 -j MASQUERADE # Allow TUN interface connections to OpenVPN server iptables -A INPUT -i tun0 -j ACCEPT # Allow TUN interface connections to be forwarded through other interfaces iptables -A FORWARD -i tun0 -j ACCEPT iptables -A OUTPUT -o tun0 -j ACCEPT iptables -A FORWARD -i tun0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -A FORWARD -i eth0 -o tun+ -m state --state RELATED,ESTABLISHED -j ACCEPT # Allow outbound access to all networks on the Internet from the VPN iptables -A FORWARD -i tun0 -s 172.31.100.0/24 -d 0.0.0.0/0 -j ACCEPT # Block client-to-client routing on the VPN iptables -A FORWARD -i tun0 -s 172.31.100.0/24 -d 172.31.100.0/24 -j DROP
Finally, enable that config as a service.
sudo systemctl enable iptables sudo systemctl start iptables sudo service iptables save
Setting up the Client
Getting the VPN server running is only half the battle. Actually, it's about 90% of the battle, but you still have to do the client side work. Assuming you're using Mac or Windows, you pretty much want to use Viscosity. It's an excellent third party VPN client, and my favorite. It's $9, but pay it. It's worth it.
First, on the server, make a directory and copy the client config and client certificates into it:
# cd ~ # mkdir vpn-client-01-config # cp pki/ca.crt vpn-client-01-config/ca.crt # cp pki/issued/vpn-client-01.crt vpn-client-01-config/client.crt # cp pki/private/vpn-client-01.key vpn-client-01-config/client.key # cp pki/ta.key vpn-client-01-config/ta.key
Now, create the config file, at
vpn-client-01-config/client.ovpn. Be sure to change the remote line to point to your vpn server!
# Secure OpenVPN Client Config #viscosity dns full #viscosity usepeerdns true #viscosity dhcp true tls-client pull client dev tun proto udp remote 22.214.171.124 1194 redirect-gateway def1 nobind persist-key persist-tun comp-lzo verb 3 ca ca.crt cert client.crt key client.key tls-auth ta.key 1 remote-cert-tls server ns-cert-type server key-direction 1 cipher AES-256-CBC tls-version-min 1.2 auth SHA512 tls-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
Now tar up the config, and download it to your Mac or Windows machine:
# tar cvfz vpn-client-01-config.tgz vpn-client-01-config
Then, simply import the
client.opvn into Viscosity, and you'll be all set.