1607 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:2px

Prerequisites#

System Requirements#

Terminal window
# Check system information
cat /etc/os-release
uname -a
# Ensure SSH is installed
ssh -V
# Check PAM version
rpm -qa | grep pam # RHEL/CentOS
dpkg -l | grep libpam # Debian/Ubuntu

Time Synchronization#

Terminal window
# Install NTP client
sudo apt install ntp # Debian/Ubuntu
sudo yum install ntp # RHEL/CentOS
# Sync time
sudo ntpdate -s time.nist.gov
# Enable NTP service
sudo systemctl enable ntp
sudo systemctl start ntp
# Verify time
date
timedatectl status

Installation#

Installing Google Authenticator#

Terminal window
# Debian/Ubuntu
sudo apt update
sudo apt install libpam-google-authenticator
# RHEL/CentOS/Fedora
sudo yum install epel-release
sudo yum install google-authenticator
# From source (if packages unavailable)
git clone https://github.com/google/google-authenticator-libpam.git
cd google-authenticator-libpam
./bootstrap.sh
./configure
make
sudo make install

Verify Installation#

Terminal window
# Check PAM module
ls -la /lib/x86_64-linux-gnu/security/pam_google_authenticator.so
# or
ls -la /lib64/security/pam_google_authenticator.so
# Test command availability
which google-authenticator

User Configuration#

Generate Secret Key#

Terminal window
# 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? yes

Configuration 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:2px

Non-Interactive Setup#

#!/bin/bash
# generate-2fa.sh - Non-interactive 2FA setup
# Generate secret for a user
generate_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"
}
# Usage
generate_2fa_secret "username"

PAM Configuration#

Configure PAM for SSH#

Edit /etc/pam.d/sshd:

Terminal window
# Backup original file
sudo cp /etc/pam.d/sshd /etc/pam.d/sshd.backup
# Edit PAM configuration
sudo nano /etc/pam.d/sshd

Add the following line:

Terminal window
# 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 options
auth required pam_google_authenticator.so nullok grace_period=30 debug

PAM Configuration Examples#

Terminal window
# Standard configuration (password + 2FA)
@include common-auth
auth required pam_google_authenticator.so
# Public key + 2FA only
#@include common-auth
auth required pam_google_authenticator.so

Advanced PAM Options#

Terminal window
# Full options example
auth required pam_google_authenticator.so \
secret=/home/${USER}/.google_authenticator \
user=root \
forward_pass \
debug \
authtok_prompt="Verification code: " \
nullok \
grace_period=30

SSH Configuration#

Enable Challenge-Response#

Edit /etc/ssh/sshd_config:

Terminal window
# Backup SSH config
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
# Edit configuration
sudo nano /etc/ssh/sshd_config

Required settings:

Terminal window
# Enable challenge-response authentication
ChallengeResponseAuthentication yes
# For public key + 2FA
AuthenticationMethods publickey,keyboard-interactive
# For password + 2FA
AuthenticationMethods password,keyboard-interactive
# Or allow either method + 2FA
AuthenticationMethods publickey,keyboard-interactive password,keyboard-interactive
# Ensure PAM is enabled
UsePAM yes
# Optional: Disable password-only auth
PasswordAuthentication no

Per-User Configuration#

Terminal window
# Match specific users for 2FA
Match User admin,root
AuthenticationMethods publickey,keyboard-interactive
Match Group sudo
AuthenticationMethods publickey,keyboard-interactive
Match User serviceaccount
AuthenticationMethods publickey

Restart SSH Service#

Terminal window
# Test configuration
sudo sshd -t
# Restart SSH service
sudo systemctl restart sshd
# Monitor logs
sudo journalctl -fu sshd

Testing 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 granted

Test Commands#

Terminal window
# Test with verbose output
ssh -v user@server
# Test specific authentication method
ssh -o PreferredAuthentications=publickey,keyboard-interactive user@server
# Test from different terminal
# Keep current session open!
ssh user@localhost

Advanced Configuration#

Backup Codes#

Terminal window
# Generate additional backup codes
google-authenticator --emergency-codes=10
# Store securely
cat ~/.google_authenticator | grep -E "^[0-9]{8}$" > ~/backup-codes.txt
chmod 600 ~/backup-codes.txt

Multiple Devices#

share-2fa-secret.sh
#!/bin/bash
# Extract secret key
SECRET=$(head -1 ~/.google_authenticator)
# Generate QR code URL
USER=$(whoami)
HOSTNAME=$(hostname)
URL="otpauth://totp/${USER}@${HOSTNAME}?secret=${SECRET}&issuer=SSH"
# Display QR code
qrencode -t UTF8 "$URL"
# Or save as image
qrencode -o ~/qr-code.png "$URL"

Conditional 2FA#

Terminal window
# Skip 2FA for certain conditions
# Create /etc/security/access-local.conf
+ : ALL : 192.168.1.0/24
+ : ALL : LOCAL
- : ALL : ALL
# Update PAM configuration
auth [success=1 default=ignore] pam_access.so accessfile=/etc/security/access-local.conf
auth required pam_google_authenticator.so

Security Hardening#

SELinux Configuration#

Terminal window
# Check SELinux status
getenforce
# Allow Google Authenticator in SELinux
sudo setsebool -P authlogin_yubikey on
# Create custom policy if needed
sudo ausearch -c 'sshd' --raw | audit2allow -M my-sshd-2fa
sudo semodule -i my-sshd-2fa.pp

File Permissions#

Terminal window
# Secure Google Authenticator files
chmod 600 ~/.google_authenticator
chmod 700 ~
# System-wide permissions
sudo chmod 644 /etc/pam.d/sshd
sudo chmod 644 /etc/ssh/sshd_config

Audit Logging#

Terminal window
# Enable detailed PAM logging
# Add to /etc/pam.d/sshd
auth required pam_google_authenticator.so debug
# Monitor authentication attempts
sudo tail -f /var/log/auth.log # Debian/Ubuntu
sudo tail -f /var/log/secure # RHEL/CentOS
# Configure rsyslog for 2FA events
echo "auth.* /var/log/2fa.log" | sudo tee -a /etc/rsyslog.conf
sudo systemctl restart rsyslog

Automation Scripts#

Bulk User Setup#

bulk-2fa-setup.sh
#!/bin/bash
USERS_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#

monitor-2fa-auth.sh
#!/bin/bash
LOG_FILE="/var/log/auth.log"
ALERT_EMAIL="admin@example.com"
# Monitor for 2FA failures
tail -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"
fi
done

Recovery 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:2px

Emergency Access#

Terminal window
# As root, temporarily disable 2FA for a user
# Option 1: Remove .google_authenticator file
sudo mv /home/user/.google_authenticator /home/user/.google_authenticator.disabled
# Option 2: Comment out PAM line
sudo sed -i 's/^auth required pam_google_authenticator.so/#&/' /etc/pam.d/sshd
sudo systemctl restart sshd
# Option 3: Add bypass for specific user
echo "auth sufficient pam_succeed_if.so user = emergencyuser" | \
sudo tee -a /etc/pam.d/sshd

Regenerate Secret#

regenerate-2fa.sh
#!/bin/bash
USER=$1
if [ -z "$USER" ]; then
echo "Usage: $0 <username>"
exit 1
fi
# Backup old secret
sudo mv /home/$USER/.google_authenticator \
/home/$USER/.google_authenticator.old
# Generate new secret
sudo -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#

  1. Time Sync Problems

    Terminal window
    # Check time difference
    ntpdate -q time.google.com
    # Force sync
    sudo ntpdate -s time.google.com
    # Increase time window
    auth required pam_google_authenticator.so window_size=10
  2. SELinux Denials

    Terminal window
    # Check SELinux logs
    sudo ausearch -m avc -ts recent
    # Generate policy
    sudo audit2allow -a -M google_auth
    sudo semodule -i google_auth.pp
  3. PAM Module Not Found

    Terminal window
    # Find PAM module
    find /lib* -name "pam_google_authenticator.so"
    # Create symlink if needed
    sudo ln -s /usr/lib64/security/pam_google_authenticator.so \
    /lib64/security/pam_google_authenticator.so

Debug Mode#

Terminal window
# Enable SSH debug
sudo /usr/sbin/sshd -d -p 2222
# Enable PAM debug
auth required pam_google_authenticator.so debug
# Check system logs
journalctl -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:2px

Implementation 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: restarted

Docker Container Support#

FROM ubuntu:22.04
RUN apt-get update && \
apt-get install -y openssh-server google-authenticator
# Configure SSH for container
RUN mkdir /var/run/sshd && \
echo 'ChallengeResponseAuthentication yes' >> /etc/ssh/sshd_config && \
echo 'UsePAM yes' >> /etc/ssh/sshd_config
# Add PAM configuration
RUN echo "auth required pam_google_authenticator.so nullok" >> /etc/pam.d/sshd
EXPOSE 22
CMD ["/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:

  1. Always test in a non-production environment first
  2. Keep backup codes in a secure location
  3. Document recovery procedures
  4. Monitor authentication logs
  5. Regularly update security components
  6. 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/
Author
Anubhav Gain
Published at
2024-11-23
License
CC BY-NC-SA 4.0