Monday, 15 December 2014

OpenVPN with 2 factor authentication on the Raspberry Pi

This guide, describes how to set up a VPN (Virtual Private Network) on your raspberry pi to allow you to remotely connect to your home network.

A VPN allows a remote computer to act as though its part of your home newtork - vpn clients can see computers sitting on your home lan.  I've found it really useful when I've been working away staying in hotels with relatively slow internet connections.  The VPN in itself does not take up much band width, so once you are connected, and remote desktopped to one of your home PC's, you can operate at your home networks speed.  As the VPN is configured to use UDP, you get a much more stable connection (believe it or not!).


  • Vanilla install of Raspbian (You should be able to login using pi/raspberry)


We're going to use OpenVPN and Ansible to orchestrate the install, and set up the configuration.  

We'll use google authenticator to provide 2 factor authentication, configured as a PAM module.

To test, we'll use a mobile phone, connected to the internet via 3g.

Setup Ansible

We're going to install ansible directly onto the Pi.  Log onto the raspberry pi, using pi/raspberry:

ssh-keygen    (go with defaults and no password)
sudo apt-get update
sudo apt-get install python-dev python-pip sshpass git
sudo pip install ansible
git clone
cd raspi-ansible
Register the raspberry pi with anisble:

ssh  (selecting yes when asked about key fingerprint, password: raspberry)
cd ~/raspi-ansible
ansible-playbook -i live bootstrap.yml -k     (password: raspberry)

Edit inventory

The live file is called the inventory.  This lists the network config, which openvpn needs.  Edit this to match your own network configuration:

[pi] isp_ip="" local_lan="" local_lan_mask="" vpn_lan="" vpn_lan_mask=""
  • isp_ip - set this to your ip address.  You can find your ip address here
  • local_lan - set this to your network subnet.  *** important!
  • local_lan_mask - usually
  • vpn_lan - go with the default.
  • vpn_lan_mask - go with the default.

Run the playbook

cd ~/raspi-ansible
ansible-playbook -i live vpn.yml
sudo /etc/network/if-up.d/openvpn_firewall
This will configure and start the openvpn server.

It also will create a client file: /etc/openvpn/client.ovpn which can be used to configure the openvpn client.

Home router configuration

  • Forward port 1194 (UDP) to your raspberry pi.


We'll test the vpn connection using a mobile phone, simply because its the quickest way to prove that its working:
  • Install openvpn on your phone.
  • Install google authenticator on your phone.
  • Somehow get the client.ovpn onto your phone. (You could email it to yourself then download it!)

Create VPN user 

For each user of your vpn, you will need to generate a user on the raspberry pi, and a run google-authenticator.  Log onto the raspberry pi using pi/raspberry:

sudo adduser fred (provide password as 'monday', and go with defaults for the remaining of the prompts)
su - fred   (password: monday)
google-authenticator  (Answer y to the questions)
This will generate a QR code. Scan this into your phone using the google authenticator app. There is also a URL, which you could email to the user:

On your phone

  • Disable wifi, to ensure that you are not connecting via your home network. Use mobile data ie. 3g.
  • Open openvpn and import client.ovpn
You will be prompted for a username and password:

username: fred password: monday13520

The password, is fred's password concatenated with the google authenticator verification code provided by the app.

If everything is happy, you should now be connected and able  to navigate to your routers home page ie.:

SSL Certs

You should generate your own certificates and private keys and copy them into /home/vagrant/playbooks/roles/vpn-server/templates/etc/openvpn. Until you do this, a potential hacker would in theory be able to intercept your and inspect your VPN traffic.  On ubuntu I use tinyca2 and on windows XCA to generate my certificates.

Route all traffic through VPN

Edit  /home/vagrant/playbooks/roles/vpn-server/templates/etc/openvpn. Uncomment the line:

push "redirect-gateway def1"


 If things are not working, try connecting using the openvpn client whilst running:

sudo tail -f /var/log/syslog 
sudo tail -f /var/log/auth.log
Look in  syslog for openvpn issues, and auth.log for google authenticator issues.

Good luck!


Raspberry Pi

Latest Raspbian image

Ansible VPN playbook

Windows SSL Certificate Manager

Update (Feb 2016)

1) VPN SSL certificate updated to never expire
2) server.conf updated to point to correct pam module:
3) Added config to optionally route all traffic through vpn


  1. Hello Mark. I followed your instructions and all worked well until "Edit Inventory" my Raspberry does not recognise the command [pi]. the respons is -bash: [pi]: command not found.

    Please advise, since I am totaly new on Linux and Raspberrys

  2. You'll need to edit the inventory file using the command:

    nano live

    (nano is a Linux text editor)
    You will need to update this with your own network config - The only thing you will probably need to change is the local_lan subnet to match your local set up.

  3. Thank you admin for sharing. Though I have idea on how to this, but not all vpn users know about this. Been using VPN for quiet sometime and it does well for security. Keep it up admin!

  4. Ok. really having some trouble! I've done this twice now and have the same problem. OpenVPN server won't start. When i reboot I see:
    Failed to start OpenVPN connection to server
    anyone know how to fix this

  5. Ok. really having some trouble! I've done this twice now and have the same problem. OpenVPN server won't start. When i reboot I see:
    Failed to start OpenVPN connection to server
    anyone know how to fix this

    1. I also get this:

      pi@raspberrypi ~ $ systemctl status openvpn@server.service
      ● openvpn@server.service - OpenVPN connection to server
      Loaded: loaded (/lib/systemd/system/openvpn@.service; disabled)
      Active: failed (Result: exit-code) since Tue 2015-11-03 04:44:14 UTC; 3min 37s ago
      Process: 421 ExecStart=/usr/sbin/openvpn --daemon ovpn-%i --status /run/openvpn/%i.status 10 --cd /etc/openvpn --config /etc/openvpn/%i.conf (code=exited, status=1/FAILURE)


  6. The certificates have expired, and I cannot get new ones to work

    1. cd /usr/share/easy-rsa
      cp openssl-1.0.0.cnf openssl.cnf
      source vars
      ./build-key-server server
      ./build-key client
      cd keys
      cp ca.crt server.crt server.key dh1024.pem /etc/openvpn/
      mv ca.crt ca.cert
      mv server.crt vpn.cert
      mv server.key private.key
      mv dh2048.pem dh1024.pem

      systemctl restart openvpn@server.service

    2. I've renewed the certificate so it never expires. :)

  7. I tried to get OpenVPN working with google auth.

    I have the same issue as reported earlier by Jimi

    pi@raspberrypi:~/raspi-ansible$ systemctl status openvpn@server.service
    รข— openvpn@server.service - OpenVPN connection to server
    Loaded: loaded (/lib/systemd/system/openvpn@.service; disabled)
    Active: failed (Result: exit-code) since Wed 2015-12-30 09:06:57 CET; 3min 19s ago
    Process: 1806 ExecStart=/usr/sbin/openvpn --daemon ovpn-%i --status /run/openvpn/%i.status 10 --cd /etc/openvpn --config /etc/openvpn/%i.conf (code=exited, status=1/FAILURE)

    I have no idea how to fix this. Is there an error in the script?

    1. I'm unable to edit my comment.

      I corrected the issue follwing this fix:

  8. OK. So I commented a bit over a year ago this wasn't working for me. Tried again and it seems to work.

    Suggest an edit to clarify the location of the server.conf. On my system, it is not at:
    but is instead at:

    One issue I'm having is that uncommenting the line:
    push "redirect-gateway def1"

    seems to pretyy much kill internet access. some pages load a bit in a browser, most not at all. Access to PCs on the server local site seem to be ok still.

    1. ok, not so sure about my comment above on the redirect gateway issue.
      Trying to make some changes to the server.conf.
      It seems I have to edit both:
      /etc/openvpn/server.conf and
      for the changes to take. I notice the latter placeholders for the ip addresses:
      server {{vpn_lan}} {{vpn_lan_mask}}

      I can change the vpn lan ip address in /etc/openvpn/server.conf and the vpn will connect, but no traffic seems to get through. I'm guessing the firewall rules don't get updated correctly.

      Where is the master for {{vpn_lan}} and {{vpn_lan_mask}}?

    2. forgot to say, I did try changing the value in /home/raspi-ansible/live and also in /etc/openvpn/server.conf, but traffic doens't get through.