With security becoming more and more of an ongoing issue, which it really has been for a while now just being getting more attention now, it is common to see web based services/companies implement things like 2 factor authentication (2FA). Luckily implementing 2 factor authentication is SSH is possible with openSSH 6.2 or newer and a extra module for [PAM] (https://en.wikipedia.org/wiki/Pluggable_authentication_module “Pluggable Authentication Module”). The PAM modules I used was [libpam-google-authenticator] (https://github.com/google/google-authenticator/tree/master/libpam “Google Authenticator PAM Plugin”), 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 Link to heading
After installing libpam-google-authenticator
, either through the your
distributions package manager or from source, there is a final step that needs
ran 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 needs to be setup in /etc/ssh/sshd_config this can go one of two ways:
- users with a valid public key can login without a token,
- users with a valid public key also has 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 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 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 Link to heading
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 for
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
setup 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 setup, 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 needs 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, expects 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 side is configured it is highly recommeneded to
store a copy of the secret key, and the “scratch” codes if using
libpam-google-authenticator
, in a safe and secure place as there is no way to
recover the secret key if lost.