Search

Two Factors Are Better Than One

Although I've always been interested in security, there are just some security
measures I've never liked. SSH brute-force attacks end up being a major way that
attackers compromise Linux systems, but when it comes to securing SSH, I've never
been a fan of changing your SSH port to something obscure, nor have I
liked scripts like fail2ban that attempt to detect brute-force attacks and block
attackers with firewall rules. To me, those measures sidestep the real issue:
brute-force attacks require password authentication. If you disable password
authentication (set PasswordAuthentication to no in your sshd_config) and
use only SSH keys, you can relax about all those brute-force attacks knocking on your
door.

In a past article ("Secret Agent Man", December 2013), I wrote about why you should
set a passphrase on your SSH keys and how to use SSH Agent to make
password-protected keys a bit less annoying. In one respect, you can think of
password-protected SSH keys as a form of two-factor authentication. The key is
something you have, and the password is something you know. The problem, however,
is that if you host a system with multiple users, you can't enforce
password-protected SSH keys from the server side. So in this article, I
discuss how to add two-factor authentication to an SSH server that accepts
only keys.

These days, more services on-line offer two-factor authentication (2FA) as an extra
layer of security on top of a user name and password. After you perform your
normal authentication, you provide your 2FA token (usually a string of digits)
that authenticates you. Although in the past, 2FA required you to carry around a
special hardware dongle, these days, a number of software approaches
can use your cell phone instead. Some approaches use TOTP (Time-based One-Time
Password), so your phone just needs accurate time but no network to function.
Other approaches use push notifications, SMS or even a phone call to share the
2FA token, and some implementations can use all of the above.

Some 2FA SSH implementations work via the ForceCommand directive placed in the
SSH configuration for a particular user and let you enable 2FA on a per-user
basis. Others offer a PAM module you can add system-wide (and use for sudo
authentication as well as SSH). Although a number of excellent 2FA SSH
implementations exist for Linux, I've chosen Google Authenticator for a few reasons:

It's free, and the source is available.

It's been available and tested for a number of years.

Packages are available for a number of distributions.

Clients are available for a number of phone operating systems.

It uses a custom PAM module, so it's easy to add 2FA system-wide.

It provides a backup in the form of backup codes in case users lose or wipe
their phones.

Install Google Authenticator

As I mentioned, Google Authenticator is packaged for a number of distributions,
so,
for instance, on Debian-based systems, you can install it with:

$ sudo apt-get install libpam-google-authenticator

If for some reason it isn't packaged for your distribution, you also can just go
here, download
the software and make and install it according to the documentation there. You
also will need to install the Google Authenticator app on your phone.

Configure User Accounts

I recommend setting up Google Authenticator for all of your user accounts (or at
least all of the sysadmin accounts) before enforcing 2FA in SSH to make it easier
to enroll all of the users and avoid the risk of locking people out. To configure
Google Authenticator, each user needs to log in and run google-authenticator. You
will be presented with a series of questions where it's safe to answer
"y";
however, I generally answer no to extending the time window to four minutes, and
I also answer no to rate limiting, since as I disable password authentication, I'm
less concerned with brute-force attacks. The output looks something like this:

$ google-authenticator
Do you want authentication tokens to be time-based (y/n) y
https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl
↪=otpauth://totp/username@debian%3Fsecret%3D4SK2LTLCTLCEV757
QR Code Removed
Your new secret key is: 4SK2LTLCTLCEV757
Your verification code is 221544
Your emergency scratch codes are:
53267360
44975412
59302752
36003899
64736155
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 (y/n) n
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) n

If you have libqrencode installed, the output also will contain a QR code in the
console you can scan with the Google Authenticator app on your phone. Otherwise,
you simply can enter the secret key into your Google Authenticator application on
your phone. Also, be sure to write down those backup codes and store them in a
safe place. These are one-time-use codes you can use to get back in to the system
in case you ever lose or wipe your phone. Once you are logged back in, you can run
google-authenticator again.

Configure PAM and SSH

Once your phone and user accounts are configured with Google Authenticator, you
are ready to enforce 2FA in PAM and SSH. To do this, edit your /etc/pam.d/sshd
file and add the following to the top of the file:

auth required pam_google_authenticator.so

On my Debian system, I noticed that once I finished the
configuration process, I would not only be prompted for my 2FA token, I'd also
be prompted for my local system password. Because I wasn't interested in
three-factor authentication (two-and-a-half factor authentication?), I noticed I
needed to comment out the following further down in the file:

@include common-auth

Of course, if you aren't on a Debian-based system, this
extra step may not be necessary.

The final step is to configure SSH. Hopefully you already have disabled password
authentication for SSH in the past, and if not, I recommend you consider it. Most
of the SSH 2FA guides out there (this one included) will tell you to enable
ChallengeResponseAuthentication in your /etc/ssh/sshd_config:

ChallengeResponseAuthentication yes

I noticed, however, that when you are using key-based authentication instead of
passwords, you need to add an additional setting to the config file:

AuthenticationMethods publickey,keyboard-interactive

Once these settings are in place, you can enable them by restarting your SSH
service, which depending on your system may be one of the following:

$ sudo service ssh restart
$ sudo service sshd restart

After SSH has restarted, you should get an additional prompt the next time you SSH
to the server:

Type in the verification code that shows up in your Google Authenticator phone
app, and you can log in. The nice thing about adding 2FA to SSH is that it
provides an additional means of protection in case your computer is ever
compromised or stolen. Attackers also would have to compromise or steal your
phone before they could access your systems.

Kyle Rankin is a Tech Editor and columnist at Linux Journal and the Chief Security Officer at Purism. He is the author of Linux Hardening in Hostile Networks, DevOps Troubleshooting, The Official Ubuntu Server Book, Knoppix Hacks, Knoppix Pocket Reference, Linux Multimedia
Hacks and Ubuntu Hacks, and also a contributor to a number of other O'Reilly books. Rankin speaks frequently on security and open-source software including at
BsidesLV, O'Reilly Security Conference, OSCON, SCALE, CactusCon, Linux World Expo and Penguicon. You can follow him at @kylerankin.