openvpn_logo
If you have landed on this post, you have probably already stumbled upon ReadWrite’s post Building A Raspberry Pi VPN Part One: How And Why To Build A Server (and the following Part Two). Both of them are based on the excellent SANS article SOHO Remote Access VPN. Easy as Pie, Raspberry Pi… which I recommend you read in its entirety, being a very informative read even if you don’t focus on the technical details of how to configure a VPN server.

However, I found that both of these sources were missing some details which, in my opinion, would have made them even better. While following these guide, I added some notes of my own for future reference, so I really had 3 goals in mind when I decided to write this post expanding on the previously mentioned resources:

  • Clarify the most confusing steps in the process of configuring an OpenVPN server
  • Add some instructions on how to include some basic OS-hardening (if you have to go through the hassle of setting up a VPN server, presumably for security reasons, I think you should do it right)
  • Increase the security of the OpenVPN server using bigger keys, for the most paranoid out there (it’s an interesting exercise even if you don’t really need the extra level of security).

I am going to skip the detailed instructions on how to prepare the SD card and how to boot into Raspbian for the first time. I am just going to recommend Win32DiskImager to do this if you are on Windows:

OS configuration and OpenVPN installation

  1. After booting into Raspbian, you can login with the default credentials:
    • Username: pi
    • Password: raspberry
  2. Refresh your repositories so you can download the latest version of the packages we are going to use:
    sudo apt-get update
  3. Run
    sudo apt-get upgrade

    to effectively upgrade your system packages

  4. Finally, run
    sudo rpi-update

    to update your Pi’s firmware (it’s important to leave this action for last in this update process so that the latest version of rpi-update will be downloaded)

  5. At the end of the process you should see a message prompting you to reboot. Go ahead and run a
    sudo reboot

    to proceed.

  6. Let’s change the hostname of the Pi from the default to something like “vpnserver”. To do this successfully, you have to update the hostname in the following two locations:
    • /etc/hostname
    • /etc/hosts
  7. Let’s create a new user for this Raspberry, as using the default user pi would leave brute force and dictionary attacks a higher chance of success. For this guide, I am going to use the name “vpn”:
  8. sudo adduser vpn
  9. Fill in the information:
  10. Add this user to the sudoers file (i.e. the users allowed to gain root privileges):
    sudo visudo
  11. Look for the line beginning with
    #includedir /etc/sudoers.d

    and add this line:

    vpn ALL=(ALL) ALL

    . Using ALL rather than the nopassword that the default user pi is using increases security by a factor of more than a billion billions.

  12. Reboot so we can login with the newly created user:
    sudo reboot
  13. Run
    sudo userdel -r pi

    to delete the default user pi

  14. Check that the command has been successful running
    cut -d: -f1 /etc/passwd
  15. Now it’s time to install OpenVPN:
    sudo apt-get install openvpn

Configuring the PKI

We will be using OpenVPN with certificate authentication, which is way more secure than plain password authentication. Every device will be issued a certificate that it will have to use to successfully connect to the OpenVPN server. To do so, however, we need to have a PKI in place to issue and validate certificates.

Server configuration

  1. Go root for a while now, since everything we will be doing now requires root privileges:
    sudo -s
  2. Create a folder for easy RSA to store all the certificate information:
    mkdir /etc/openvpn/easy-rsa
  3. Copy the included configuration example to the newly created folder so we have a template to start from:
    cp /usr/share/doc/openvpn/examples/easy-rsa/2.0/* /etc/openvpn/easy-rsa
  4. Move to the newly created folder:
    cd /etc/openvpn/easy-rsa
  5. Open the vars file
    nano vars

    and paste the following content:

    # easy-rsa parameter settings
    
    # NOTE: If you installed from an RPM,
    # don't edit this file in place in
    # /usr/share/openvpn/easy-rsa --
    # instead, you should copy the whole
    # easy-rsa directory to another location
    # (such as /etc/openvpn) so that your
    # edits will not be wiped out by a future
    # OpenVPN package upgrade.
    
    # This variable should point to
    # the top level of the easy-rsa
    # tree.
    export EASY_RSA="/etc/openvpn/easy-rsa"
    
    #
    # This variable should point to
    # the requested executables
    #
    export OPENSSL="openssl"
    export PKCS11TOOL="pkcs11-tool"
    export GREP="grep"
    
    
    # This variable should point to
    # the openssl.cnf file included
    # with easy-rsa.
    export KEY_CONFIG=`$EASY_RSA/whichopensslcnf $EASY_RSA`
    
    # Edit this variable to point to
    # your soon-to-be-created key
    # directory.
    #
    # WARNING: clean-all will do
    # a rm -rf on this directory
    # so make sure you define
    # it correctly!
    export KEY_DIR="$EASY_RSA/keys"
    
    # Issue rm -rf warning
    echo NOTE: If you run ./clean-all, I will be doing a rm -rf on $KEY_DIR
    
    # PKCS11 fixes
    export PKCS11_MODULE_PATH="dummy"
    export PKCS11_PIN="dummy"
    
    # Increase this to 2048 if you
    # are paranoid.  This will slow
    # down TLS negotiation performance
    # as well as the one-time DH parms
    # generation process.
    export KEY_SIZE=2048
    
    # In how many days should the root CA key expire?
    export CA_EXPIRE=3650
    
    # In how many days should certificates expire?
    export KEY_EXPIRE=3650
    
    # These are the default values for fields
    # which will be placed in the certificate.
    # Don't leave any of these fields blank.
    export KEY_COUNTRY="CO"
    export KEY_PROVINCE="Province"
    export KEY_CITY="City"
    export KEY_ORG=changeme
    export KEY_EMAIL="me@myhost.mydomain"
    export KEY_EMAIL=mail@host.domain
    export KEY_CN=changeme
    export KEY_NAME=changeme
    export KEY_OU=changeme
    export PKCS11_MODULE_PATH=changeme
    export PKCS11_PIN=1234
    

    Feel free to customize this of course, but the most important settings here are the key size (the comments say to “Increase this to 2048 if you are paranoid”, so we have done so) and the CA lifetime, 3650 days in this case.

  6. You should still be in the easy-rsa folder, if not, cd to it
  7. Run
    source ./vars
  8. Run
    ./clean-all
  9. Run
    ./build-ca

    buildca

  10. Fill in all the fields
  11. Build the server key pair (private key and public key):
    ./build-key-server vpnserver
    • The common name must match the server name, but by default it should prompt you to use the same name
    • Leave the challenge password blank
    • Answer yes when prompted to “sign cert?”
    • Answer yes when prompted to “commit?”

Client configuration

It’s now time to generate client certificates. This procedure will need to be followed for every end device that you plan to use with OpenVPN.

  1. Run
    ./build-key-pass UserName
    • pem passphrase: your passwords. This protects the private key with a password, to be entered every time you connect to the server
    • Leave the challenge password blank
    • Answer yes when prompted to “sign cert?”
    • Answer yes when prompted to “commit?”
  2. Run
    cd keys
    openssl rsa -in UserName.key -des3 -out UserName.3des.key
    
    • This step is necessary if you are going to use the Android or iOS versions of OpenVPN
    • Don’t get confused by 3DES: it’s much better than DES. It theoretically uses a 56*3 bit key but due to a meet-in-the-middle attack it is equivalent to a cypher suite using a 112-bit key (still much better than standard DES)
    • Enter the passphrase you chose before

Generate Diffie-Hellman parameters

It’s time to generate Diffie-Hellman parameters now for secure key exchange. Keep in mind that, using 2048 bit keys on a machine like a Raspberry Pi, this can take quite a long time (it took a bit less than two hours on my machine).

  1. Run
    cd /etc/openvpn/easy-rsa/
    ./build-dh
    

    builddh

OpenVPN Server Configuration

It’s finally time to tweak the server configuration now.

  1. Run
    openvpn --genkey --secret keys/ta.key

    This will provide DoS protection: each client will have to use this value during the initial handshake, otherwise the connection will be dropped

  2. Run
    nano /etc/openvpn/server.conf

    and paste the following configuration file:

    local 192.168.0.140 # SWAP THIS IP WITH YOUR RASPBERRY PI IP ADDRESS
    topology subnet
    dev tun
    proto udp
    port 1194
    ca /etc/openvpn/easy-rsa/keys/ca.crt
    cert /etc/openvpn/easy-rsa/keys/vpnserver.crt # SWAP WITH YOUR CRT NAME
    key /etc/openvpn/easy-rsa/keys/vpnserver.key # SWAP WITH YOUR KEY NAME
    dh /etc/openvpn/easy-rsa/keys/dh2048.pem
    server 10.8.0.0 255.255.255.0
    # server and remote endpoints
    ifconfig 10.8.0.1 10.8.0.2
    # Add route to Client routing table for the OpenVPN Server
    push "route 10.8.0.1 255.255.255.255"
    # Add route to Client routing table for the OpenVPN Subnet
    push "route 10.8.0.0 255.255.255.0"
    # your local subnet
    #push "route 192.168.0.140 255.255.255.0" # SWAP THIS IP WITH YOUR RASPBERRY PI IP ADDRESS
    # Set primary domain name server
    push "dhcp-option DNS 192.168.0.254" # SWAP THIS IP WITH YOUR DNS SERVER ADDRESS
    # Override the Client default gateway by using 0.0.0.0/1 and
    # 128.0.0.0/1 rather than 0.0.0.0/0. This has the benefit of
    # overriding but not wiping out the original default gateway.
    push "redirect-gateway def1"
    client-to-client
    duplicate-cn
    keepalive 10 120
    tls-auth /etc/openvpn/easy-rsa/keys/ta.key 0
    cipher AES-128-CBC
    comp-lzo
    user nobody
    group nogroup
    persist-key
    persist-tun
    status /var/log/openvpn-status.log 20
    log /var/log/openvpn.log
    verb 1
    
  3. Run
    nano /etc/sysctl.conf

    and paste the following content to enable IP forwarding:

    #
    # /etc/sysctl.conf - Configuration file for setting system variables
    # See /etc/sysctl.d/ for additional system variables
    # See sysctl.conf (5) for information.
    #
    
    #kernel.domainname = example.com
    
    # Uncomment the following to stop low-level messages on console
    #kernel.printk = 3 4 1 3
    
    ##############################################################3
    # Functions previously found in netbase
    #
    
    # Uncomment the next two lines to enable Spoof protection (reverse-path filter)
    # Turn on Source Address Verification in all interfaces to
    # prevent some spoofing attacks
    #net.ipv4.conf.default.rp_filter=1
    #net.ipv4.conf.all.rp_filter=1
    
    # Uncomment the next line to enable TCP/IP SYN cookies
    # See http://lwn.net/Articles/277146/
    # Note: This may impact IPv6 TCP sessions too
    #net.ipv4.tcp_syncookies=1
    
    # Uncomment the next line to enable packet forwarding for IPv4
    net.ipv4.ip_forward=1
    
    # Uncomment the next line to enable packet forwarding for IPv6
    #  Enabling this option disables Stateless Address Autoconfiguration
    #  based on Router Advertisements for this host
    #net.ipv6.conf.all.forwarding=1
    
    
    ###################################################################
    # Additional settings - these settings can improve the network
    # security of the host and prevent against some network attacks
    # including spoofing attacks and man in the middle attacks through
    # redirection. Some network environments, however, require that these
    # settings are disabled so review and enable them as needed.
    #
    # Do not accept ICMP redirects (prevent MITM attacks)
    #net.ipv4.conf.all.accept_redirects = 0
    #net.ipv6.conf.all.accept_redirects = 0
    # _or_
    # Accept ICMP redirects only for gateways listed in our default
    # gateway list (enabled by default)
    # net.ipv4.conf.all.secure_redirects = 1
    #
    # Do not send ICMP redirects (we are not a router)
    #net.ipv4.conf.all.send_redirects = 0
    #
    # Do not accept IP source route packets (we are not a router)
    #net.ipv4.conf.all.accept_source_route = 0
    #net.ipv6.conf.all.accept_source_route = 0
    #
    # Log Martian Packets
    #net.ipv4.conf.all.log_martians = 1
    #
    
  4. Apply the change running
    sysctl -p
  5. Enable NAT between the VPN subnet and your home subnet using iptables:
    nano /etc/openvpn_firewall_rules.sh

    and paste this:

    #!/bin/sh 
    iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j SNAT --to-source 192.168.0.140
    
  6. Run
    chmod 700 /etc/openvpn_firewall_rules.sh
  7. Run
    chown root /etc/openvpn_firewall_rules.sh
  8. Let’s enable this firewall route every time the network interface comes up:
    nano /etc/network/interfaces

    and check that your configuration file looks similar to this:

    # This file describes the network interfaces available on your system
    # and how to activate them. For more information, see interfaces(5).
    
    # The loopback network interface
    auto lo
    iface lo inet loopback
    
    # The primary network interface
    auto eth0
    iface eth0 inet static
    address 192.168.0.140
    netmask 255.255.255.0
    gateway 192.168.0.1
    network 192.168.0.0
    broadcast 192.168.0.255
    dns-nameservers 192.168.0.254
    pre-up /etc/openvpn_firewall_rules.sh
    
  9. Run
    reboot
  10. Run
    sudo -s
  11. Let’s create a default configuration that will be applied to every OpenVPN client:
    nano /etc/openvpn/easy-rsa/keys/Default.txt

    (the file must be called Default.txt unless you modify the MakeOVPN.sh script later) and paste the following:

    client
    dev tun
    proto udp
    remote  1194
    resolv-retry infinite
    nobind
    persist-key
    persist-tun
    mute-replay-warnings
    ns-cert-type server
    key-direction 1
    cipher AES-128-CBC
    comp-lzo
    verb 1
    mute 20
    
  12. Run
    nano /etc/openvpn/easy-rsa/keys/MakeOVPN.sh

    and paste this:

    #!/bin/bash
    # Default Variable Declarations
    DEFAULT="Default.txt"
    FILEEXT=".ovpn"
    CRT=".crt"
    KEY=".3des.key"
    CA="ca.crt"
    TA="ta.key"
    #Ask for a Client name
    echo "Please enter an existing Client Name:"
    read NAME
    #1st Verify that client’s Public Key Exists
    if [ ! -f $NAME$CRT ]; then
    echo "[ERROR]: Client Public Key Certificate not found: $NAME$CRT"
    exit
    fi
    echo "Client’s cert found: $NAME$CR"
    #Then, verify that there is a private key for that client
    if [ ! -f $NAME$KEY ]; then
    echo "[ERROR]: Client 3des Private Key not found: $NAME$KEY"
    exit
    fi
    echo "Client’s Private Key found: $NAME$KEY"
     
    #Confirm the CA public key exists
    if [ ! -f $CA ]; then
    echo "[ERROR]: CA Public Key not found: $CA"
    exit
    fi
    echo "CA public Key found: $CA"
     
    #Confirm the tls-auth ta key file exists
    if [ ! -f $TA ]; then
    echo "[ERROR]: tls-auth Key not found: $TA"
    exit
    fi
    echo "tls-auth Private Key found: $TA"
    #Ready to make a new .opvn file - Start by populating with the
    default file
    cat $DEFAULT > $NAME$FILEEXT
    #Now, append the CA Public Cert
    echo "" >> $NAME$FILEEXT
    cat $CA >> $NAME$FILEEXT
    echo "" >> $NAME$FILEEXT
     
    #Next append the client Public Cert
    echo "" >> $NAME$FILEEXT
    cat $NAME$CRT | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' >> $NAME$FILEEXT
    echo "" >> $NAME$FILEEXT
    #Then, append the client Private Key
    echo "" >> $NAME$FILEEXT
    cat $NAME$KEY >> $NAME$FILEEXT
    echo "" >> $NAME$FILEEXT
    #Finally, append the TA Private Key
    echo "" >> $NAME$FILEEXT
    cat $TA >> $NAME$FILEEXT
    echo "" >> $NAME$FILEEXT
    echo "Done! $NAME$FILEEXT Successfully Created."
     
    #Script written by Eric Jodoin
    \ No newline at end of file
    

Generate OpenVPN configuration files

We finally arrived at the last step of the process: using the certificates we created in the previous steps, we can now generate the OpenVPN configuration files that end devices are going to use to connect to your VPN server.

  1. Run
    cd /etc/openvpn/easy-rsa/keys/
  2. Run
    chmod 700 MakeOVPN.sh
  3. Run
    ./MakeOVPN.sh

    to create the .ovpn configuration file

  4. Download the configuration file to the end device (you might need to temporarily change the permissions of the /etc/openvpn folder from 600 to 700 to do so)