SSH is actually a lot more flexible than you might think for two-factor authentication, and you can use standard tools like Time-based One-Time Passwords (TOTP) and even hardware security keys to secure your logins without needing specialized SSH server modules.

Let’s see it in action. Imagine you’re logging into a server. Your normal ssh user@your.server.com command will suddenly prompt you for more.

$ ssh jdoe@prod.example.com
Password:
Verification code:

The "Verification code" prompt is where the magic happens. This isn’t some proprietary system; it’s your standard TOTP authenticator app (like Google Authenticator, Authy, or even built-in ones on some phones) or a plugged-in hardware security key (like a YubiKey).

Here’s how it all shakes out under the hood.

The Problem SSH Solves (and How 2FA Fits In)

Traditionally, SSH relies on passwords or public-key cryptography for authentication. Passwords are, well, weak. Public-key crypto is strong but can be a pain to manage across many devices, and it doesn’t protect against a compromised private key on a device you are using. Two-factor authentication adds a layer: something you know (password) and something you have (your phone with an authenticator app, or a hardware key).

The brilliance here is that SSH itself, specifically the sshd daemon, has a built-in PAM (Pluggable Authentication Modules) interface. PAM is the standard Linux way of handling authentication, and it’s designed to be extensible. We’re going to configure sshd to use PAM, and then configure PAM to require a TOTP or hardware key code after the initial password or public key authentication.

Setting Up TOTP

This is the most common second factor.

1. Install google-authenticator (or libpam-google-authenticator on Debian/Ubuntu):

sudo apt update && sudo apt install libpam-google-authenticator
# or
sudo yum install google-authenticator

2. Run google-authenticator on the client:

You’ll run this command as the user who will be logging in.

$ google-authenticator

This will output a QR code (which you scan with your authenticator app), a secret key, a verification code, and emergency scratch codes. Save the scratch codes somewhere safe.

3. Configure PAM on the server:

You need to edit your PAM configuration file. The exact file depends on your distribution, but it’s usually /etc/pam.d/sshd.

Add this line near the top, but after any nullok or debug directives for pam_unix.so if you’re using password authentication, or before pam_unix.so if you’re using public key auth and want the TOTP after the key. For a typical password + TOTP setup, it would look like this:

# /etc/pam.d/sshd
# ... other lines ...
auth required pam_unix.so nullok
auth required pam_google_authenticator.so forward_pass
# ... other lines ...
  • auth: Specifies that this module handles authentication.
  • required: This module must succeed for authentication to proceed. If it fails, the user is denied, but subsequent modules will still run (useful for logging).
  • pam_google_authenticator.so: The module we installed.
  • forward_pass: This is crucial. It tells pam_google_authenticator to prompt for the TOTP after the primary authentication (password or public key) has succeeded. Without it, it would try to use the same password field for the TOTP, which wouldn’t work.

4. Configure SSH daemon:

You need to tell sshd to use PAM. Edit /etc/ssh/sshd_config:

# /etc/ssh/sshd_config
ChallengeResponseAuthentication yes
UsePAM yes
  • ChallengeResponseAuthentication yes: This enables the interactive prompts that pam_google_authenticator.so uses.
  • UsePAM yes: This explicitly tells sshd to use the PAM stack.

5. Restart sshd:

sudo systemctl restart sshd
# or
sudo service ssh restart

Now, when you ssh, you’ll be prompted for your password, and then for the verification code from your app.

Setting Up Hardware Keys (U2F/FIDO2)

This is a bit more involved, as it typically requires sshd to be compiled with specific support or to use a proxy/gateway. However, a common and powerful way to use hardware keys is via pam_u2f or pam_fido2.

1. Install pam_u2f or pam_fido2:

sudo apt update && sudo apt install libpam-u2f
# or
sudo yum install pam_u2f

2. Register your hardware key:

This is done on the client machine as the user.

pamu2fcfg -u jdoe > ~/.config/google-authenticator/u2f_keys
# or for FIDO2
pamu2fcfg -u jdoe -f > ~/.config/google-authenticator/fido2_keys

When prompted, touch your hardware key. This creates a file (e.g., ~/.config/google-authenticator/u2f_keys) containing the key’s registration information.

3. Configure PAM on the server:

Similar to TOTP, edit /etc/pam.d/sshd. You’ll add a line for pam_u2f.so or pam_fido2.so. This typically goes after your primary authentication.

# /etc/pam.d/sshd
# ... other lines ...
auth required pam_unix.so nullok
auth required pam_u2f.so authfile=/home/jdoe/.config/google-authenticator/u2f_keys # or pam_fido2.so
# ... other lines ...
  • authfile: Points to the file created in step 2.

Important Note for pam_u2f: If you want to use it alongside TOTP, you’d add both pam_google_authenticator.so and pam_u2f.so lines. The order matters: pam_google_authenticator.so would prompt for the code, then pam_u2f.so would prompt for the hardware key.

4. Configure SSH daemon:

Ensure ChallengeResponseAuthentication yes and UsePAM yes are in /etc/ssh/sshd_config, as described for TOTP.

5. Restart sshd:

sudo systemctl restart sshd

Now, after your password or public key, you’ll be prompted to touch your hardware security key.

The One Thing Most People Don’t Know

The forward_pass option for pam_google_authenticator.so is a game-changer. Without it, you’d have to get creative with PAM configuration to make the TOTP prompt appear in the same interactive session as your password or public key prompt. forward_pass neatly hijacks the next auth prompt that the SSH client makes, effectively chaining the authentication steps without needing complex PAM logic or custom SSH server builds. It’s the glue that makes the standard TOTP apps work seamlessly with sshd’s challenge-response mechanism.

The next hurdle you’ll likely encounter is dealing with users who forget their scratch codes or lose their authenticator device, leading to lockout scenarios that require manual intervention via root.

Want structured learning?

Take the full Ssh course →