If you recall from part 5 of my LXC tutorials I discussed out you can set up OpenVPN and create a dedicated virtual instance for that service, as well as how to successfully connect to it. Now I want to cover adding a bit more security by using Google Authenticator to provide 2-factor authentication. Assumptions (Things You Know/Have) This guide assumes you followed up to part 5 so far and have a working instance of OpenVPN you can log into. This will also assume you have the essential programs (g++, make, libpam, libpam development files) installed as we will have to compile the Google Authenticator module by source. Your package repo might work, but for Ubuntu 12.04 LTS it did not. This will also only work if you have a smartphone that is capable of running the Google Authenticator app (iPhone and Android do, BlackBerry might). Sorry, but most people have one or can get access to one pretty easily. Things You Will Learn While this may not be the most elaborate tutorial or most in-depth, I try to bring a sense of security and its purpose to my articles. This is the first one I’ve written that is purely dedicated to security measures. You will also learn how to compile most programs from source (if you haven’t done so already). Getting the Source The first thing we need to do before anything is download the archive that has the source. I’m a personal fan of wget but if you use curl or some other downloader you can use that instead. Code: cd /tmp wget https://google-authenticator.googlecode.com/files/libpam-google-authenticator-1.0-source.tar.bz2 tar -xf libpam-google-authenticator-1.0-source.tar.bz2 cd libpam-google-authenticator-1.0 The code hasn’t been updated since April 2012 as of this writing so you might want to check for a more recent version, but this is the version I used. Editing the Source This is the major reason why we couldn’t just use the compiled version from the package manager. While it exists in Ubuntu and such, it wasn’t compiled with PAM support, which is basically a modular system Linux can use to authenticate and perform other tasks. In turn, this makes using Google Authenticator pointless and moot. So, we have to enable PAM support, which is thankfully very easy. Somewhere near the top (most people will say before or after “VERSION := 1.0”) put the following line there: Code: LDFLAGS="-lpam" This will be about the biggest change we make to anything. This tells the compiler and linker (which basically creates the PAM module and binaries) to include PAM support, which is also why we need the PAM development files (i.e.: libpam-dev on Debian-based systems). After this, simply run the following commands: Code: ./configure make sudo make install This will install the PAM module (for me) in the directory /lib/x86_64-linux-gnu/security/ and the google-authenticator binary in /usr/local/bin/ which we will need shortly. Setting Up The System We got most of the work done now so lets get this going. Unfortunately in the last part we didn’t do something that we need to here, and that is create a user on the system. Since we’ll be using Google Authenticator to provide a more secure login we need to also have a home directory for the user (GA writes to ~/.google_authenticator). Now, to showcase a neat little trick later on in this guide we’ll create the user with as the CN of our certificate (here we’ll assume it’s “bob”): Code: sudo useradd -m bob sudo passwd bob We set a password for bob so we can make things even more secure (this will all make sense later on but for now just use a simple password for testing purposes). Setting Up Google Authenticator For bob Now, while we have bob in our system, we need to set up Google Authenticator for them now. Instead of logging in as bob, lets just drop into a quick shell in their name: Code: sudo su -c “google-authenticator” bob This switches users to bob and runs the command “google-authenticator” as the user (bob), and once the program is finished you’re back to your own shell. You’ll be asked to answer a few questions. I chose yes to all of them but I’ll go into what they mean and why I chose yes. Code: Do you want authentication tokens to be time-based (y/n) Chose yes. This basically means that the tokens (the 6 digits you enter to authenticate) will be refreshed every 30 seconds. Otherwise the tokens will change once you use it. This, from my experience, has no real ramifications with the VPN and you won’t be continuously prompted to log in every 30 seconds as the session will be established. Side Note: The QR code At this point you’ll receive some information similar to this: Code: https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/bob@vpn%3Fsecret%3DFBSOFLJF5OWHDOMU Your new secret key is: FBSOFLJF5OWHDOMU Your verification code is 514303 Your emergency scratch codes are: 33787359 88277496 68555403 41114417 92152374 If you have libqrencode (or similar) installed you’ll see a QR code in place of the URL. But, if not then just copy/paste the URL into your browser, and then either way scan the barcode using your phone after you launch the Google Authenticator app on your phone. Now, back to the rest of the guide. Code: Do you want to disallow multiple uses of the same authentication token? This restricts you to one login about every 30s, but it increases your chances to notice or even prevent man-in-the-middle attacks (y/n) If it wasn’t for OpenVPN being a SSL-based VPN system, the authentication data is encrypted from end to end, and that both the username and password are masked when using the CLI client I would strongly suggest saying yes, as someone could sniff out your token and try to log in as you. However, due to all the precautions I just choose yes to be on the safe side (overly paranoid? most likely). Code: By default, tokens are good for 30 seconds and in order to compensate for possible time-skew between the client and the server, we allow an extra token before and after the current time. If you experience problems with poor time synchronization, you can increase the window from its default size of 1:30min to about 4min. Do you want to do so (y/n) Server time can and most likely will change over the course of time, even if by a small amount. With passwords being time-based (assuming you chose “y” to the first question), it is possible that even after entering the correct token information the server will reject you due to a conflict in time (same reason why RSA fobs get out of synch). Code: If the computer that you are logging into isn't hardened against brute-force login attempts, you can enable rate-limiting for the authentication module. By default, this limits attackers to no more than 3 login attempts every 30s. Do you want to enable rate-limiting (y/n) Whenever possible I prefer to enable rate limiting anyways. Basically you can make it so GA doesn’t allow more than 3 login attempts every 30 seconds (when the token refreshes). If you worry about main-in-the-middle attacks or someone stealing your information this might be helpful. Usually rate limiting is used to reduce the possibility of a brute force attempt. Setting Up PAM While setting up Google Authenticator was pretty easy, the rest of the guide is the same! Now we’ll set up PAM (and make some adjustments later) to make sure everything is peachy. As a side note, this set up for OpenVPN can be used for SSH and normal system logins as well (just an idea ). First we’ll copy the “common-account” file in /etc/pam.d/ and create our own OpenVPN PAM profile: Code: cd /etc/pam.d cp common-account openvpn Now lets open it up in our editor and add this line at the bottom, then save & exit: Code: auth required pam_google_authenticator.so Save that, now we’ll open up our OpenVPN server config file (/etc/openvpn/server.conf) and add a new line in there at the bottom as well: Code: plugin /usr/lib/openvpn/openvpn-auth-pam.so openvpn The important thing to note here is the last part (“openvpn”). This tells the plugin which PAM profile to load (any of the files found in /etc/pam.d/). So in theory you can share profiles between SSH and OpenVPN if you wanted, but its always better to separate things whenever possible. Now restart the OpenVPN server (service openvpn restart) and now make one change to the ciient’s config file, adding the following line at the end: Code: auth-user-pass This is what will make OpenVPN prompt us to log in. Now once you start OpenVPN with the client profile it should ask you for a username and password. The username is what you provided earlier (“bob” in this case) while the password is the 6-digit code/token in the Google Authenticator app. If all goes well you should see the normal status that a session’s been initiated and that you got a tun adapter going with a 10.8.0.0/24 IP. Other Ways to Be More Secure Require System Password and Token What we’ve done so far is nice, but why not make the user actually know their information? Lets make it so they have to also type in their password to log in to the VPN system. This only requires a small change, and that’s to our PAM profile (/etc/pam.d/openvpn), so open it up and go down to the bottom line again, and make the following changes. Change the last line from: Code: auth required pam_google_authenticator.so To: Code: auth requisite pam_google_authenticator.so forward_pass Add the following line after that: Code: auth required pam_unix.so use_first_pass ]The change we made to the last line basically means that instead of it being required to log in, its a requisite to continue the chain of events. So basically if the token (which is stripped from the last 6 characters of the sent password) doesn’t match, it won’t bother trying to authenticate via the system. “forward_pass” I can only guess forwards the returned password. As a side note: PAM works by storing the provided password in memory, from what I can guess the Authenticator module strips out the last 6 characters and uses that as the token. The second line we add (involving pam_unix.so) is the common “authenticate via /etc/shadow” module. This will get the password after Google Authenticator is done with it, essentially retrieving the user’s password, and check it against /etc/shadow. If it matches, you’re considered a real user, otherwise VPN access is rejected. You also don’t have to restart OpenVPN again as we didn’t change the server config, just the PAM settings, which gets reloaded temporarily when a new session is requested anyways. So next time you try to log in, your password will be “system_password+authenticator_token”. So, if bob’s password is “password” and the token is “123456”, you’d enter “password123456” without quotes. Use CN As Username This does require you to restart your OpenVPN server and doesn’t really add security but provides a lazier way of logging in, lets go! Its really simple, just add these two lines to the end of your server config: Code: auth-user-pass-verify username-as-common-name This will make it so when you’re prompted for the username you just hit enter and OpenVPN will use the value of your CN field to store the username. Pretty lazily cool, huh?