SSH key authentication is genuinely strong security — cryptographic keys are much harder to compromise than passwords. But I work across multiple machines and occasionally worry about what happens if a private key is ever compromised.
Adding TOTP-based 2FA means an attacker needs both the key file and the current 6-digit code from my authenticator app. The two-factor part isn't about doubting SSH keys — it's about defense in depth.
Important warning before you start: Test 2FA in a second SSH session while keeping your existing session open. If you misconfigure this and get locked out, you'll need console access to fix it. I'll make this explicit in the guide.
I set this up on Tencent Cloud Lighthouse for servers that contain sensitive data. It takes about 10 minutes and works with any TOTP authenticator app. One important practical note specific to Lighthouse: if you configure 2FA incorrectly and lock yourself out of SSH, OrcaTerm (the browser-based terminal in the Lighthouse console) gives you direct server access without SSH authentication. This means you can fix a misconfiguration without needing a rescue boot or support ticket — which makes testing 2FA setups much less risky.
- Key Takeaways
| Scenario | 2FA value |
|---|---|
| SSH key + 2FA | Very high security — attacker needs key AND phone |
| Password auth + 2FA | High security — better than password alone |
| Key auth only | Good security — appropriate for most setups |
| Root login allowed | Always add 2FA if you keep this enabled |
For most personal servers using SSH keys, 2FA is optional but adds meaningful protection. For servers with sensitive data or that you share access to, 2FA is strongly recommended.
| Requirement | Notes |
|---|---|
| Cloud server | Tencent Cloud Lighthouse Ubuntu 22.04 |
| A TOTP authenticator app | Google Authenticator, Authy, 1Password, Bitwarden, etc. |
| An active SSH session open | Keep it open during configuration (don't close it) |
ssh ubuntu@YOUR_SERVER_IP
sudo apt update
sudo apt install -y libpam-google-authenticator
Run the configuration as the user who will use 2FA:
google-authenticator
Answer the questions:
Do you want authentication tokens to be time-based (y/n): y
A QR code and secret key appear. Scan the QR code with your authenticator app now.
Continue with the prompts:
Do you want me to update your "~/.google_authenticator" file? y
Do you want to disallow multiple uses of the same authentication token? y
# Prevents replay attacks
Do you want to increase the time skew window? n
# Unless your server clock is consistently off
Do you want to enable rate-limiting? y
# Limits to 3 login attempts per 30 seconds
The setup generates emergency scratch codes — save these somewhere safe. They let you log in if you lose your phone.
sudo nano /etc/pam.d/sshd
Add this line at the top (before other auth lines):
auth required pam_google_authenticator.so
Or to make 2FA optional (only required if configured for a user):
auth required pam_google_authenticator.so nullok
nullok means users without 2FA configured can still log in. Remove nullok to enforce 2FA for all users.
sudo nano /etc/ssh/sshd_config
Update these settings:
# Enable PAM
UsePAM yes
# Enable challenge-response (required for TOTP prompts)
ChallengeResponseAuthentication yes
# Or for newer OpenSSH:
KbdInteractiveAuthentication yes
# Require both public key AND 2FA
AuthenticationMethods publickey,keyboard-interactive
# Or for password + 2FA only:
# AuthenticationMethods password keyboard-interactive
sudo systemctl restart sshd
Keep your current SSH session open. Open a new terminal and test:
# In a NEW terminal window
ssh ubuntu@YOUR_SERVER_IP
You should now see two prompts:
Verification code: — enter the 6-digit code from your authenticator appIf the login succeeds — 2FA is working.
If it fails, your original session is still open to fix the configuration.
Many people prefer: SSH key auth works without 2FA, but if you use password auth, 2FA is required. This is convenient for trusted devices with keys, while still protecting password-based logins.
sudo nano /etc/ssh/sshd_config
# Public key auth: key only (no 2FA)
# Password auth: password + TOTP
AuthenticationMethods publickey keyboard-interactive:pam
sudo nano /etc/pam.d/sshd
Comment out @include common-auth (to prevent double password prompt):
# @include common-auth ← comment this out
auth required pam_google_authenticator.so
sudo systemctl restart sshd
With this setup:
Your 2FA secret is stored in ~/.google_authenticator. Back it up:
cat ~/.google_authenticator
This file contains:
Store this file (or just the secret key) in a password manager or secure offline location. If you lose your phone, you can regenerate TOTP codes from the secret key in any TOTP app.
The most dangerous mistake in 2FA setup: making SSH changes and then closing your terminal before testing. If the configuration is wrong, you lock yourself out.
The rule: always test in a separate terminal window while keeping the original session open.
If you accidentally lock yourself out:
# Revert to no 2FA
sudo nano /etc/pam.d/sshd
# Comment out: auth required pam_google_authenticator.so
sudo systemctl restart sshd
If you've lost your phone and need to access the server:
When you ran google-authenticator, it generated 5 emergency scratch codes. Each works once. If you saved them, use one now.
Access the server directly via the Lighthouse console browser terminal — bypasses SSH entirely.
Once in:
# Temporarily disable 2FA
sudo nano /etc/pam.d/sshd
# Comment out the google-authenticator line
sudo systemctl restart sshd
Reconfigure 2FA with your new phone:
# Re-run setup as your user
google-authenticator
# New QR code will appear
Re-enable 2FA in PAM config.
If you backed up the secret key from ~/.google_authenticator, enter it manually into any TOTP app to regenerate codes.
| Issue | Likely Cause | Fix |
|---|---|---|
| Connection refused | Service not running or wrong port | Check systemctl status SERVICE and verify firewall rules |
| Permission denied | Wrong file ownership or permissions | Check file ownership with ls -la and use chown/chmod to fix |
| 502 Bad Gateway | Backend service not running | Restart the backend service; check logs with journalctl -u SERVICE |
| SSL certificate error | Certificate expired or domain mismatch | Run sudo certbot renew and verify domain DNS points to server IP |
| Service not starting | Config error or missing dependency | Check logs with journalctl -u SERVICE -n 50 for specific error |
| Out of disk space | Logs or data accumulation | Run df -h to identify usage; clean logs or attach CBS storage |
| High memory usage | Too many processes or memory leak | Check with htop; consider upgrading instance plan if consistently high |
| Firewall blocking traffic | Port not open in UFW or Lighthouse console | Open port in Lighthouse console firewall AND sudo ufw allow PORT |
Does SSH 2FA conflict with Lighthouse's console-level firewall?
No — they operate at different layers. Lighthouse's console firewall blocks at the network infrastructure level. SSH 2FA operates at the OS level. Both provide independent protection layers.
How do I check if SSH 2FA is working correctly?
Check the service status with sudo systemctl status ssh. Review logs for recent activity. Most security tools have a test or dry-run mode to verify configuration.
What should I do if SSH 2FA blocks a legitimate user or IP?
Most security tools have a whitelist mechanism. Add trusted IPs to the whitelist/ignore list. For Fail2ban, use fail2ban-client set JAIL unbanip IP_ADDRESS.
How often should I review security logs?
For personal servers, a weekly review of auth logs and firewall logs is reasonable. For production or sensitive servers, set up log monitoring with alerts for suspicious patterns (unusually high fail rates, unusual hours).
Secure your server with 2FA:
👉 Tencent Cloud Lighthouse — Console terminal access as a safety net
👉 View current pricing and promotions
👉 Explore all active deals and offers