In my prior post I made the case against a rotating password policy and suggested two-factor authentication as a password policy that worked. Two-factor authentication requires both a password that is memorized and an item you have to verify that you are who you say you are.
Two-factor authentication doesn’t have to be expensive. In fact, Google has developed a solution that makes use of the smart phone to generate time-based, one-time verification codes that are unique to each individual user and the machine they are logging in to. It’s called Google Authenticator.
Installing it on FreeBSD is simple, but it probably won’t behave as you intended out of the box. I’ll walk you through installing it and tailoring it to your needs.
Step 1: Install libqrencode. This is optional, but you’ll be happy you did when you see the QR code in your SSH terminal to be scanned by your smartphone. This doesn’t have to be installed first, in fact, you can install it after Google Authenticator is installed as well.
cd /usr/ports/graphics/libqrencode make && make install |
Step 2: Install Google Authenticator.
cd /usr/ports/security/pam_google_authenticator make && make install |
Step 3: Download the Google Authenticator app to your smart phone. Sorry, I can’t help you with this step. Search for it in the market place, it’s free.
Step 4: As the user you’d like to generate a key for, run the program (on your FreeBSD machine):
google-authenticator |
Choose ‘y’ for yes to update your .google_authenticator file and scan the QR code into your smartphone’s Google Authenticator app.
Step 5: Configure your OpenPam config file. Lets set it up to work for SSH… Using your favorite editor add the following line to the bottom of your /etc/pam.d/sshd file:
auth optional /usr/local/lib/pam_google_authenticator.so |
You’re “done!” If you’ve setup Google Authenticator for a particular account it will prompt you for the verification code that your smartphone now generates.
Problems:
You’ve noticed that we used “optional” in the pam config file. This means the use of it is, well, optional. Not only can users login who have not setup Google Authenticator yet, but users who have set it up can get away without using it by leaving the verification code blank after being prompted for it.
If you want to require Google Authenticator for your users you can change that to “required.” However, you then face another problem: users who havn’t set it up yet will not be able to login.
The desired effect should be that users who have set it up are required to use it, but users who have not set it up can get away without it (at least during early deployment).
If you used LinuxPam, this would be easy. LinuxPam allows for conditional statements within the pam config file that upon matching, the specified number of following modules could be skipped. OpenPam, used by FreeBSD, to my knowledge doesn’t have that feature. So, to get the intended functionality we can apply a simple patch to pam_google_authenticator.c.
Warning: I suspect Google didn’t do it this way because with this patch, by setting the control-flag in the pam configuration file to “sufficient” you will essentially open a gapping security hole that allows anyone to log in without a password or a verification code to an account where google-authenticator is not setup. You have been warned. With this patch, only use the “required” control-flag in the pam configuration.
Modify pam_google_authenticator.c by adding the following code just above the ‘// Clean up’ comment:
if ((rc != PAM_SUCCESS) && (secret_filename != NULL) && (access(secret_filename, F_OK) == -1) && (errno == ENOENT)) { log_message(LOG_ERR, pamh, "No config file found, skipping authentication"); rc = PAM_SUCCESS; } |
It’s pretty straight forward. If the secret_filename (by default .google_authenticator) doesn’t exist in the users home directory, then return the PAM_SUCCESS return code, thus satisfying the two-factor authentication requirement for users who have not set it up.
Enjoy. If you have trouble re-compiling with the changes, post a comment.
Randy,
Thanks for the great writeup! I also changed ‘pam_unix.so’ from “required” to “requisite.” This requires a valid password before ever prompting for a verification code.
Cheers!
-Scott
Is there a way to use this with pubkey authentication? Could only get it to work if I turn on password based authentication.
@John:
`keyboard-interactive` in sshd_config is anything passing something like a password on to PAM or whatever checking the credentials. Nowadays you can use AuthenticationMethods in sshd_config to chain PubKey and PAM (the later one using the google authenticator): “AuthenticationMethods publickey,keyboard-interactive” requires a valid pubkey before asking for anything else and as “keyboard-interactive” get’s passed to PAM you can use
auth sufficient /usr/local/lib/pam_google_authenticator.so
auth required pam_unix.so no_warn try_first_pass
in /etc/pam.d/ssh to let users first try the Google authenticator with password as fallback, so
successful_login = valid pubkey AND (Google Authenticator OR password)
Just tested on FreeBSD 9.2.
Oh, and make sure to enable ntpd in /etc/rc.conf before trying this…