Cybersecurity

2FA SSH Linux: Add Google Authenticator in 10 Min

2FA SSH Linux: Add Google Authenticator in 10 Min

SSH credentials for a staging server sold on Telegram. The server was exposed to the internet and relied on password-only authentication. 2FA would have stopped the intrusion cold. The attack ran for 6 hours before anyone noticed the anomalous CPU load. A single compromised password gives attackers persistent access to your infrastructure. Password policies alone cannot stop credential stuffing or phishing. 2FA adds a physical factor, the phone in the user’s pocket, which an attacker in another country cannot replicate. 73% of breaches start from stolen credentials, according to the Verizon DBIR 2025. When I implemented two-factor authentication on 50 servers for a public healthcare organization, the main concern was avoiding locking out operators during the night shift. The solution is testing while keeping an open session. Here is how to do it. SSH with 2FA on Linux is implemented by installing the PAM module google-authenticator, generating the TOTP secret for the user, and modifying the PAM and sshd configuration to require the OTP code after the password or public key. In 10 minutes you block access from stolen credentials.

2FA SSH Prerequisites and Test Environment

You need an Ubuntu 20.04 or 22.04 LTS server, SSH access with sudo privileges, and a TOTP app on your phone like Google Authenticator, Authy, or Microsoft Authenticator. 51% of SSH brute force attacks succeed within 12 hours if the password is weak, according to Rapid7 2025. Test on a virtual machine or a non-critical staging server first. Verify your SSH client supports ChallengeResponseAuthentication. Most modern clients like PuTTY or the native macOS terminal handle it without issues. Do not test this procedure directly on a production server without a fallback session open.

Installation and User Secret Generation

Install the PAM Module

The package links the Linux authentication system (PAM) to the TOTP code generator. On Debian-based systems, libpam-google-authenticator pulls the required dependencies automatically.

sudo apt update
sudo apt install libpam-google-authenticator

On RHEL 8 or 9, enable the EPEL repository first, then install the package: sudo yum install epel-release && sudo yum install google-authenticator. The installation does not modify SSH access yet. It only makes the module available.

Generate the User Secret and QR Code

Each user who needs 2FA generates their own secret. Do not run this command with sudo for another user, because the secret would end up in root’s home directory.

google-authenticator

The command asks five questions. Answer Y to all:

  1. Do you want authentication tokens to be time-based? Y
  2. Do you want to inhibit multiple uses? Y (blocks code reuse)
  3. Do you want to enforce rate-limiting? Y (max 3 attempts every 30 seconds)
  4. Do you want to limit the number of failures before banning? Y (max 3 failures)
  5. Do you want to enable rate-limiting in the log file? Y

The terminal displays the QR code, the manual secret key, and the scratch codes for recovery. The secret key is a 16-character base32 string. Write it down on paper or store it in a secure password manager like Bitwarden. Never store it in a plain text file on the same server.

Scan the QR Code

Open Google Authenticator on your phone. Select Add account, scan the QR code displayed in the terminal. The app starts generating 6-digit codes that change every 30 seconds. If you cannot scan the QR code from the remote terminal, enter the manual secret key displayed below the QR code. Save the secret key in a secure location. You need it to regenerate the account if you lose your phone.

SSH and PAM Configuration

Modify PAM to Enable 2FA

PAM (Pluggable Authentication Modules) decides how Linux verifies credentials. Modify the file that handles SSH.

sudo nano /etc/pam.d/sshd

Add this line at the top of the file, before the other auth lines:

# /etc/pam.d/sshd: auth required pam_google_authenticator.so
auth required pam_google_authenticator.so nullok

The nullok parameter is critical. It allows access without 2FA if the user has not yet generated the .google_authenticator file in their home directory. Without nullok, all users without a secret are locked out. Remove nullok only after every user has configured their secret.

Modify sshd_config

The SSH daemon must know to require challenge-response for the OTP code.

sudo nano /etc/ssh/sshd_config

Find and modify this line:

# sshd_config: ChallengeResponseAuthentication yes
ChallengeResponseAuthentication yes

On newer Ubuntu versions (22.04 and later), the parameter might be named KbdInteractiveAuthentication instead of ChallengeResponseAuthentication. Check your sshd_config for the correct directive. If both exist, set the active one to yes. If the line is commented out or set to no, SSH ignores the OTP request from PAM. Verify that PasswordAuthentication is yes if you want the password + OTP combination. If you use public key + OTP only, PasswordAuthentication stays no, but you need the Keyboard-Interactive configuration described in the OpenSSH official documentation.

Apply the changes:

sudo systemctl restart sshd

Test Without Closing the Current Session

This is the step where many lock themselves out. DO NOT close the active SSH session. Open a new terminal and attempt to log in.

ssh user@server-ip

The system asks for the Verification code (the OTP from your phone) first, then the password. When you connect, the prompt order might surprise you. PAM processes modules in the order they appear in the configuration file. If you want the password prompt first, then the OTP, ensure the password auth module comes before the google_authenticator module in /etc/pam.d/sshd. However, the default setup often asks for OTP first. Train your team on this new prompt sequence to avoid confusion during night shifts. If the login fails, return to the original session, remove the line from /etc/pam.d/sshd, restart sshd, and check what went wrong. If the login works, access now requires password and OTP. Close the test session.

Manage Backup Codes and Exclude Users

The google-authenticator command generates 5 emergency scratch codes. They are one-time codes that work once without your phone. Save these codes offline, in a place accessible without the server itself. If you lose your phone and lack the scratch codes, you stay locked out. To regenerate the codes, run google-authenticator again, but the secret changes and you must update the app.

Service accounts or backup administrative users do not have a phone for OTP. The nullok parameter handles this case. If the .google_authenticator file does not exist in the user’s home, PAM skips the OTP verification and requires only the password. To exclude a specific user even after activating 2FA for everyone, modify the auth line in PAM:

auth [success=done default=ignore authinfo_missing=ignore] pam_google_authenticator.so nullok_skippable

nullok allows login without OTP if the secret file does not exist. nullok_skippable does the same but does not log the event as a PAM error. Use nullok for gradual transition, use nullok_skippable for permanent service accounts.

Common Errors and Troubleshooting

Access denied after sshd restart: ChallengeResponseAuthentication is still no in sshd_config, or the PAM line is in the wrong position (after the other auth lines). It must be the first auth line.

SSH asks only for password, no OTP: nullok is active and the user has not generated the secret, or sshd was not restarted after the change.

OTP code rejected: The server time is out of sync. TOTP tolerates a 30-second window. Synchronize the server with NTP (sudo timedatectl set-ntp true).

Locked out: If you did not keep the session open and the login fails, you need physical console access or cloud provider web console (VNC) to restore the files.

How does SSH 2FA work with public keys instead of passwords?

If you use a public key, SSH already authenticates the identity. To add 2FA, configure Keyboard-Interactive in sshd_config and PAM requires the OTP after key verification. The procedure differs from the password+OTP setup.

Is 2FA SSH required by NIS2 compliance?

Article 21 of NIS2 requires MFA for access to critical assets. 2FA SSH on production servers is a direct requirement for healthcare organizations and public administration. A public healthcare organization with 2,000 endpoints cannot rely on single-factor SSH for system administrators. The auditor will flag missing MFA as a critical non-conformity under ISO 27001:2022 Annex A.9.4.2 and NIST CSF 2.0 PR.AC-7.

Conclusions with Operational Takeaways

Adding Google Authenticator to SSH Linux takes 10 minutes and blocks 100% of accesses with stolen credentials. The three critical steps: install the PAM module, generate the secret for each user, modify sshd_config and PAM. The biggest risk is locking yourself out during the test. Always keep an open session. Remove nullok only when all users have an active secret. Organizations subject to NIS2 must implement MFA on all critical accesses. 2FA SSH is the first step.

Read also: Hardening SSH en Linux: Guía Completa 2026

Read also: Fail2ban on Linux: Complete Setup and SSH Hardening

Share this article:

Written by

Rosario Giordano

Rosario Giordano is a system administrator and IT consultant specializing in cybersecurity and cloud, with over 20 years of experience managing enterprise Linux infrastructures. His areas of expertise include SSH hardening, Kubernetes platforms, PostgreSQL databases, VMware/ Proxmox virtualization, and compliance with NIS2 and ISO 27001 security frameworks