How to Configure Two-Factor Authentication for SSH
Overview and Goals
This article explains how to add two-factor authentication (2FA) to SSH in a clear, practical way.
You will learn about common 2FA methods, how to install and configure them, and how to combine 2FA with SSH public-key authentication.
The goal is to help you secure remote shell access while keeping user experience predictable and recoverable.
Prerequisites and Environment
You need root or sudo access on the SSH server.
OpenSSH 6.2 or newer is required for the AuthenticationMethods directive.
Install and enable an NTP client (chrony or ntpd) to keep clocks aligned for TOTP tokens.
Have a second admin account or console access ready before changing SSH to avoid locking yourself out.
This guide uses examples for Debian/Ubuntu and RHEL/CentOS systems; package names may vary on other distributions.
Two-Factor Authentication Methods for SSH
TOTP (Time-based One-Time Password)
- Uses authenticator apps (Google Authenticator, Authy, Microsoft Authenticator).
- Works offline and is widely supported.
U2F/WebAuthn hardware keys (YubiKey, SoloKey)
- Physical keys provide phishing-resistant second factors.
- Use PAM modules like pam_u2f or pam_webauthn.
Push or cloud 2FA (Duo, Okta)
- Provider-hosted push notifications or SMS.
- Better for large deployments but relies on external services.
SMS (not recommended)
- Vulnerable to SIM swap and interception.
- Avoid unless no alternatives exist.
Choose a method based on threat model, user devices, budget, and offline needs.
Choosing the Right 2FA Solution
Decide using a few simple criteria:
- Security: Hardware keys are best, TOTP is strong, SMS is weakest.
- Usability: TOTP apps are easy for most users; hardware keys require physical tokens.
- Scale: Cloud providers (Duo) offer central management for many users.
- Recovery: Consider how users will recover lost phones or keys.
Pick a solution that balances protection with operational reality.
Installing Required Packages and Tools
On Debian/Ubuntu (TOTP):
- sudo apt update
- sudo apt install libpam-google-authenticator qrencode
On RHEL/CentOS (TOTP, using EPEL):
- sudo yum install epel-release
- sudo yum install google-authenticator-libpam qrencode
For U2F hardware keys (Debian/Ubuntu):
- sudo apt install libpam-u2f pamu2fcfg
For U2F on RHEL/CentOS, enable EPEL and install equivalent packages.
If you choose a managed provider (Duo), follow the vendor guide to install their PAM integration package.
Package names and availability vary by distribution; check your distro documentation if a package is not found.
Configuring PAM for TOTP-Based 2FA
Edit the PAM file for SSH, typically /etc/pam.d/sshd.
Add a line to invoke the TOTP module. Example for libpam-google-authenticator:
auth required pam_google_authenticator.so
Place that line after any pam_sepermit or pam_unix lines that handle initial checks.
If you want users to be able to skip 2FA (not recommended) use nullok, e.g.:
auth required pam_google_authenticator.so nullok
Do not use nullok if you want the factor mandatory.
Save the file and do not restart sshd yet — finish sshd_config changes first.
Permissions and file location:
- The TOTP module by default reads ~/.google_authenticator for each user.
- Ensure home directories and those files have correct ownership and restrictive permissions.
Integrating 2FA with sshd_config
Open /etc/ssh/sshd_config and check these settings:
UsePAM yes
ChallengeResponseAuthentication yes
To require public-key plus 2FA, use AuthenticationMethods. Example:
AuthenticationMethods publickey,keyboard-interactive:pam
This requires a successful public-key login followed by the PAM keyboard-interactive step (where TOTP runs).
If you prefer password plus 2FA:
AuthenticationMethods password,keyboard-interactive:pam
After editing, reload sshd:
sudo systemctl reload sshd
Always keep an active session or console access while testing configuration changes.
Combining Public-Key Authentication with 2FA
Combining public-key authentication with 2FA gives strong protection with good user experience.
Users authenticate with their SSH key first. If the key is valid, the server then prompts for a second factor.
To implement:
- Ensure users have their SSH public key in ~/.ssh/authorized_keys.
- Configure sshd_config as shown above to require publickey,keyboard-interactive:pam.
- Ensure UsePAM and ChallengeResponseAuthentication are enabled.
The result: an attacker needs both the private key and the second factor to log in.
You can use Match blocks in sshd_config to exempt certain accounts or hosts if needed:
Match User admin
AuthenticationMethods publickey,keyboard-interactive:pam
Use Match cautiously to avoid weakening the policy.
User Enrollment, Backup Codes, and Recovery Procedures
TOTP enrollment (per user):
- Log in as the user (or use sudo -u username -s).
- Run google-authenticator and follow prompts.
- Scan the QR code with an authenticator app or manually enter the seed.
- Save the provided emergency scratch codes in a secure vault.
Hardware key enrollment (example with pamu2fcfg):
- Run pamu2fcfg > ~/.config/Yubico/u2f_keys (or follow your distro’s instructions).
- Secure the file and restrict permissions.
- Verify the PAM mapping location matches /etc/pam.d/sshd configuration for pam_u2f.
Backup codes and recovery:
- Treat scratch/backup codes as highly sensitive; store them in an encrypted vault or printed and locked in a safe.
- Create procedures for lost phone or key: allow temporary console login, require identity verification, then remove or re-register second factor.
- Consider an emergency “break-glass” admin account with keys stored offline and access strictly audited.
Document user enrollment and recovery steps and train at least two administrators.
Testing and Validation Procedures
Follow a safe testing plan:
- Keep a working SSH session open before reloading SSH.
- From a different terminal or device, attempt to connect with ssh -vvv user@host to see debug messages.
- Confirm the server prompts first for key (if using publickey) and then for the OTP.
- If using TOTP, verify tokens from the authenticator app work and that scratch codes are accepted.
- Check server logs for authentication events:
- Debian/Ubuntu: sudo tail -f /var/log/auth.log
- RHEL/CentOS: sudo journalctl -u sshd -f
- Validate time synchronization by intentionally skewing clocks on a test client to confirm expected failures occur when time is wrong.
Automated checks:
- Add automated login tests from a CI job that uses test keys and not production accounts.
- Periodically test recovery steps with a non-critical account.
Troubleshooting Common Issues and Logs
Problem: SSH does not prompt for the second factor.
- Confirm UsePAM yes and ChallengeResponseAuthentication yes in sshd_config.
- Ensure AuthenticationMethods is set correctly.
- Restart or reload sshd after changes.
Problem: PAM module errors in logs.
- Check /var/log/auth.log or journalctl for the PAM module name and error code.
- Ensure the module is installed and accessible by root.
- Check SELinux denials with sudo ausearch -m AVC -ts recent or sudo journalctl -t setroubleshoot.
Problem: TOTP tokens rejected unexpectedly.
- Verify server and client clocks; enable NTP/chrony.
- Check ~/.google_authenticator file permissions and ownership.
- Ensure users did not regenerate tokens accidentally.
Problem: Lockout risk when disabling password auth.
- Keep an active session or console access before setting PasswordAuthentication no.
- Test key-plus-2FA logins for multiple users.
Useful log commands:
- sudo tail -n 200 /var/log/auth.log
- sudo journalctl -u sshd –since today
- sudo ausearch -m AVC — if using SELinux
Security Best Practices and Ongoing Maintenance
Require public-key plus a second factor for interactive SSH access when possible.
Disable PasswordAuthentication if all users can use key-based login.
Disable root login over SSH (PermitRootLogin no) and require sudo for privilege escalation.
Enforce strict permissions on SSH and 2FA configuration files and on user home directories.
Use fail2ban or rate-limiting to slow brute-force attacks.
Keep system and PAM modules up to date with regular patching.
Rotate backup codes and re-enroll hardware tokens periodically.
Document procedures for enrollment, lost tokens, and emergency access; test these procedures yearly.
Monitor and alert on authentication failures and unusual login patterns.
Store recovery and emergency keys in an encrypted, audited vault accessible to authorized operators only.
Closing note: Implement 2FA carefully and test thoroughly to avoid accidental lockouts. Proper planning, user training, and clear recovery procedures make 2FA a practical, high-value step for securing SSH access.
About Jack Williams
Jack Williams is a WordPress and server management specialist at Moss.sh, where he helps developers automate their WordPress deployments and streamline server administration for crypto platforms and traditional web projects. With a focus on practical DevOps solutions, he writes guides on zero-downtime deployments, security automation, WordPress performance optimization, and cryptocurrency platform reviews for freelancers, agencies, and startups in the blockchain and fintech space.
Leave a Reply