When securing an Ubuntu server with both internal and external IP addresses, you might want different authentication requirements for each access point. For public-facing SSH access, two-factor authentication (2FA) adds a critical security layer, while internal connections might maintain simpler authentication for operational efficiency.
Before configuration, ensure you have:
- Ubuntu Server 18.04 LTS or later
- SSH server installed and running
- Root or sudo privileges
- Google Authenticator PAM module (libpam-google-authenticator)
First, install the necessary package:
sudo apt update
sudo apt install libpam-google-authenticator
Then modify the SSH configuration to apply 2FA only to public IP connections. Edit /etc/ssh/sshd_config
:
Match Address [YOUR_PUBLIC_IP]
AuthenticationMethods publickey,keyboard-interactive
PasswordAuthentication no
ChallengeResponseAuthentication yes
UsePAM yes
Match all
PasswordAuthentication no
PubkeyAuthentication yes
For each user needing public access:
google-authenticator
# Follow the interactive prompts to generate QR code
# Save emergency scratch codes securely
Create a custom PAM configuration file at /etc/pam.d/sshd_public
:
auth required pam_google_authenticator.so
auth required pam_unix.so use_first_pass
account required pam_unix.so
Then modify /etc/pam.d/sshd
to conditionally include this configuration:
auth [success=1 default=ignore] pam_access.so accessfile=/etc/security/public_ips.conf
auth required pam_deny.so
auth include sshd_public
Always maintain a separate SSH connection when testing configuration changes. Verify the setup:
ssh -v user@public_ip
# Should prompt for both SSH key and OTP
ssh -v user@private_ip
# Should only require SSH key
Common issues to check:
- SELinux/AppArmor permissions on PAM modules
- Firewall rules blocking UDP/123 for NTP (time sync critical for OTP)
- Correct file permissions on
~/.google_authenticator
When managing servers with both internal and external interfaces, security policies often need to differ between trusted local networks and public-facing access. Here's how to implement selective 2FA for SSH:
Edit your /etc/ssh/sshd_config
with these conditional blocks:
# Public IP specific configuration Match Address YOUR_PUBLIC_IP AuthenticationMethods publickey,keyboard-interactive UsePAM yes # Internal network configuration Match Address YOUR_INTERNAL_NETWORK AuthenticationMethods publickey UsePAM no
Install the PAM module:
sudo apt-get install libpam-google-authenticator -y
Configure PAM by editing /etc/pam.d/sshd
:
# Only apply to public IP connections auth [success=1 default=ignore] pam_access.so accessfile=/etc/security/public-ip-access.conf auth required pam_google_authenticator.so
Create the access control file at /etc/security/public-ip-access.conf
:
# Only require 2FA from public IP +: ALL : YOUR_PUBLIC_IP -: ALL : ALL
For more flexibility, consider these methods:
1. TCP Wrappers with PAM:
# /etc/hosts.allow sshd: LOCAL_NETWORK sshd: PUBLIC_IP : spawn (/usr/bin/logger -t sshd "Public IP login attempt") & : pam_enable
2. Conditional PAM via iptables MARK:
# Mark packets from public IP sudo iptables -t mangle -A PREROUTING -p tcp --dport 22 -d YOUR_PUBLIC_IP -j MARK --set-mark 0x1 # Then match in PAM: auth [success=1 default=ignore] pam_exec.so quiet /usr/sbin/iptables -L -t mangle -n | grep -q MARK\ 0x1 auth required pam_google_authenticator.so
Always test with two separate connections:
# Test internal access (should skip 2FA) ssh user@internal_ip # Test external access (should prompt for token) ssh user@public_ip
Check logs for verification:
tail -f /var/log/auth.log | grep -E 'sshd|pam'
If 2FA triggers for internal IPs:
- Verify netmasks in access files
- Check for conflicting PAM rules
- Test with
pamtester
utility
Remember to restart SSH after changes:
sudo systemctl restart sshd