SSH Security and Access Control
Complete guide to securing SSH including key-based authentication, fail2ban, bastion hosts, certificate-based auth, port forwarding, and access control.
# SSH Security and Access Control
Introduction
Secure Shell (SSH) is the primary protocol for remote administration of Linux and Unix servers. It is also one of the most frequently attacked services, with brute-force attacks, credential stuffing, and exploitation of misconfigurations being common.
This tutorial provides a comprehensive guide to securing SSH deployments. Topics include key-based authentication, SSH hardening, bastion hosts, certificate-based authentication, port forwarding security, and access control integration.
Learning Objectives
By the end of this tutorial, you will be able to:
- Configure SSH daemon for maximum security
- Deploy key-based authentication with passphrase-protected keys
- Implement SSH bastion hosts for jump-box access
- Use SSH certificates for short-lived, audited access
- Secure SSH port forwarding and tunneling
- Integrate SSH access with identity providers and MFA
- Monitor and audit SSH sessions
Technical Background
SSH Protocol Architecture
SSH operates at the application layer (TCP port 22 by default) and provides:
1. **Transport Layer Protocol:** Server authentication, key exchange, encryption, integrity 2. **User Authentication Protocol:** Password, public key, keyboard-interactive, certificate 3. **Connection Protocol:** Multiplexing multiple channels over one connection
Authentication Methods
| Method | Security Level | Description | |--------|---------------|-------------| | Password | Low | Vulnerable to brute-force and credential stuffing | | Public Key | High | Cryptographic key pair, resistant to brute-force | | Certificate | Very High | Short-lived, CA-signed, automatic expiration | | Keyboard-Interactive | Medium | Supports OTP, PAM modules | | Host-based | Low | Trust based on source host (weak) |
Common SSH Attacks
| Attack | Description | Mitigation | |--------|-------------|------------| | Brute-force | Automated password guessing | Key-based auth, fail2ban, rate limiting | | Credential stuffing | Reusing breached credentials | Key-based auth, unique passwords | | Man-in-the-middle | Intercepting SSH connections | Host key verification, SSHFP DNS records | | Key theft | Stealing private keys | Passphrase protection, hardware keys, agent forwarding restrictions | | Port forwarding abuse | Using SSH to bypass network controls | Disable forwarding, restrict allowed users |
Prerequisites
- Linux server with OpenSSH server installed
- Root or sudo access
- Client machine with OpenSSH client
- Understanding of public key cryptography
Hardware and Software Requirements
| Component | Requirement | |-----------|-------------| | Server | OpenSSH 8.0+ (OpenSSH 8.8+ for certificate features) | | Client | OpenSSH client, PuTTY (Windows), or terminal | | Bastion | Dedicated bastion host or cloud instance | | CA | SSH certificate authority (optional, for certificate auth) | | MFA | Google Authenticator, Duo, or OATH-compatible (optional) |
Step-by-Step Implementation
Phase 1: SSH Daemon Hardening
#### 1.1 Configure sshd_config
Edit `/etc/ssh/sshd_config`:
# --- Network ---
Port 22
AddressFamily inet
ListenAddress 0.0.0.0
# ListenAddress 10.0.1.50 # Restrict to specific IP if needed
# --- Authentication ---
LoginGraceTime 60
PermitRootLogin no
StrictModes yes
MaxAuthTries 3
MaxSessions 2
MaxStartups 10:30:100
# --- Authentication Methods ---
PasswordAuthentication no
PermitEmptyPasswords no
PubkeyAuthentication yes
PubkeyAcceptedAlgorithms +ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com
AuthorizedKeysFile .ssh/authorized_keys
AuthorizedKeysCommand none
AuthorizedKeysCommandUser nobody
# Challenge-Response / Keyboard-Interactive
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
# GSSAPI
GSSAPIAuthentication no
# Host-based authentication
HostbasedAuthentication no
IgnoreUserKnownHosts yes
IgnoreRhosts yes
# --- Cryptography ---
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512
HostKeyAlgorithms ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519
# --- Session Controls ---
ClientAliveInterval 300
ClientAliveCountMax 3
AllowTcpForwarding no
AllowStreamLocalForwarding no
GatewayPorts no
X11Forwarding no
PermitTTY yes
PermitUserRC no
PermitListen no
# --- Logging ---
SyslogFacility AUTH
LogLevel VERBOSE
# --- Subsystem ---
Subsystem sftp /usr/lib/openssh/sftp-server
# or use internal-sftp for chrooted SFTP:
# Subsystem sftp internal-sftpValidate and apply:
sudo sshd -t
sudo systemctl reload ssh
# or: sudo systemctl reload sshd#### 1.2 Disable Root Login and Password Auth
Critical settings:
- `PermitRootLogin no` — Prevent direct root login; use sudo instead
- `PasswordAuthentication no` — Force key-based authentication
Create a dedicated admin user:
sudo useradd -m -s /bin/bash admin
sudo usermod -aG sudo admin
sudo mkdir -p /home/admin/.ssh
sudo chmod 700 /home/admin/.sshPhase 2: Key-Based Authentication
#### 2.1 Generate SSH Key Pair
On the client machine:
# Generate Ed25519 key (recommended)
ssh-keygen -t ed25519 -a 100 -f ~/.ssh/id_ed25519_admin -C "admin@ityantra.co.in"
# For hardware security key (FIDO2)
ssh-keygen -t sk-ed25519@openssh.com -f ~/.ssh/id_sk_ed25519_adminDuring generation, set a strong passphrase. The private key is encrypted with this passphrase.
#### 2.2 Deploy Public Key to Server
# Using ssh-copy-id
ssh-copy-id -i ~/.ssh/id_ed25519_admin.pub admin@server-ip
# Manual method
cat ~/.ssh/id_ed25519_admin.pub | ssh admin@server-ip "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"Verify permissions:
# On server
sudo chmod 700 /home/admin/.ssh
sudo chmod 600 /home/admin/.ssh/authorized_keys
sudo chown -R admin:admin /home/admin/.ssh#### 2.3 Configure SSH Client
Create `~/.ssh/config`:
Host ityantra-server
HostName 10.0.1.50
User admin
IdentityFile ~/.ssh/id_ed25519_admin
IdentitiesOnly yes
ForwardAgent no
ForwardX11 no
ServerAliveInterval 60
ServerAliveCountMax 3
ConnectTimeout 10
StrictHostKeyChecking yes
UserKnownHostsFile ~/.ssh/known_hostsConnect:
ssh ityantra-serverPhase 3: SSH Bastion Host
A bastion host (jump box) provides a single controlled entry point for SSH access to internal servers.
#### 3.1 Bastion Host Architecture
Internet --- Bastion Host (203.0.113.10) --- Internal Network
|
+--- Server A (10.0.1.10)
+--- Server B (10.0.1.11)
+--- Server C (10.0.1.12)#### 3.2 Configure Bastion Host
On bastion host (`/etc/ssh/sshd_config`):
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AllowTcpForwarding yes
AllowStreamLocalForwarding yes
PermitListen yes
PermitUserRC no
MaxStartups 10:30:100Restrict which users can use bastion:
AllowUsers admin deploy#### 3.3 Configure Client for Bastion Access
`~/.ssh/config`:
Host bastion
HostName 203.0.113.10
User admin
IdentityFile ~/.ssh/id_ed25519_admin
IdentitiesOnly yes
Host *.internal
ProxyJump bastion
User admin
IdentityFile ~/.ssh/id_ed25519_admin
IdentitiesOnly yes
ForwardAgent noAccess internal server:
ssh server-a.internalThis transparently connects through the bastion host.
Phase 4: SSH Certificate-Based Authentication
SSH certificates provide short-lived, automatically expiring access with centralized revocation.
#### 4.1 Set Up SSH CA
On the CA server:
# Generate CA key pair
ssh-keygen -t ed25519 -f /etc/ssh/ca/ca_key -N ""
# The public key (ca_key.pub) is distributed to all SSH servers
# The private key (ca_key) signs user and host certificatesDistribute CA public key to all SSH servers (`/etc/ssh/sshd_config`):
TrustedUserCAKeys /etc/ssh/ca/ca_key.pub#### 4.2 Sign User Certificate
On CA server:
# Sign user's public key for 8-hour validity
ssh-keygen -s /etc/ssh/ca/ca_key \
-I admin@ityantra.co.in \
-n admin \
-V +8h \
~/.ssh/id_ed25519_admin.pub
# Output: id_ed25519_admin-cert.pubClient uses the certificate:
ssh -i ~/.ssh/id_ed25519_admin admin@server-ipSSH automatically uses the `-cert.pub` file if present.
#### 4.3 Host Certificates (Optional)
Sign host keys to prevent man-in-the-middle:
# On CA server, sign host's public key
ssh-keygen -s /etc/ssh/ca/ca_key \
-H \
-I server-01 \
-h \
-n server-01.internal \
-V +52w \
/etc/ssh/ssh_host_ed25519_key.pubDistribute to clients via `known_hosts` with `@cert-authority`:
@cert-authority *.internal ssh-ed25519 AAAAC3NzaC1lZDI1NTE5...Phase 5: Port Forwarding Security
SSH port forwarding can bypass network controls. Restrict it carefully.
#### 5.1 Disable Unnecessary Forwarding
In `/etc/ssh/sshd_config`:
AllowTcpForwarding no
AllowStreamLocalForwarding no
GatewayPorts no
PermitListen no
PermitUserRC no#### 5.2 Allow Forwarding for Specific Users (If Required)
Using Match blocks:
Match User deploy
AllowTcpForwarding yes
AllowStreamLocalForwarding yes
PermitListen 8080#### 5.3 Detect Port Forwarding
Monitor for dynamic port forwarding:
# Check for active SSH forwarding
ss -tlnp | grep ssh
# Check for SSH processes with forwarding flags
ps aux | grep sshPhase 6: Access Control and Integration
#### 6.1 Use LDAP/AD for Centralized Authentication
Configure SSSD for LDAP:
# Ubuntu/Debian
sudo apt install -y sssd sssd-tools ldap-utils
# /etc/sssd/sssd.conf
[domain/example.com]
id_provider = ldap
auth_provider = ldap
ldap_uri = ldaps://ldap.example.com
ldap_search_base = dc=example,dc=com
ldap_user_ssh_public_key = sshPublicKey
cache_credentials = true
[sssd]
services = nss, pam, ssh
domains = example.com
[nss]
[pam]
[ssh]sudo systemctl restart sssdIn `sshd_config`:
AuthorizedKeysCommand /usr/bin/sss_ssh_authorizedkeys
AuthorizedKeysCommandUser nobody#### 6.2 SSH with MFA (Google Authenticator)
# Install Google Authenticator PAM module
sudo apt install -y libpam-google-authenticator
# Generate user secrets
google-authenticator
# In /etc/pam.d/sshd, add:
auth required pam_google_authenticator.so secret=/home/${USER}/.google_authenticator
# In sshd_config:
KbdInteractiveAuthentication yes
PasswordAuthentication no
ChallengeResponseAuthentication yes#### 6.3 Restrict SSH Access by Source IP
Using firewall:
# UFW
sudo ufw allow from 10.0.10.0/24 to any port 22 proto tcp
# iptables
iptables -A INPUT -s 10.0.10.0/24 -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROPUsing sshd Match:
Match Address 10.0.10.0/24
AllowUsers admin deploy
Match Address *
AllowUsers noneVerification and Expected Outputs
| Check | Method | Expected Result | |-------|--------|-----------------| | Password auth disabled | `ssh -o PasswordAuthentication=yes user@host` | Permission denied | | Root login disabled | `ssh root@host` | Permission denied | | Key-based auth works | `ssh -i ~/.ssh/id_ed25519_admin user@host` | Login succeeds | | SSH config valid | `sudo sshd -t` | No errors | | Fail2ban active | `sudo fail2ban-client status sshd` | Jail listed | | Host key verified | First connection shows fingerprint | Matches expected fingerprint | | Certificate auth works | `ssh -i user-cert.pub user@host` | Login succeeds, expires after validity |
Troubleshooting Scenarios
| Issue | Diagnosis | Resolution | |-------|-----------|------------| | Permission denied (publickey) | Wrong key or permissions | Check `~/.ssh` permissions (700), `authorized_keys` (600) | | Connection refused | SSH not running or firewall blocking | Check `systemctl status ssh`, firewall rules | | Host key verification failed | Changed host key or MITM | Verify fingerprint, update known_hosts | | Agent forwarding not working | ForwardAgent disabled | Enable in client config or use `-A` flag | | Bastion connection times out | Bastion unreachable or wrong config | Verify bastion hostname, ProxyJump config | | Certificate expired | Certificate validity period passed | Re-sign certificate with longer validity | | Slow SSH connection | DNS resolution | Set `UseDNS no` in sshd_config |
Security Best Practices
- **Never use password authentication:** Key-based or certificate-based only
- **Protect private keys:** Use passphrases, hardware keys, or encrypted storage
- **Limit agent forwarding:** Only enable when necessary, disable by default
- **Use bastion hosts:** Never expose internal servers directly to the internet
- **Implement MFA:** For high-security environments, combine keys with OTP
- **Monitor SSH logs:** Forward auth.log to SIEM, alert on failed logins
- **Rotate keys regularly:** Replace user keys annually or after suspected compromise
- **Use allowlists:** Restrict SSH access to specific source IPs and users
- **Audit authorized_keys:** Regularly review and remove stale keys
- **Disable X11 forwarding:** Unless explicitly needed for GUI applications
Enterprise Considerations
- **Centralized key management:** Use SSH CA for certificate-based access at scale
- **Just-in-time access:** Issue short-lived certificates (1-8 hours) via PAM or vault
- **Session recording:** Record all privileged SSH sessions for compliance
- **Break-glass accounts:** Maintain emergency access accounts with strong controls
- **Source IP restrictions:** Limit SSH to management network or VPN
- **Key rotation policy:** Automate key rotation using configuration management
- **Compliance:** SSH access logs required for PCI DSS, HIPAA, SOC 2
Conclusion
SSH is a critical administrative channel that requires rigorous security controls. By combining key-based authentication, bastion hosts, certificate-based access, and careful daemon configuration, you create a secure remote access infrastructure. Regular monitoring, key rotation, and access reviews ensure SSH remains a trusted channel rather than an attack vector.
Frequently Asked Questions
**Q: Should I change the SSH port from 22?** A: Changing ports reduces automated scan noise but provides no real security. Focus on key-based auth, fail2ban, and firewall restrictions instead.
**Q: Can I use SSH certificates and keys together?** A: Yes. `sshd_config` supports both `TrustedUserCAKeys` (certificates) and `AuthorizedKeysFile` (keys). Clients can use either method.
**Q: How do I revoke a compromised SSH key?** A: For authorized_keys: remove the key from the file. For certificates: publish a CRL via `RevokedKeys` directive or reduce certificate validity and don't renew.
Official Technical References
- OpenSSH Manual: https://man.openbsd.org/sshd_config
- SSH Certificate Authentication: https://www.openssh.com/txt/certificates.proto
- CIS SSH Benchmark: https://www.cisecurity.org/
- NIST SP 800-77: Guide to IPsec VPNs (SSH alternative guidance): https://csrc.nist.gov/publications/detail/sp/800-77/final
Related ITYantra Tutorials
- Enterprise Cybersecurity Fundamentals (cybersecurity)
- Linux Server Security Hardening (cybersecurity)
- Identity and Access Management Fundamentals (cybersecurity)
- Linux Systemd Services and Boot Process (linux)
- Linux User Group and Permission Management (linux)