OpenVPN is a full-featured SSL VPN which implements OSI layer 2 or 3 secure network extension using the industry standard SSL/TLS protocol, supports flexible client authentication methods based on certificates, smart cards, and/or username/password credentials, and allows user or group-specific access control policies using firewall rules applied to the VPN virtual interface
[OVPN-HOWTO]. Its cross-platform portability, renown security and ease of use have made OpenVPN one of the most popular VPN solutions today.
Unlike IPsec, OpenVPN is not tightly integrated into the Operating System's kernel, but runs as a user-mode daemon and communicates with the TCP/IP stack via a tun(4) pseudo-device. Please refer to [OVPN-SEC2] for a detailed overview of the OpenVPN protocol and security model.
In the next paragraphs, we will implement the same VPN topology as in the previous chapter, though replacing IPsec with OpenVPN. The VPN1 machine will act as the server and wait for incoming connections from VPN2.
OpenVPN installation simply requires adding a couple of packages on both server and client(s):
The first step in configuring OpenVPN is to set up the Public Key Infrastructure, by creating:
The CA private key will be used to sign the server and client certificates; this will allow the two VPN endpoints to mutually authenticate each other simply by verifying the CA signature of the other party's certificate, without having to previously know any other certificate but their own (see [OVPN-PKI] for further details).
OpenVPN provides a set of scripts, located in /usr/local/share/examples/openvpn/easy-rsa/2.0/, that greatly simplify the process of creating and managing the PKI. These scripts require, as a preliminary step, that you initalize a bunch of parameters in the vars file with your organization's data, to avoid being prompted for the same information every time you create a new certificate:
export EASY_RSA="`pwd`" export OPENSSL="openssl" export PKCS11TOOL="pkcs11-tool" export GREP="grep" export KEY_CONFIG="$EASY_RSA/openssl.cnf" export KEY_DIR="$EASY_RSA/keys" echo NOTE: If you run ./clean-all, I will be doing a rm -rf on $KEY_DIR export PKCS11_MODULE_PATH="dummy" export PKCS11_PIN="dummy" export KEY_SIZE=1024 export CA_EXPIRE=3650 export KEY_EXPIRE=3650 export KEY_COUNTRY="IT" export KEY_PROVINCE="Italy" export KEY_CITY="Milan" export KEY_ORG="Kernel Panic Inc." export KEY_EMAIL="danix@kernel-panic.it"
Now, after sourcing the vars file, you can initialize the PKI by building the Diffie-Hellman parameters and creating the root CA certificate and key:
# cd /usr/local/share/examples/openvpn/easy-rsa/2.0/ # . ./vars NOTE: when you run ./clean-all, I will be doing a rm -rf on /usr/local/share/example/opevvpn/easy-rsa/2.0/keys # ./clean-all # ./build-dh Generating DH parameters, 1024 bit long safe prime, generator 2 This is going to take a long time [ ... ] # ./pkitool --initca Using CA Common Name: Kernel Panic Inc. CA Generating a 1024 bit RSA private key .........................++++++ ......++++++ writing new private key to 'ca.key' ----- #
The next step is creating the certificate and key for the VPN server:
# ./pkitool --server vpn1.kernel-panic.it Generating a 1024 bit RSA private key ........++++++ ...........................................++++++ writing new private key to 'vpn1.kernel-panic.it.key' ----- Using configuration from /usr/local/share/examples/openvpn/easy-rsa/2.0/openssl.cnf Check that the request matches the signature Signature ok The Subject's Distinguished Name is as follows countryName :PRINTABLE:'IT' stateOrProvinceName :PRINTABLE:'Italy' localityName :PRINTABLE:'Milan' organizationName :PRINTABLE:'Kernel Panic Inc.' commonName :PRINTABLE:'vpn1.kernel-panic.it' emailAddress :IA5STRING:'danix@kernel-panic.it' Certificate is to be certified until Jun 2 08:41:51 2019 GMT (3650 days) Write out database with 1 new entries Data Base Updated #
Next, we will use the pkitool utility to generate as many client certificates as we need:
# ./pkitool vpn2.kernel-panic.it Generating a 1024 bit RSA private key ...................++++++ ..++++++ writing new private key to 'vpn2.kernel-panic.it.key' ----- Using configuration from /usr/local/share/examples/openvpn/easy-rsa/2.0/openssl.cnf Check that the request matches the signature Signature ok The Subject's Distinguished Name is as follows countryName :PRINTABLE:'IT' stateOrProvinceName :PRINTABLE:'Italy' localityName :PRINTABLE:'Milan' organizationName :PRINTABLE:'Kernel Panic Inc.' commonName :PRINTABLE:'vpn2.kernel-panic.it' emailAddress :IA5STRING:'danix@kernel-panic.it' Certificate is to be certified until Jun 2 08:47:25 2019 GMT (3650 days) Write out database with 1 new entries Data Base Updated #
So we have generated all the certificates and keys we need; you can find them in the /usr/local/share/examples/openvpn/easy-rsa/2.0/keys directory, ready to be copied to the appropriate machines. But before proceeding to copy the key files, we need to create, on both server and clients, the directory (/etc/openvpn/private) that will contain the private keys and assign it restrictive permissions to prevent unauthorized access.
# mkdir -p /etc/openvpn/private # chmod 700 /etc/openvpn/private
The following are the files that must be copied from the CA-signing machine to the OpenVPN hosts:
Finally, remember to delete all the files in /usr/local/share/examples/openvpn/easy-rsa/2.0/keys/:
# ./clean-all
OpenVPN supports a number of configuration parameters, allowing you to deeply customize its behaviour. These parameters can be either passed from the command-line or in a configuration file. Omitted parameters take the default value.
Below is a sample configuration file (see [OVPN-MAN] for a complete list of all the available parameters):
# Transport protocol to use. Available protocols are udp and tcp-server proto udp # TCP/UDP port to bind to port 1194 # Name of the tun(4) device to use dev tun0 # Uncomment to enable the management interface on port 1195. The password file # only contains the management password on a single line. #management 127.0.0.1 1195 /etc/openvpn/private/mgmt.pwd # Path to the CA certificate ca /etc/openvpn/ca.crt # Path to the server's certificate file cert /etc/openvpn/vpn1.kernel-panic.it.crt # Path to the private key file key /etc/openvpn/private/vpn1.kernel-panic.it.key # Path to the file containing the Diffe-Hellman parameters dh /etc/openvpn/dh1024.pem # Address range for the tun(4) interfaces server 10.0.1.0 255.255.255.0 # Uncomment to allow clients to dynamically change address (useful for # road-warriors) #float # Send periodic keepalive messages keepalive 10 120 # Use lzo compression to reduce network utilization comp-lzo # User the OpenVPN daemon should run as user _openvpn # Group the OpenVPN daemon should run as group _openvpn # Make the server daemonize after initialization daemon openvpn # Don't re-read key files upon receiving a SIGUSR1 signal persist-key # Don't close and reopen the tun(4) device upon receiving a SIGUSR1 signal persist-tun # Add a route to the local network to the client's routing table push "route 172.16.0.0 255.255.255.0" # Add routes to the remote networks to the server's routing table route 192.168.0.0 255.255.255.0 route 192.168.1.0 255.255.255.0 # Directory for client-specific configuration files client-config-dir /etc/openvpn/ccd # Uncomment to periodically write status information to the specified file #status /var/log/openvpn-status.log # Uncomment to raise verbosity level for debugging #verb 11
The client-config-dir directive in the server configuration file allows you to specify a directory containing client-specific configuration files. These files must have have the same name as the client's X509 Common Name, specified during the creation of the certificates. In this case, we will create a file named /etc/openvpn/ccd/vpn2.kernel-panic.it, which will specify which private networks can be reached through the OpenVPN client:
iroute 192.168.0.0 255.255.255.0 iroute 192.168.1.0 255.255.255.0
Though very similar, both the route and iroute directives are necessary, because route controls the routing from the kernel to the OpenVPN server (via the tun(4) interface) while iroute controls the routing from the OpenVPN server to the remote clients
[OVPN-HOWTO].
The client-side configuration is pretty similar to server-side configuration. The address and port of the server are specified via the remote directive. Make sure that the configuration matches the server configuration, in particular that they both use the same protocol, device type and that they both enable or disable lzo compression.
# Act as a client client # IP address (or hostname) and port of the OpenVPN server. You may specify # multiple 'remote' options for redundancy. remote 1.2.3.4 1194 # Transport protocol to use. Available protocols are udp and tcp-client proto udp # Name of the tun(4) device to use dev tun0 # Uncomment if you connect through an HTTP proxy. The authfile must contain # user and password on 2 lines. The authentication type can be 'none', 'basic' # or 'ntlm' #http-proxy proxy_addr proxy_port /etc/openvpn/private/authfile auth_type # Make the server daemonize after initialization daemon openvpn # Send periodic keepalive messages keepalive 10 120 # Don't bind to the local address and port, i.e. don't wait for incoming # connections nobind # User the OpenVPN daemon should run as user _openvpn # Group the OpenVPN daemon should run as group _openvpn # Directory to chroot to after initialization chroot /var/empty # Don't re-read key files upon receiving a SIGUSR1 signal persist-key # Don't close and reopen the tun(4) device upon receiving a SIGUSR1 signal persist-tun # Path to the CA certificate ca /etc/openvpn/ca.crt # Path to the client's certificate file cert /etc/openvpn/vpn2.kernel-panic.it.crt # Path to the private key file key /etc/openvpn/private/vpn2.kernel-panic.it.key # Require that the peer certificate has the nsCertType field set to 'server' ns-cert-type server # Use lzo compression to reduce network utilization comp-lzo # Uncomment to periodically write status information to the specified file #status /var/log/openvpn-status.log # Uncomment to raise verbosity level for debugging #verb 11
Before starting the VPN, we have to enable IP forwarding on both gateways, since they will have to perform routing of network traffic:
# sysctl net.inet.ip.forwarding=1
Uncomment the following line in /etc/sysctl.conf(5) to re-enable IP forwarding after reboot:
net.inet.ip.forwarding=1
So we're ready to start the VPN! Just run the following command on the server:
vpn1# openvpn --config /etc/openvpn/server.conf
and the following on the client:
vpn2# openvpn --config /etc/openvpn/client.conf
To finish, we just have to create the configuration file for the tun(4) interface on the server (starting OpenVPN from this file improves compatibility with PF):
up !/usr/local/sbin/openvpn --daemon --config /etc/openvpn/server.conf
and on the client:
up !/usr/local/sbin/openvpn --daemon --config /etc/openvpn/client.conf