1582 words
8 minutes
Securing SSH with Google Authenticator: Two-Factor Authentication Setup
Table of Contents
Overview
Securing SSH access is crucial for protecting servers from unauthorized access. This guide demonstrates how to implement two-factor authentication (2FA) using Google Authenticator, adding an extra layer of security beyond traditional password or key-based authentication.
Security Architecture
graph TB subgraph "Authentication Flow" A[SSH Client] --> B[SSH Server] B --> C{Auth Method} C --> D[Password/Key] D --> E{Valid?} E -->|Yes| F[PAM Module] E -->|No| G[Reject] F --> H[Google Auth] H --> I{TOTP Valid?} I -->|Yes| J[Grant Access] I -->|No| K[Reject] end
subgraph "Components" L[Google Authenticator App] M[PAM Module] N[SSH Configuration] O[User Secret Keys] end
L --> H M --> F N --> B O --> H
style A fill:#4ecdc4,stroke:#087f5b,stroke-width:2px style J fill:#51cf66,stroke:#2f9e44,stroke-width:2px style G fill:#ff6b6b,stroke:#c92a2a,stroke-width:2px style K fill:#ff6b6b,stroke:#c92a2a,stroke-width:2pxPrerequisites
System Requirements
# Check system informationcat /etc/os-releaseuname -a
# Ensure SSH is installedssh -V
# Check PAM versionrpm -qa | grep pam # RHEL/CentOSdpkg -l | grep libpam # Debian/UbuntuTime Synchronization
# Install NTP clientsudo apt install ntp # Debian/Ubuntusudo yum install ntp # RHEL/CentOS
# Sync timesudo ntpdate -s time.nist.gov
# Enable NTP servicesudo systemctl enable ntpsudo systemctl start ntp
# Verify timedatetimedatectl statusInstallation
Installing Google Authenticator
# Debian/Ubuntusudo apt updatesudo apt install libpam-google-authenticator
# RHEL/CentOS/Fedorasudo yum install epel-releasesudo yum install google-authenticator
# From source (if packages unavailable)git clone https://github.com/google/google-authenticator-libpam.gitcd google-authenticator-libpam./bootstrap.sh./configuremakesudo make installVerify Installation
# Check PAM modulels -la /lib/x86_64-linux-gnu/security/pam_google_authenticator.so# orls -la /lib64/security/pam_google_authenticator.so
# Test command availabilitywhich google-authenticatorUser Configuration
Generate Secret Key
# Run as the user (not root)google-authenticator
# Interactive prompts:# Do you want authentication tokens to be time-based? yes# Do you want me to update your ~/.google_authenticator file? yes# Do you want to disallow multiple uses? yes# Increase time window? no# Enable rate-limiting? yesConfiguration Options
graph TD A[Google Authenticator Setup] --> B[Time-based tokens] A --> C[Counter-based tokens]
B --> D[30-second window] B --> E[QR Code generation] B --> F[Emergency codes]
C --> G[HOTP mode]
D --> H[Clock sync critical] E --> I[Mobile app scan] F --> J[Backup access]
style A fill:#4ecdc4,stroke:#087f5b,stroke-width:2px style B fill:#74c0fc,stroke:#1971c2,stroke-width:2px style F fill:#ffd43b,stroke:#fab005,stroke-width:2pxNon-Interactive Setup
#!/bin/bash# generate-2fa.sh - Non-interactive 2FA setup
# Generate secret for a usergenerate_2fa_secret() { local user=$1
# Generate with specific options su - $user -c "google-authenticator \ --time-based \ --disallow-reuse \ --force \ --rate-limit=3 \ --rate-time=30 \ --window-size=3 \ --secret=/home/$user/.google_authenticator"
# Display QR code su - $user -c "google-authenticator \ --time-based \ --qr-mode=utf8"}
# Usagegenerate_2fa_secret "username"PAM Configuration
Configure PAM for SSH
Edit /etc/pam.d/sshd:
# Backup original filesudo cp /etc/pam.d/sshd /etc/pam.d/sshd.backup
# Edit PAM configurationsudo nano /etc/pam.d/sshdAdd the following line:
# For required 2FA (must pass)auth required pam_google_authenticator.so
# For optional 2FA (can bypass if not configured)auth required pam_google_authenticator.so nullok
# With custom optionsauth required pam_google_authenticator.so nullok grace_period=30 debugPAM Configuration Examples
# Standard configuration (password + 2FA)@include common-authauth required pam_google_authenticator.so
# Public key + 2FA only#@include common-authauth required pam_google_authenticator.soAdvanced PAM Options
# Full options exampleauth required pam_google_authenticator.so \ secret=/home/${USER}/.google_authenticator \ user=root \ forward_pass \ debug \ authtok_prompt="Verification code: " \ nullok \ grace_period=30SSH Configuration
Enable Challenge-Response
Edit /etc/ssh/sshd_config:
# Backup SSH configsudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
# Edit configurationsudo nano /etc/ssh/sshd_configRequired settings:
# Enable challenge-response authenticationChallengeResponseAuthentication yes
# For public key + 2FAAuthenticationMethods publickey,keyboard-interactive
# For password + 2FAAuthenticationMethods password,keyboard-interactive
# Or allow either method + 2FAAuthenticationMethods publickey,keyboard-interactive password,keyboard-interactive
# Ensure PAM is enabledUsePAM yes
# Optional: Disable password-only authPasswordAuthentication noPer-User Configuration
# Match specific users for 2FAMatch User admin,root AuthenticationMethods publickey,keyboard-interactive
Match Group sudo AuthenticationMethods publickey,keyboard-interactive
Match User serviceaccount AuthenticationMethods publickeyRestart SSH Service
# Test configurationsudo sshd -t
# Restart SSH servicesudo systemctl restart sshd
# Monitor logssudo journalctl -fu sshdTesting and Verification
Test Authentication Flow
sequenceDiagram participant U as User participant S as SSH Client participant D as SSH Server participant P as PAM Module participant G as Google Auth participant A as Auth App
U->>S: ssh user@server S->>D: Connection request D->>S: Public key challenge S->>D: Public key response D->>P: Verify authentication P->>G: Request TOTP G->>S: Prompt for code U->>A: Check code A->>U: Display code U->>S: Enter code S->>D: Submit code D->>G: Verify TOTP G->>P: Valid P->>D: Authentication success D->>S: Access grantedTest Commands
# Test with verbose outputssh -v user@server
# Test specific authentication methodssh -o PreferredAuthentications=publickey,keyboard-interactive user@server
# Test from different terminal# Keep current session open!ssh user@localhostAdvanced Configuration
Backup Codes
# Generate additional backup codesgoogle-authenticator --emergency-codes=10
# Store securelycat ~/.google_authenticator | grep -E "^[0-9]{8}$" > ~/backup-codes.txtchmod 600 ~/backup-codes.txtMultiple Devices
#!/bin/bash# Extract secret keySECRET=$(head -1 ~/.google_authenticator)
# Generate QR code URLUSER=$(whoami)HOSTNAME=$(hostname)URL="otpauth://totp/${USER}@${HOSTNAME}?secret=${SECRET}&issuer=SSH"
# Display QR codeqrencode -t UTF8 "$URL"
# Or save as imageqrencode -o ~/qr-code.png "$URL"Conditional 2FA
# Skip 2FA for certain conditions# Create /etc/security/access-local.conf+ : ALL : 192.168.1.0/24+ : ALL : LOCAL- : ALL : ALL
# Update PAM configurationauth [success=1 default=ignore] pam_access.so accessfile=/etc/security/access-local.confauth required pam_google_authenticator.soSecurity Hardening
SELinux Configuration
# Check SELinux statusgetenforce
# Allow Google Authenticator in SELinuxsudo setsebool -P authlogin_yubikey on
# Create custom policy if neededsudo ausearch -c 'sshd' --raw | audit2allow -M my-sshd-2fasudo semodule -i my-sshd-2fa.ppFile Permissions
# Secure Google Authenticator fileschmod 600 ~/.google_authenticatorchmod 700 ~
# System-wide permissionssudo chmod 644 /etc/pam.d/sshdsudo chmod 644 /etc/ssh/sshd_configAudit Logging
# Enable detailed PAM logging# Add to /etc/pam.d/sshdauth required pam_google_authenticator.so debug
# Monitor authentication attemptssudo tail -f /var/log/auth.log # Debian/Ubuntusudo tail -f /var/log/secure # RHEL/CentOS
# Configure rsyslog for 2FA eventsecho "auth.* /var/log/2fa.log" | sudo tee -a /etc/rsyslog.confsudo systemctl restart rsyslogAutomation Scripts
Bulk User Setup
#!/bin/bashUSERS_FILE="users.txt"QR_OUTPUT_DIR="/tmp/qr-codes"
mkdir -p "$QR_OUTPUT_DIR"
while IFS= read -r username; do echo "Setting up 2FA for $username..."
# Generate secret su - "$username" -c "google-authenticator \ --time-based \ --disallow-reuse \ --force \ --rate-limit=3 \ --rate-time=30 \ --window-size=3"
# Extract secret and generate QR SECRET=$(su - "$username" -c "head -1 ~/.google_authenticator") URL="otpauth://totp/${username}@$(hostname)?secret=${SECRET}"
# Save QR code qrencode -o "$QR_OUTPUT_DIR/${username}-qr.png" "$URL"
echo "QR code saved to: $QR_OUTPUT_DIR/${username}-qr.png"done < "$USERS_FILE"Monitoring Script
#!/bin/bashLOG_FILE="/var/log/auth.log"ALERT_EMAIL="admin@example.com"
# Monitor for 2FA failurestail -F "$LOG_FILE" | while read line; do if echo "$line" | grep -q "Failed to authenticate"; then echo "2FA failure detected: $line" | \ mail -s "2FA Authentication Failure" "$ALERT_EMAIL" fidoneRecovery Procedures
Lost Device Recovery
graph TD A[Lost Device] --> B{Backup Codes?} B -->|Yes| C[Use Backup Code] B -->|No| D{Root Access?}
C --> E[Login Success]
D -->|Yes| F[Disable 2FA Temporarily] D -->|No| G[Contact Admin]
F --> H[Regenerate Secret] G --> I[Admin Assistance]
H --> J[Re-enable 2FA] I --> J
style A fill:#ff6b6b,stroke:#c92a2a,stroke-width:2px style E fill:#51cf66,stroke:#2f9e44,stroke-width:2px style J fill:#74c0fc,stroke:#1971c2,stroke-width:2pxEmergency Access
# As root, temporarily disable 2FA for a user# Option 1: Remove .google_authenticator filesudo mv /home/user/.google_authenticator /home/user/.google_authenticator.disabled
# Option 2: Comment out PAM linesudo sed -i 's/^auth required pam_google_authenticator.so/#&/' /etc/pam.d/sshdsudo systemctl restart sshd
# Option 3: Add bypass for specific userecho "auth sufficient pam_succeed_if.so user = emergencyuser" | \ sudo tee -a /etc/pam.d/sshdRegenerate Secret
#!/bin/bashUSER=$1
if [ -z "$USER" ]; then echo "Usage: $0 <username>" exit 1fi
# Backup old secretsudo mv /home/$USER/.google_authenticator \ /home/$USER/.google_authenticator.old
# Generate new secretsudo -u $USER google-authenticator \ --time-based \ --disallow-reuse \ --force \ --rate-limit=3 \ --rate-time=30 \ --window-size=3
echo "New 2FA secret generated for $USER"Troubleshooting
Common Issues
-
Time Sync Problems
Terminal window # Check time differencentpdate -q time.google.com# Force syncsudo ntpdate -s time.google.com# Increase time windowauth required pam_google_authenticator.so window_size=10 -
SELinux Denials
Terminal window # Check SELinux logssudo ausearch -m avc -ts recent# Generate policysudo audit2allow -a -M google_authsudo semodule -i google_auth.pp -
PAM Module Not Found
Terminal window # Find PAM modulefind /lib* -name "pam_google_authenticator.so"# Create symlink if neededsudo ln -s /usr/lib64/security/pam_google_authenticator.so \/lib64/security/pam_google_authenticator.so
Debug Mode
# Enable SSH debugsudo /usr/sbin/sshd -d -p 2222
# Enable PAM debugauth required pam_google_authenticator.so debug
# Check system logsjournalctl -xe | grep -i "pam\|ssh\|google"Best Practices
Security Recommendations
graph TD A[2FA Best Practices] --> B[Regular Updates] A --> C[Secure Storage] A --> D[Monitoring] A --> E[Backup Plans]
B --> F[Update PAM modules] B --> G[Patch SSH server]
C --> H[Encrypt backup codes] C --> I[Secure secret files]
D --> J[Log analysis] D --> K[Failed attempts alerts]
E --> L[Multiple admins with 2FA] E --> M[Recovery procedures]
style A fill:#4ecdc4,stroke:#087f5b,stroke-width:2px style C fill:#ff6b6b,stroke:#c92a2a,stroke-width:2px style D fill:#74c0fc,stroke:#1971c2,stroke-width:2pxImplementation Checklist
- Time synchronization configured
- Google Authenticator installed
- PAM module configured
- SSH settings updated
- Test authentication working
- Backup codes generated
- Recovery procedures documented
- Monitoring configured
- Team training completed
- Documentation updated
Integration Examples
Ansible Playbook
---- name: Configure Google Authenticator 2FA hosts: all become: yes tasks: - name: Install Google Authenticator package: name: google-authenticator state: present
- name: Configure PAM lineinfile: path: /etc/pam.d/sshd line: "auth required pam_google_authenticator.so nullok" insertafter: "@include common-auth"
- name: Configure SSH lineinfile: path: /etc/ssh/sshd_config regexp: "^ChallengeResponseAuthentication" line: "ChallengeResponseAuthentication yes" notify: restart sshd
handlers: - name: restart sshd service: name: sshd state: restartedDocker Container Support
FROM ubuntu:22.04
RUN apt-get update && \ apt-get install -y openssh-server google-authenticator
# Configure SSH for containerRUN mkdir /var/run/sshd && \ echo 'ChallengeResponseAuthentication yes' >> /etc/ssh/sshd_config && \ echo 'UsePAM yes' >> /etc/ssh/sshd_config
# Add PAM configurationRUN echo "auth required pam_google_authenticator.so nullok" >> /etc/pam.d/sshd
EXPOSE 22CMD ["/usr/sbin/sshd", "-D"]Conclusion
Implementing Google Authenticator for SSH provides robust two-factor authentication that significantly enhances server security. Key benefits include:
- Strong Security: Time-based tokens resistant to replay attacks
- Easy Integration: Works with existing SSH infrastructure
- Flexible Configuration: Supports various authentication combinations
- Wide Compatibility: Works with multiple authenticator apps
- Cost-Effective: Free and open-source solution
Best practices for deployment:
- Always test in a non-production environment first
- Keep backup codes in a secure location
- Document recovery procedures
- Monitor authentication logs
- Regularly update security components
- Train users on proper 2FA usage
With proper implementation, Google Authenticator provides enterprise-grade security for SSH access while maintaining usability.
Securing SSH with Google Authenticator: Two-Factor Authentication Setup
https://mranv.pages.dev/posts/google-authentication-ssh-setup/