Learn how to secure SSH access with keys, disable root login, change default ports, and configure fail2ban with advanced rules.
Our SSH daemon is the front door to our server. Leaving it with default settings is akin to leaving our physical front door unlocked. A constant stream of automated bots, not just "script kiddies," relentlessly probe for weaknesses. Implementing a few critical steps can dramatically reduce our exposure and provide robust security.
This guide will walk us through the essential steps to harden our server's SSH access, making it resilient to the most common attacks.
Step 1: First rules of the house: audit what we have
Run these to see current SSH and firewall state.
Which sshd binary and version
sshd -v 2>&1 | head -n1
Show effective sshd config lines (non-comment)
sudo sshd -T
Check listening sockets
ss -tlpn | grep ssh || ss -tlpn
If using ufw / firewalld / nftables check status
sudo ufw status verbose
sudo firewall-cmd --state 2>/dev/null || true
sudo nft list ruleset 2>/dev/null || true
Step 2: Disable Root Login
Allowing direct SSH access for the root user is a major security risk. If an attacker guesses the root password, they have complete control over the system. The best practice is to log in as a standard user and use sudo for administrative tasks.
First, we'll open the SSH configuration file with a text editor. We use nano here for simplicity:
nano /etc/ssh/sshd_config
Example secure minimal config:
Port 2222 # optional: change from 22 (see note)
AddressFamily any
ListenAddress 0.0.0.0
PermitRootLogin no # absolutely: do not allow root SSH
PasswordAuthentication no # require keys
ChallengeResponseAuthentication no
UsePAM yes
X11Forwarding no
AllowTcpForwarding no # restrict unless needed
PermitEmptyPasswords no
PubkeyAuthentication yes
HostKey /etc/ssh/ssh_host_ed25519_key
# Force publickey only
AuthenticationMethods publickey
# Limit algorithm choices if needed (careful with compatibility)
KexAlgorithms curve25519-sha256@libssh.org
Apply changes:
# test config
sudo sshd -t
# reload sshd (systemd)
sudo systemctl reload sshd
Notes:
- PermitRootLogin no prevents direct root SSH; admin tasks use sudo. This is standard practice.
- PasswordAuthentication no removes password logins — only do this after key test passes. AuthenticationMethods publickey is stricter and enforces key-only
Step 3: Implement SSH Key Authentication
Password-based authentication is a relic. It's vulnerable to brute-force attacks and is fundamentally insecure. SSH keys, a form of public-key cryptography, provide a far more secure alternative. This is the single most important step in this guide. We will create a key pair on our local machine and then upload the public key to the server.
First, on our local machine, we generate a new key pair:
ssh-keygen -t ed25519 -C "our_email@example.com"
The -t ed25519
flag specifies a modern, highly secure key type. We should choose a strong passphrase for our key when prompted. The command will save two files in the ~/.ssh/ directory: id_ed25519
(our private key) and id_ed25519.pub
(our public key).
Next, we securely copy our public key to the server using the ssh-copy-id command. Replace [user] and [server_ip] with our information.
With ssh-copy-id (recommended)
ssh-copy-id -i ~/.ssh/id_ed25519.pub -p 22 alice@server.example.com
Or manually:
cat ~/.ssh/id_ed25519.pub | ssh -p 22 alice@server.example.com "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
This command will prompt us for our password one last time and then install the public key on the server, adding it to the authorized keys file.
Step 4: Configure Fail2ban
Even with the previous steps, bots will still attempt to connect. We can use a service called Fail2ban to automatically monitor logs for malicious activity and temporarily (or permanently) ban the offending IP addresses.
First, we need to install Fail2ban if it's not already on our system:
# Debian/Ubuntu
sudo apt update && sudo apt install -y fail2ban
# RHEL/CentOS/Fedora (dnf/yum)
sudo dnf install -y fail2ban || sudo yum install -y fail2ban
Fail2ban's configuration is managed through a file called jail.conf, but we should never edit it directly. Instead, we'll create a new file named jail.local to override the default settings. This makes our configuration more robust against updates.
sudo nano /etc/fail2ban/jail.local
Inside this file, we'll add the following configuration to enable and configure the sshd jail. Note that we are specifying the new SSH port we configured in Step 2.
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log # Debian/Ubuntu; /var/log/secure on RHEL
maxretry = 3
findtime = 600 # window to count failures (seconds)
bantime = 86400 # 1 day in seconds; use -1 for permanent
backend = systemd # use journald if journald is available
Add recidive jail: permanently ban repeat offenders
recidive watches fail2ban logs and escalates repeat offenders into long/permanent bans.
Enable recidive by creating /etc/fail2ban/jail.d/recidive.local
:
[recidive]
enabled = true
logpath = /var/log/fail2ban.log
action = iptables-allports[name=recidive]
# after repeated offences, ban for a long time (one year) or -1 permanent
bantime = 31536000
findtime = 86400
maxretry = 5
Notes:
iptables-allports
or iptables-multiport
will block the IP across protocols/ports. If server uses nftables or cloud firewall, adapt the action. Some setups need protocol = all in action definitions.
Banning permanently must be used carefully — monitor banned lists. Keep an allowlist for admin IPs.
Check recidive status:
sudo fail2ban-client status recidive
We did what’s effective and practical: key auth, root locked, firewall, fail2ban with an escalation path. If we want to go further, next steps are: integrate SSH certificates for fleet-wide key management, add hardware-key (FIDO2) logins, and implement two-factor auth for interactive sessions
Checkout our dedicated servers India, Instant KVM VPS, and cPanel Hosting India