(Edited 5/8/2022 with some changes to make sure it works on Buster / other linux distributions with sec-linux. The new stuff is in purple.)

It took me a while but I finally found someone that had solved this. I am linking the solution. However, typing in a password and following it up with the one-time-password (OTP) is *extremely* user unfriendly. Anything that is hard to do to make better security actually makes worse security. Instead my approach protects the private keys with a password, and you then only use the OTP as the user’s password each login.

So, here is the process. Assuming you have pivpn already installed and working with an OpenVPN configuration.

  1. Install google authenticator on the pi: sudo apt-get install libpam-google-authenticator
  2. Edit your openvpn server configuration: sudo nano /etc/openvpn/server.conf and add plugin /usr/lib/openvpn/openvpn-plugin-auth-pam.so openvpn (to use google authenticator) and reneg-sec 0 (to not reconnect every x minutes as the password changes every few seconds).
    • NOTE: This will make this server configuration only work with OTP. If you have accounts that will just be using passwords then you will need to have a separate server configuration and separate port for that. Info on how to do that is here.
    • NOTE 2: If you are on Buster, or some version of Linux where the openvpn-plugin-auth-pam.so is not in that location, you should link it into that location. For example in Buster, this would be the command you would run to create the appropriate link: sudo ln -s /usr/lib/arm-linux-gnueabihf/openvpn/plugins/openvpn-plugin-auth-pam.so /usr/lib/openvpn/openvpn-plugin-auth-pam.so 
  3. Create a pam.d openvpn profile: sudo cp /etc/pam.d/common-account /etc/pam.d/openvpn
  4. Edit it sudo nano /etc/pam.d/openvpn to add this line at the end: auth required pam_google_authenticator.so 
  5. Newer versions of linux with sec-linux use a more strict sandboxing config in systemd which interferes with google-authenticator. To get past this, we will need to edit /lib/systemd/system/openvpn@.service and remove this line to make sure it can read the .google_authenticator files in the home directories of the accounts we create:
  6. Now run sudo service openvpn restart to reload the configuration change.

Now, create your user. For this to work you will use system accounts (accounts you use to log to your raspberry like ‘pi’). You can create as many account as you with the sudo adduser username command. The user’s password really doesn’t matter. Once you’ve created the user:

  1. login as the user on the raspberry pi: sudo su - username (replace username with the actual username)
  2. run the google-authenticator command and follow the instructions (save the barcode url for next step, or import it directly on the user’s device at that time)
  3. Type exit to get out of that user’s shell and return to your own. 
  4. Executing google-authenticator adds a file .google_authenticator in the user’s home directory. This file must have no rights except read for the user, so run sudo chmod 400 /home/username/.google_authenticator (change to the correct username)
  5. create a pivpn account with the exact same name as the user : pivpn -a Note: the username must be the same than the system account. (The original directions suggest doing this with no password; It is safer to use a password to protect the private key. The password used here will need to be communicated safely to the user)
  6. edit the freshly created username.ovpn file and add the lines auth-user-pass (to tell the client to request username and password on connection) and reneg-sec 0 (to not reconnect every x minutes as the password changes every few seconds) And comment out the auth-nocache line by putting a # at the front of it. (This will keep the connection from re-negotiating every 60 minutes which is not good for an always-on VPN.)

Now, just install your .OVPN file on your client. (You can save the private key password if your client supports it, or require prompting for it every time.) Use the barcode URL generated earlier to show the QR Code for import into your authenticator app on your mobile device, and profit! 
Login with the same username and the OTP as the password. (The private key password being the one used when you created the account with the pivpn -a command.) You’re now using multifactor authentication!. Something you know (the private key password) and something you have (your authenticator app which is a one-time-password generator).

2 thoughts on “PIVPN with 2-Factor Authentication

  1. Good way of telling, and pleasant post to get data concerning my presentation subject matter, which i am going to convey in academy.

  2. redyooy says:

    Hi, thanks for the great post, I followed the steps, the “openvpn-plugin-auth-pam.so” file is located at “/usr/lib/arm-linux-gnueabihf/openvpn/plugins/openvpn-plugin-auth-pam.so” for me.
    Also, I am getting authentication failed msg when trying to connect to the server from the client. any help is appreciated. thanks

Comments are closed.