Two-factor authentication for SSH (TOTP on Ubuntu/Debian)
What this is
Requiring a six-digit authenticator code at SSH login, the same TOTP apps (Google Authenticator, Authy, 1Password) you use for your account's 2FA, enforced by the server through libpam-google-authenticator.
First, the honest positioning, because this upgrade is often oversold:
- If you still allow password logins, TOTP is a genuinely big upgrade: a leaked or guessed password stops being enough. (Though the bigger upgrade remains switching to keys, and our network already blunts password brute-force.)
- If you're already key-only, a passphrase-protected key is effectively two factors (the key file + the passphrase), so TOTP adds a third, mostly protecting against a stolen, unlocked key. Worth it for high-stakes boxes and compliance checklists; optional for a hobby server.
The strong combination this guide builds: key + code (publickey,keyboard-interactive), something you have, something you know, something that rotates.
Setup
Everything below on Ubuntu/Debian; same module on RHEL-family via the google-authenticator package (EPEL).
1. Install the PAM module:
apt install libpam-google-authenticator
2. Enroll the login user. As the user who logs in over SSH (not necessarily root), run:
google-authenticator
Answer y to time-based tokens, scan the QR with your authenticator app, and save the emergency scratch codes somewhere safe off the server, they are your login of last resort if the phone is gone. The sensible defaults for the remaining prompts: update the file (y), disallow token reuse (y), and enable rate limiting (y).
3. Tell PAM to require the code. Add to the top section of /etc/pam.d/sshd:
auth required pam_google_authenticator.so
(For a gradual rollout, auth required pam_google_authenticator.so nullok lets users who haven't enrolled yet log in without a code, remove nullok once everyone's enrolled.)
4. Tell SSH to ask. In /etc/ssh/sshd_config:
KbdInteractiveAuthentication yes
(older configs call it ChallengeResponseAuthentication), and set the policy with AuthenticationMethods:
- Key plus code (recommended):
AuthenticationMethods publickey,keyboard-interactive - Password plus code:
AuthenticationMethods password,keyboard-interactive(make surePasswordAuthentication yesin this case)
5. Restart and test, without closing your session:
systemctl restart ssh
Then, from a second terminal, log in fresh: key (or password) first, then Verification code: prompts for the app's six digits.
The anti-lockout discipline
PAM misconfiguration is the classic way to lock yourself out of SSH entirely, so the rules from every firewall and key guide apply doubly: keep your working session open until a second terminal proves the new flow, keep the scratch codes off-server, and know the safety net, the Console in your client area doesn't authenticate through SSH or PAM's sshd stack, so even a fully botched config is a fix-it-from-the-console incident, not a rebuild.
Operational notes
- TOTP lives and dies by the clock. Codes are time-based; if the server's clock drifts, valid codes get rejected. Verify
timedatectlshowsSystem clock synchronized: yes(systemd-timesyncd handles it by default), before blaming the app. - Each user enrolls separately (
google-authenticatorrun as that user; the secret lives in their~/.google_authenticator). Automation and deploy accounts are usually better served by keys without TOTP, scopeAuthenticationMethodsper-user with aMatch Userblock if you mix. - Know what it protects: SSH logins, that's all. Your web apps, panels, and everything else exposed have their own login doors.
Still need help?
You can open a support ticket. So we can help on the first reply, it's worth mentioning:
- the VPS hostname or IP,
- where the flow fails (PAM, sshd config, a rejected code), with the exact prompt or error,
- whether the Console still gets you in.
Related questions
- "How do I add two-factor authentication to SSH?"
- "How do I set up google-authenticator on Ubuntu?"
- "Should I use SSH keys, TOTP, or both?"
- "Why are my SSH verification codes rejected (clock skew)?"
- "What happens if I lose my phone (scratch codes)?"
- "Can some users skip the code requirement (nullok, Match User)?"