SSH Security and Access Control

4 Nov 2026, 11:2825 min readby ITYantra Admin

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-sftp

Validate 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/.ssh

Phase 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_admin

During 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_hosts

Connect:

ssh ityantra-server

Phase 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:100

Restrict 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 no

Access internal server:

ssh server-a.internal

This 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 certificates

Distribute 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.pub

Client uses the certificate:

ssh -i ~/.ssh/id_ed25519_admin admin@server-ip

SSH 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.pub

Distribute 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 ssh

Phase 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 sssd

In `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 DROP

Using sshd Match:

Match Address 10.0.10.0/24
    AllowUsers admin deploy

Match Address *
    AllowUsers none

Verification 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)
#access-control#port-forwarding#ssh-certificates#fail2ban#bastion-host#key-based-authentication#ssh-security#ssh