What the second factor adds
SSH two-factor authentication adds a one-time code to the login. Install libpam-google-
The key and the code answer two different questions. The key proves the device; the time-based one-time password (TOTP) proves the person holding it. With both required, a stolen laptop or a copied private key is no longer enough on its own.
- Something you have: the SSH private key or a hardware token.
- Something you know or hold: the six-digit code from an authenticator app.
- What it stops: credential stuffing, a leaked key, a reused password.
- What it does not stop: a phishing site that proxies the code in real time. A hardware key does.
Before you change anything
This is the change that locks administrators out. Ten minutes of preparation removes the risk entirely.
- Keep your current SSH session open. It survives the sshd restart and can undo the edit.
- Confirm the provider console or rescue mode works before you need it.
- Create or confirm a second sudo user, so one broken profile is not fatal. The sudo user guide covers it.
- Take a snapshot if the panel offers one.
- Know your scratch codes' location before enrolment, because they are printed once.
Install and register the token
The PAM module is in the distribution repositories. Register the token as the login user, not as root, because the secret is stored in that user's home directory.
sudo apt update
sudo apt install libpam-google-authenticator
google-authenticator
Answer the questions this way: time-based tokens yes, update the file yes, disallow multiple uses yes, increase the time window no, enable rate limiting yes. The command prints a QR code and a list of scratch codes. Store the codes offline; they are the recovery path when the phone is gone.
The secret lives in ~/.google_. Check that the file is readable only by its owner with chmod 600 ~/.google_. On RHEL-family systems the package is google-authentic from EPEL.
Wire PAM and sshd
Two files change: the PAM stack and the sshd configuration. Read both before saving, and test the sshd file before restarting.
- Add one line to
/etc/pam.d/sshd:auth required pam_google_.authenticator. so - In
/etc/ssh/setsshd_config KbdInteractiveAu. Older guides saythentication yes ChallengeRespons; it is the deprecated name for the same setting.eAuthentication - Confirm
UsePAM yesis present and not commented out. - To require both factors, add
AuthenticationMe.thods publickey, keyboard- interactive - Run
sudo sshd -t, restart withsudo systemctl restart ssh, and open a second session to test.
If the second session asks for a verification code after your key, it worked. If it refuses, go back to the first session and revert the two files; the old session is still authenticated.
Know it worked
Success has three visible signs: the login prompts for a verification code after the key, a wrong code is rejected, and the auth log records the method used.
sudo tail -n 20 /var/log/auth.log
sudo journalctl -u ssh -n 20 --no-pager
Look for Accepted keyboard- next to the successful login. A line saying Accepted publickey alone means the second factor was not required, which usually means the AuthenticationMe line is missing or misspelled.
Automation accounts need an exemption. A backup job cannot type a code, so give it its own key and a Match User backup block that allows publickey only, rather than weakening the rule for everyone.
Recovery when the phone is lost
A lost phone is not a lost server if you prepared. Work through these in order, from least to most invasive.
- Enter one of the scratch codes at the verification prompt. Each works once.
- If the codes are gone, open the provider console and log in there.
- Move the secret aside:
mv ~/.google_, then log in with the key alone.authenticator ~/.google_ authenticator. bak - Re-enrol with
google-authenticand store the new scratch codes.ator - If sshd itself will not start, set
AuthenticationMeback to publickey, runthods sshd -t, and restart.
The connection refused guide covers the console and rescue-mode path when SSH is not answering at all.
TOTP, hardware key or both
TOTP is the pragmatic default: any phone, no extra hardware, one extra prompt. A hardware key is stronger against phishing but needs to be bought and carried. Many teams use a key for administrators and TOTP for everyone else.
| Method | What the user needs | Phishing resis | Fits |
|---|---|---|---|
| Password only | Nothing extra | None | Nowhere on a public VPS |
| Key only | A key file | Good, unless the key is copied | Autom |
| Key plus TOTP | A key and a phone app | Strong against stolen keys | Produ |
| Key plus hardware token | A key and a FIDO2 device | Stron | Adminis |
Whatever you choose, turn off password authentication only after the new method has worked in a second session. A WebJi VPS includes console access for exactly this kind of change; support@webji.
Frequently Asked Questions
How do I enable 2FA for SSH on Ubuntu?
Install libpam-google-
What is the most secure SSH setup?
Key-only login plus a second factor: AuthenticationMe
Can I use Google Authenticator for SSH login?
Yes. The PAM module reads a TOTP secret stored in the user's home directory, and any TOTP app works: Google Authenticator, Aegis, 1Password, Microsoft Authenticator. The server does not depend on Google's service.
Should I use SSH keys and 2FA together?
Yes, when the server matters. The key proves the device and the code proves the person. Requiring both means a stolen laptop or a copied key alone is not enough to log in.
What if I lose my 2FA device for SSH access?
Use one of the scratch codes printed during enrolment, then log in and re-enrol the new phone. Without a scratch code, use the provider console or rescue mode to move .google_
Does fail2ban work with SSH 2FA?
Yes, and it complements it. fail2ban blocks repeated failed attempts at the network level; 2FA blocks a valid password or key from completing the login. Whitelist your admin IP in fail2ban before you enable 2FA.

