SSH Two-Factor with Google Authenticator: TOTP Setup
You can configure SSH to require a Time-based One-Time Password (TOTP) from Google Authenticator (or any compatible TOTP app) in addition to a traditional password or SSH key.
Let’s see it in action. Imagine a user trying to SSH into a server.
ssh user@your_server_ip
Normally, they’d be prompted for their password or passphrase. Now, after that, they’ll see a prompt like this:
Password:
Verification code:
If they enter their password correctly, they then need to enter the 6-digit code currently displayed in their Google Authenticator app. If both are correct, they’re logged in.
This setup leverages the Pluggable Authentication Modules (PAM) system on Linux. SSH itself doesn’t handle the TOTP generation; it delegates that to PAM. When you SSH into a server, the sshd daemon consults its PAM configuration. For SSH, this is typically defined in /etc/pam.d/sshd. We’ll modify this file to include an entry that uses the google-authenticator PAM module.
The google-authenticator PAM module is a separate piece of software that needs to be installed. On Debian/Ubuntu systems, it’s usually available as the libpam-google-authenticator package. On Red Hat/CentOS, it’s often google-authenticator.
Here’s the core of how it works:
-
User Initialization: Each user who wants to use TOTP must run the
google-authenticatorcommand once in their own home directory. This command generates a secret key, a QR code (which you scan with your authenticator app), and a set of emergency backup codes. It also creates a.google_authenticatorfile in the user’s home directory, storing this secret key and other configuration for that user.# Run this as the user who will be SSHing, NOT as root google-authenticatorThis will prompt you with questions like:
- "Do you want authentication tokens to be time-based (y/n)" -> y (This is crucial for Google Authenticator compatibility)
- It will then display a QR code to scan and a secret key, along with emergency codes. Save these emergency codes securely!
- It asks about "Disabling time-based tokens" -> n (You want time-based)
- "Do you want to update the .google_authenticator file?" -> y (This saves the configuration)
- "Do you want to disallow multiple uses of the same authentication token?" -> y (Good security practice)
- "By default, new Unix authentication tokens are allowed to be used for up to 4 minutes (240 seconds) past their expiration. If you want to disable this, uncomment the following line in the PAM service file…" -> y (This tightens security)
- "Do you want to enable rate-limiting?" -> y (This is a good defense against brute-force attacks)
-
PAM Configuration: Next, you need to tell PAM to use this module for SSH logins. You’ll edit
/etc/pam.d/sshd. You need to add a line that tells PAM to use thegoogle-authenticatormodule. The exact placement matters. It’s typically added after the line that handles standard authentication (likepam_unix.soorpam_sss.so) but before any lines that handle session setup.Here’s a typical
/etc/pam.d/sshdfile, with the added line highlighted:#%PAM-1.0 auth required pam_secdedit.so auth required pam_unix.so try_first_pass nullok # Add this line for Google Authenticator auth required pam_google_authenticator.so account required pam_nologin.so account include password-auth password required pam_unix.so use_authtok sha512 password include password-auth session required pam_selinux.so close session required pam_loginuid.so session optional pam_keyinit.so force revoke session include password-auth session required pam_selinux.so open session optional pam_ck_connector.soThe
requiredkeyword means that this module must succeed for the authentication to pass. If it fails, the overall authentication will fail, but other modules will still be tried. -
SSH Server Configuration: Finally, you need to configure
sshditself to interact with PAM correctly for two-factor authentication. You’ll edit/etc/ssh/sshd_config.You need to ensure two directives are set:
ChallengeResponseAuthentication yes: This tellssshdto allow challenge-response authentication, which is how PAM modules likegoogle-authenticatorprompt the user for input (like the verification code).UsePAM yes: This is usually set by default, but it’s essential. It tellssshdto use PAM for authentication.
Here’s an example snippet from
/etc/ssh/sshd_config:# ... other sshd_config settings ... PubkeyAuthentication yes PasswordAuthentication yes ChallengeResponseAuthentication yes # <--- This is critical UsePAM yes # <--- This is critical # ... other sshd_config settings ...After making these changes, you must restart the SSH service for them to take effect:
sudo systemctl restart sshd # or sudo service ssh restartIf you disable
PasswordAuthentication yesand only havePubkeyAuthentication yesandChallengeResponseAuthentication yesenabled, you can achieve a truly passwordless SSH login that still requires TOTP after the SSH key authentication.
One subtle point is that pam_google_authenticator.so reads configuration from the user’s home directory (~/.google_authenticator). This means the PAM module needs to be able to access and read this file for the user attempting to log in. If the SSH daemon runs as a different user (e.g., root) and tries to check the user’s home directory, it might fail. However, the standard PAM setup for sshd correctly handles user context.
The next hurdle you’ll likely encounter is configuring this for multiple users or dealing with situations where users don’t have write access to their home directories for the initial google-authenticator setup.