SSH Two-Factor Authentication
SSH two-factor authentication adds a one-time code to key-based login. Install libpam-google-authenticator, enrol each user with google-authenticator, add the PAM module to /etc/pam.d/sshd, set KbdInteractiveAuthentication yes and AuthenticationMethods publickey,keyboard-interactive, then test from a second session before closing the first.
For Linux VPS and server admins adding a second factor to SSH without risking a lockout.
knowledge
- Read time
- 4 min
- Sections
- 7
- Updated
- 14 Nov 2026
- Written by
- WebJi® team
Last editorial update: 14 November 2026. Prices follow the dated sources on this page; checkout confirms current charges.
What does SSH two-factor authentication add?
SSH two-factor authentication requires a time-based one-time code (TOTP) from an authenticator app in addition to your SSH key, so a stolen laptop or copied private key is no longer enough to log in. The key proves the device; the code proves the person holding the phone.
- Stops: leaked or copied keys, reused passwords, credential stuffing.
- Does not stop: a phishing proxy that relays your code in real time — FIDO2 hardware keys do.
- Works with: Google Authenticator, Aegis, Microsoft Authenticator, 1Password or any TOTP app; the server never contacts Google.
How do you avoid locking yourself out?
Keep your current SSH session open throughout, confirm the provider's web console works, and test every change from a second terminal before closing the first. An existing session survives an sshd restart and can undo a bad edit.
- Open the VPS provider's console once to confirm you can log in there.
- Make sure a second sudo user exists; see add a sudo user on Ubuntu.
- Take a snapshot if your plan offers one.
- Have somewhere offline ready to store scratch codes; they are shown only once.
How do you install and enrol Google Authenticator for SSH?
Install the PAM module from your distribution, then run google-authenticator as the user who will log in — not as root — and scan the QR code. The secret is stored in that user's home directory.
# Ubuntu / Debian
sudo apt update
sudo apt install libpam-google-authenticator
# AlmaLinux / Rocky (EPEL)
sudo dnf install epel-release && sudo dnf install google-authenticator qrencode
# Run as the login user
google-authenticator
chmod 600 ~/.google_authenticator
Answer: time-based tokens yes, update the file yes, disallow reuse yes, widen the time window no, rate-limit yes. Save the printed emergency scratch codes offline; each works once.
How do you configure PAM and sshd to require key plus code?
Add the Google Authenticator module to /etc/pam.d/sshd, enable keyboard-interactive authentication in sshd, and set AuthenticationMethods publickey,keyboard-interactive. On Ubuntu and Debian, also comment out the password include, or users are asked for a password after the key.
# /etc/pam.d/sshd
# @include common-auth <- comment out on Ubuntu/Debian for key + code only
auth required pam_google_authenticator.so
# /etc/ssh/sshd_config (or a file in /etc/ssh/sshd_config.d/)
UsePAM yes
KbdInteractiveAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
PasswordAuthentication no
- Enrol every account that logs in by SSH before restarting sshd; users without a secret file will be refused.
- Check files in
/etc/ssh/sshd_config.d/; a cloud-init file there can override your settings. - Older guides use
ChallengeResponseAuthentication, the deprecated name forKbdInteractiveAuthentication. - Validate and restart:
sudo sshd -t && sudo systemctl restart ssh(the unit issshdon RHEL-family systems). - Open a second terminal and log in; you should be asked for a verification code after the key.
How do you confirm two-factor login is enforced?
A successful login should prompt for a code after the key, reject a wrong code, and log both methods. Check the authentication log right after testing.
sudo journalctl -u ssh -n 30 --no-pager # Ubuntu/Debian (use -u sshd on RHEL)
sudo tail -n 30 /var/log/auth.log # Ubuntu/Debian
sudo tail -n 30 /var/log/secure # AlmaLinux/Rocky
Look for Partial publickey followed by Accepted keyboard-interactive/pam. A plain Accepted publickey means the code was not required — usually a missing or misspelled AuthenticationMethods line, or an override in sshd_config.d.
Automated accounts that cannot type a code, such as a backup user, need a scoped exception rather than a weaker global rule:
Match User backup
AuthenticationMethods publickey
What if you lose the phone with your authenticator app?
Use an emergency scratch code at the verification prompt; if those are gone, log in through the provider's console, move the secret file aside and enrol a new phone. Work from least to most invasive.
- Enter one scratch code at the "Verification code" prompt.
- Without codes, log in via the provider console or rescue mode.
- In the console, set
AuthenticationMethods publickeyin sshd_config, runsudo sshd -tand restart ssh, so the key alone works again. - Log in over SSH, run
google-authenticatorto enrol the new phone, and store the new scratch codes. - Restore
AuthenticationMethods publickey,keyboard-interactive, validate withsshd -t, restart and test from a second session.
If SSH does not answer at all, follow SSH connection refused on a VPS.
Should you use TOTP codes or a hardware security key?
TOTP is the practical default for most teams; FIDO2 hardware keys are stronger because they resist real-time phishing, and OpenSSH supports them natively with ed25519-sk keys. Many teams give administrators hardware keys and use TOTP for everyone else.
| Method | User needs | Phishing resistance | Fits |
|---|---|---|---|
| Password only | Nothing extra | None | Not suitable for public servers |
| Key only | Key file | Good unless the key is copied | Automation, most admins |
| Key plus TOTP | Key and phone app | Strong against stolen keys | Production VPS, small teams |
FIDO2 key (-sk) | Hardware key with touch | Strongest | Critical systems |
# Create a hardware-backed SSH key (OpenSSH 8.2+), then add the .pub file to authorized_keys
ssh-keygen -t ed25519-sk -C "admin@example.in"
WebJi® VPS hosting starts at ₹499 a month before 18% GST and includes console access for changes like this; support@webji.services answers 24/7 in Hindi and English, average first reply under 60 minutes.
Frequently asked questions
Does SSH 2FA break SFTP or scp?
Interactive clients such as FileZilla and WinSCP prompt for the code and work normally. Scripts and CI jobs cannot type a code, so give them a separate user with a Match User exception that allows key-only login.
Why does it ask for my password and then the code?
On Ubuntu and Debian, @include common-auth in /etc/pam.d/sshd still runs the password step. Comment that line out and keep AuthenticationMethods publickey,keyboard-interactive so the flow becomes key, then code.
Do codes fail if the server clock is wrong?
Yes. TOTP depends on accurate time. Make sure time sync is on with timedatectl, which should show "System clock synchronized: yes". A drift of more than about 30 seconds causes rejected codes.
Does fail2ban still help with 2FA enabled?
Yes. fail2ban blocks IPs after repeated failures, cutting noise and brute-force attempts; 2FA stops a valid key alone from completing a login. Allow-list your office IP in fail2ban before testing.
Last editorial update: 15 November 2026. Prices follow the dated sources on this page; checkout confirms current charges.
Ready to get started with WebJi®?
Join 12,000+ businesses across India and overseas. INR pricing, 18% GST shown before payment and 24/7 email support in Hindi and English — average first reply under 60 minutes.