← News
/PLAYBOOK · 2026-08-12

2FA Hands-On: TOTP One-Time Passwords and SSH Login Hardening

SSH password brute-force is hard to stop. 2FA is the most effective hardening. This article covers TOTP internals and a full Google Authenticator + PAM setup for SSH two-step verification, plus a recovery plan.

Use Cases

2FA applies when: SSH passwords get brute-forced or credential-stuffed; leaked employee passwords let attackers into internal systems; compliance requires OTP on critical systems (e.g. MLPS 2.0 Level 3 mandates 2FA). This guide starts from TOTP internals and gives a complete Linux SSH setup with Google Authenticator, PAM, and a recovery plan.

Prerequisites

How It Works

TOTP (Time-based One-Time Password) is the mainstream 2FA method. Core: shared secret + time window. Server and client hold the same Base32-encoded secret. Both compute a one-time code with HMAC-SHA1 over the timestamp, 30-second period. Each login needs something you know (password) + something you have (phone). Stolen password is not enough without the OTP. On Linux, PAM (Pluggable Authentication Modules) inserts TOTP into the SSH auth chain: password + OTP.

Steps

1. Install and initialize google-authenticator

# Ubuntu
sudo apt update && sudo apt install -y libpam-google-authenticator

# CentOS
sudo yum install -y epel-release
sudo yum install -y google-authenticator

# Generate secret for current user (answer y to all prompts)
google-authenticator

Key params: back up the QR code and 16-char Base32 secret from init. `~/.google_authenticator` must be mode 600; it holds the secret and scratch codes.

2. Wire PAM into SSH

# Edit /etc/pam.d/sshd; add at top
# Ubuntu
auth required pam_google_authenticator.so

# CentOS: also comment out the leading auth substack password-auth line
# then add:
auth required pam_google_authenticator.so

3. Enable challenge in sshd_config

# Edit /etc/ssh/sshd_config
UsePAM yes
ChallengeResponseAuthentication yes      # old (CentOS7)
KbdInteractiveAuthentication yes        # new (Ubuntu22+)
AuthenticationMethods "password,keyboard-interactive"   # password + OTP, two-step

Key param: `AuthenticationMethods "password,keyboard-interactive"` requires both factors. For pubkey-only, use `"publickey,keyboard-interactive"`: key + OTP.

4. Restart and verify

# Keep the current SSH session open before restart. A bad config will lock you out
sudo systemctl restart sshd

# Open a new terminal and test login; password then verification code
ssh user@server
# Password:
# Verification code:

5. Recovery plan and bulk deploy

# Scratch codes: 5 one-time emergency codes from init live in ~/.google_authenticator; use if locked out
# Show scratch codes (last 5 lines; not the secret)
cat ~/.google_authenticator | tail -5

# Bulk deploy: copy secret file to the new user's home and chmod 600
sudo cp /home/tpl/.google_authenticator /home/newuser/
sudo chown newuser:newuser /home/newuser/.google_authenticator
sudo chmod 600 /home/newuser/.google_authenticator

Verify Config

# 1. Confirm the PAM module loads
ldd /usr/lib/x86_64-linux-gnu/security/pam_google_authenticator.so | head -1
# or
sudo grep google /etc/pam.d/sshd

# 2. Check time sync (TOTP depends on the system clock; skew > 30s fails)
timedatectl status
# Should show System clock synchronized: yes

# 3. Test login with a phone code. Wrong codes should be rejected three times (default max tries is 3)

FAQ

Q1: SSH locked out after config?

If a new session will not even take a password, PAM or sshd_config is wrong. Recovery: log in via cloud console VNC (bypass SSH), drop the `AuthenticationMethods` line or comment out the PAM entry, then restart sshd. No VNC: keep one live admin session and change config in small steps.

Q2: OTP always fails?

Check server time (`timedatectl`). TOTP is clock-sensitive. Confirm the phone app and server share the same secret; re-run `google-authenticator` to mint a new one. Also check for extra `.google_authenticator` copies. The SSH user's home must match the user you initialized.

Summary

2FA is effective against password brute-force and credential stuffing. PAM + google-authenticator adds TOTP to SSH in about 30 minutes. Keep an admin session before you change config, keep scratch codes after, and keep the clock in sync. Roll 2FA out to web admin, VPN, and every other critical entry. One MFA stack.