2 Factor Authentication With SSH
With security becoming more and more of an ongoing issue, which it really has been for a while now, just getting more attention, it is common to see web-based services/companies implement things like 2-factor authentication (2FA). Luckily, implementing 2-factor authentication in SSH is possible with OpenSSH 6.2 or newer and an extra module for PAM. The PAM modules I used were libpam-google-authenticator available on Arch Linux in the AUR for Linux, and oath-toolkit on FreeBSD; both implement event-based HOTP and time-based TOTP algorithms.
Setup on Linux with Google Authenticator libpam plugin
After installing libpam-google-authenticator, either through your distribution’s package manager or from source, there is a final step that needs to be run to complete the installation. As root, run libtool --finish /usr/lib/security. Once that is done, the module needs to be added to PAM, and since we only want to use 2FA for SSH connections, edit /etc/pam.d/sshd and add pam_google_authenticator.so below the system-remote-login lines.
auth include system-remote-login
auth required pam_google_authenticator.so
account include system-remote-login
password include system-remote-login
session include system-remote-login
This will make it so the user will have to give the correct password for the account and a 2FA token to be able to log into the system. Next, make sure that challenge-response authentication is enabled in /etc/ssh/sshd_config, which should look like
ChallengeResponseAuthentication yes
At this point the authentication methods must be set up in /etc/ssh/sshd_config. This can go one of two ways.
- Users with a valid public key can log in without a token.
- Users with a valid public key also have to have a token.
Either way, authentication attempts without a valid public/private key pair will have to supply a password and token to be able to authenticate.
To make it work with a valid public/private key pair and no token, find the AuthenticationMethods section in /etc/ssh/sshd_config and change it to the following;
AuthenticationMethods publickey keyboard-interacive:pam
To make it so a 2FA token is required even for users with a valid key pair, set the Authentication Method line to be:
AuthenticationMethods publickey,keyboard-interacive:pam
Finally, the secret key file needs to be created so it can be added to your OTP generator. This is done by running google-authenticator as the user you want to enable 2FA for; it will ask a few questions and then generate the secret key that you put in your generator, as well as generate a handful of emergency “scratch” codes. If qrencode is installed, it will also generate a scannable QR code.
$ google-authenticator
Do you want authentication tokens to be time-based (y/n) y
<Here you will see generated QR code>
Your new secret key is: ZVZG5UZU4D7MY4DH
Your verification code is 269371
Your emergency scratch codes are:
70058954
97277505
99684896
56514332
82717798
Do you want me to update your "/home/username/.google_authenticator" file (y/n) y
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) y
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 (/n) n
If the computer that you are logging into is not 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) y
Once that is done, restart sshd and enjoy!
Setup on FreeBSD with OATH-Toolkit PAM plugin
Note: It has recently come to my attention that the libpam-google-authenticator plugin also will work under FreeBSD, but since that was not known to me at the time of setting this up, I’ll continue to document using OATH-Toolkit. I assume using the Google-developed plugin will require the same setup as it did under Linux to work.
Once OATH-Toolkit has been installed, either through pkg or from ports, PAM needs to be set up to load the correct module. As root, edit /etc/pam.d/sshd and remove the pam_opie modules and add the pam_oath module.
auth requisite pam_uix.so no_warn try_first_pass
auth required /usr/local/lib/security/pam_oath.so usersfile=/usr/local/etc/users.oath
Once PAM has been set up, the pam_oath module requires some configuring itself before it can function. The usersfile that was specified in /etc/pam.d/ssh needs to be created with an entry for each OATH user. Each entry uses the following format:
token type, username, optional password, secret
A simple way to generate a secret and add the entry to the file is with
SECRET="$(head -c 1024 /dev/urandom | openssl sha1)"
echo "HOPT/T30/6 username - $SECRET" >> /usr/local/etc/users.oath
chmod 600 /usr/local/etc/users.oath
chown root /usr/local/etc/users.oath
The same secret used for OATH is the secret key that has to be used in your authenticator app. The problem here is that OATH expects the secret to be hexadecimal, while most token generators, like Google Authenticator, expect the secret to be Base32 encoded. The hexadecimal secret can be converted to Base32 with:
oathtool -v -totp $SECRET | grep Base32 | awk '{ print $3; }'
That will return a Base32-encoded secret that can be used to configure your token generator.
Once both the server and client sides are configured, it is highly recommended to store a copy of the secret key and the “scratch” codes, if using, in a safe and secure place. There is no way to recover the secret key if lost.
Comments
No comments yet. Be the first to share your thoughts!
Leave a comment