I’ve been playing with personal security lately and trying to get OATH-TOTP to work on my private server. This post is a tutorial of how to get the more interesting features implemented.
OATH-TOTP (not related to OAuth) is a standardized way of providing 2-factor authentication (2FA) with tokens that change at predetermined intervals. You may be familiar with the Google Authenticator app, which implements the client side of the spec. A wide range of services implement the server-side part - from Battle.net, through Google, to Facebook and Dropbox.
This post will describe how you too can implement TOTP 2FA for SSH logins to your servers.
As part of the Authenticator project Google released a PAM (Pluggable Authentication Module) implementation of the 2-factor system. This is readily available and packaged as
libpam-google-authenticator on Debian/Ubuntu.
Once installed, you can run
google-authenticator to generate your user’s secret and configuration settings. Just follow the prompts and scan the ASCII QR code to add it to the Authenticator app.
You can then add
auth required pam_google_authenticator.so to
/etc/pam.d/sshd. You’ll also need to have
ChallengeResponseAuthentication yes in your
This way, when you log in with a password, the system will request your one-time token as well.
The reason this is less than ideal is that any sufficiently hardened setup already disables password logins. What I wanted in my particular case was a public key login with a mandatory second factor. Turns out, however, that public key login completely bypasses challenge-response login.
After some searching, I found the
sshd_config parameter. Adding the line
sshd_config did indeed prompt me for my verification code. But not before prompting me for my password as well. This was a bit too much, so I set out to limit the login sequence to just a prompt for the code.
The reason this was happening was that
publickey,keyboard-interactive:pam required a successful complete PAM run before letting you proceed onwards. My PAM config (in
/etc/pam.d/sshd), however, was asking for a password and I’d added the line at the end to also ask for a verification code.
I moved the line to the beginning of the file and changed it so that it is enough to log you in:
auth sufficient pam_google_authenticator.so
However, pressing Ctrl+C or entering an empty code at the 2FA prompt still dropped me to a password prompt. This would clearly not do.
sufficient is an alias for
[success=done new_authtok_reqd=done default=ignore]. Notice that the default action is
ignore. That is, the module is sufficient to authenticate me but not required.
The final solution was to prepend the following to my
auth [success=done new_authtok_reqd=done default=die] pam_google_authenticator.so nullok
nullok option means that if the user doesn’t have 2FA set up, the module will still let you through.
I also left the rest of
/etc/pam.d/sshd intact, even though it’s unnecessary now - the 2FA module either kills authentication or declares it complete, nothing else should ever execute. That said, PAM is magic and I’m no wizard.
I hope I freed someone’s afternoon for better things than fiddling with
P.S.: Always have a backup SSH session open, in case you screw up any part of the configuration. I learned the hard way.