(DRAFT) Two factor authentication with TOTP/HOTP

This is a draft, intended for inclusion into OpenSSH Server as a new section.

Two factor authentication with TOTP/HOTP

For the best two factor authentication (2FA) security, we recommend using hardware authentication devices that support U2F/FIDO. See the previous section for details. However, if this is not possible or practical to implement in your case, TOTP/HOTP based 2FA is an improvement over no two factor at all. Smartphone apps to support this type of 2FA are common, such as Google Authenticator.

Background

The configuration presented here makes public key authentication the first factor, the TOTP/HOTP code the second factor, and makes password authentication unavailable. Apart from the usual setup steps required for public key authentication, all configuration and setup takes place on the server. No changes are required at the client end; the 2FA prompt appears like a password prompt would.

The two supported methods are HOTP and TOTP. Generally, TOTP is preferable if the 2FA device supports it.

HOTP is based on a sequence predictable only to those who share a secret. The user must take an action to cause the client to generate the next code in the sequence, and this response is sent to the server. The server also generates the next code, and if it matches the one supplied by the user, then the user has proven to the server that they share the secret. A downside of this approach is that if the user generates codes without the server following along, such as in the case of a typo, then the sequence generators can fall “out of sync”. Servers compensate by allowing a gap in the sequence and considering a few subsequent codes to also be valid; if this mechanism is used, then the server “skips ahead” to sync back up. But to remain secure, this can only go so far before the server must refuse. When HOTP falls out of sync like this, it must be reset using some out of band method, such as authenticating using a second backup key in order to reset the secret for the first one.

TOTP avoids this downside of HOTP by using the current timezone independent date and time to determine the appropriate position in the sequence. However, this results in additional requirements and a different failure mode. Both devices must have the ability to tell the time, which is not practical for a USB 2FA token with no battery, for example. And both the server and client must agree on the correct time. If their clocks are skewed, then they will disagree on their current position in the sequence. Servers compensate for clock skew by allowing a few codes either side to also be valid. But like HOTP, they can only go so far before the server must refuse. One advantage of TOTP over HOTP is that correcting for this condition involves ensuring the clocks are correct at both ends; an out-of-band authentication to reset unfortunate users’ secrets is not required. When using a modern smartphone app, for example, the requirement to keep the clock correct isn’t usually a problem since this is typically done automatically at both ends by default.

Note

It is not recommended to configure U2F/FIDO at the same time as TOTP/HOTP. This combination has not been tested, and using the configuration presented here, TOTP/HOTP would become mandatory for everyone, whether or not they are also using U2F/FIDO.

Install software

From a terminal prompt, install the google-authenticator PAM module:

sudo apt update
sudo apt install libpam-google-authenticator

Note

The libpam-google-authenticator package is in Ubuntu’s universe archive component, which receives best-effort community support only.

Configure users

Since public key authentication with TOTP/HOTP 2FA is about to be configured to be mandatory for users, each user who wishes to continue using ssh must first set up public key authentication and then configure their 2FA keys by running the user setup tool. If this isn’t done first, users will not be able to do it later over ssh, since at that point they won’t have public key authentication and/or 2FA configured to authenticate with.

Configure users’ key-based authentication

To set up key-based authentication, see “SSH Keys” above. Once this is done, it can be tested independently of subsequent 2FA configuration. At this stage, user authentication should work with keys only, requiring the supply of the private key passphrase only if it was configured. If configured correctly, the user should not be prompted for their password.

Configure users’ TOTP/HOTP 2FA secrets

Each user needs to run the setup tool to configure 2FA. This will ask some questions, generate a key, and display a QR code for the user to import the secret into their smartphone app, such as the Google Authenticator app on Android. The tool creates the file ~/.google-authenticator, which contains a shared secret, emergency passcodes and per-user configuration.

As a user that needs 2FA configured, from a terminal prompt run the following command:

google-authenticator

Follow the prompts, scanning the QR code into your 2FA app as directed.

It’s important to plan for the eventuality that the 2FA device gets lost or damaged. Will this lock the user out of their account? In mitigation, it’s worth each user considering doing one or more of the following:

  • Use the 2FA device’s backup or cloud sync facility if it has one.
  • Write down the backup codes printed by the setup tool.
  • Take a photo of the QR code.
  • (TOTP only) Scan the QR code on multiple 2FA devices. This only works for TOTP, since multiple HOTP 2FA devices will not be able to stay in sync.
  • Ensure that the user has a different authentication path to be able to rerun the setup tool if required.

Of course, any of these backup steps also negate any benefit of 2FA should someone else get access to the backup, so the steps taken to protect any backup should be considered carefully.

Configure the ssh server

Once all users are configured, configure sshd itself by editing /etc/ssh/sshd_config. Depending on your installation, some of these settings may be configured already, but not necessarily with the values required for this configuration. Check for and adjust existing occurences of these configuration directives, or add new ones, as required:

KbdInteractiveAuthentication yes
PasswordAuthentication no
AuthenticationMethods publickey,keyboard-interactive

Note

On Ubuntu 20.04 “Focal Fossa” and earlier, use ChallengeResponseAuthentication yes instead of KbdInteractiveAUthentication yes.

Restart the ssh service to pick up configuration changes:

sudo systemctl try-reload-or-restart ssh

Edit /etc/pam.d/sshd and replace the line:

@include common-auth

with:

auth required pam_google_authenticator.so

Changes to PAM configuration have immediate effect, and no separate reloading command is required.

Log in using 2FA

Now when you log in using ssh, in addition to the normal public key authentication, you will be prompted for your TOTP or HOTP code:

$ ssh jammy.server
Enter passphrase for key 'id_rsa':
(ubuntu@jammy.server) Verification code:
Welcome to Ubuntu Jammy Jellyfish...
(...)
ubuntu@jammy.server:~$

Special cases

On Ubuntu, the following settings are default in /etc/ssh/sshd_config, but if you have overridden them, note that they are required for this configuration to work correctly and must be restored as follows:

UsePAM yes
PubkeyAuthentication yes

Remember to run sudo systemctl try-reload-or-restart ssh for any changes make to sshd configuration to take effect.

Maybe clarify where this should be installed. It’s on the ssh server, correct?

This is also on the ssh server, right?

Can you elaborate a bit more here? The first question I got, for example was if I wanted a time-based token. Maybe explain a bit what these are in an earlier section (difference between totp and hotp).

Also, this would be a good opportunity to mention backing up this QRCODE perhaps, or at least reinforce the importance of saving the backup codes somewhere else.

@ahasenack thank you for the feedback!

I’ve added a note to the top to say that everything is done on the server, rather than making that point in every step.

I’ve added an explanation of HOTP and TOTP to the Background section at the top.

And I’ve given some options on backups for planning for 2FA loss.

Maybe say “Configure the ssh server” instead, as this could mean configuring the server machine.

I’m debating whether it’s worth to add a note here about something. The common-auth include file is what other tools and packages change when dealing with authentication configuration. For example, pam-auth-update is called in libpam-sss.postinst. Maybe we should alert the user that after making the change proposed above, this sort of integration when installing packages will be lost for the sshd service. I know you warned earlier in the text that this is reconfiguring ssh for pubkey + 2fa, and removing password authentication, and that is correct, that’s why I’m debating whether it’s necessary or not to discuss this further.

1 Like

As we discussed out-of-band, if you’re on the fence, I’d like to avoid adding more text as I think it’s already quite long.

Thank you for the feedback and improvements! I’ll put this into the main document now then, and remove this topic.