Skip to content
Back to Blog
Security10 min read

Server Security Hardening 2026: 8 Steps to Lock Down SSH

A step-by-step SSH hardening checklist that closes common attack vectors on internet-facing Linux servers, from key-based auth to fail2ban.

Written by Abdul AbrorTechnical Hosting Support Engineer
Server Security Hardening 2026: 8 Steps to Lock Down SSH
On this page

SSH is the front door to your Linux server. Leave it misconfigured and you'll see thousands of login attempts every day, most of them automated scans hunting for weak passwords or default credentials. I've watched auth.log files scroll with failed root login attempts from IP ranges across five continents—all hitting the same server within an hour.

Hardening SSH doesn't require exotic tools or deep kernel knowledge. It's a checklist of changes to /etc/ssh/sshd_config and a handful of supporting tools that block the noise and shrink the attack surface. Walk through these eight steps on any internet-facing server and you'll cut out the easy wins attackers count on.

1. Disable Root Login Over SSH

The root account is the first target in every brute-force campaign because the username is always the same. Attackers only need to guess the password. Blocking direct root login forces them to guess both a valid username and a password, then escalate privileges afterward—two problems instead of one.

Open your SSH daemon config:

sudo nano /etc/ssh/sshd_config

Find the line PermitRootLogin and set it to no:

PermitRootLogin no

Restart SSH to apply:

sudo systemctl restart sshd

From this point forward, you'll log in with a regular user account and use sudo or su to run privileged commands. Make sure you have a non-root user with sudo rights before you restart SSH, or you'll lock yourself out.

2. Enforce Public Key Authentication

Password authentication is the weakest link. Even a strong password can fall to a brute-force attack given enough time and enough botnets. Public key authentication replaces the password with a cryptographic keypair: the server holds the public key, you keep the private key. An attacker without your private key can't log in, even if they know your username.

Generate a keypair on your local machine if you don't have one already:

ssh-keygen -t ed25519 -C "[email protected]"

Copy the public key to your server:

ssh-copy-id user@your-server-ip

Then disable password authentication entirely. Back in /etc/ssh/sshd_config, set:

PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no

Restart SSH again:

sudo systemctl restart sshd

Test the key-based login from a new terminal before you close your existing session. If something broke, you still have the old session to fix it.

3. Change the Default SSH Port

Port 22 is hardcoded into every automated scanner on the internet. Moving SSH to a non-standard port won't stop a determined attacker, but it eliminates 99% of the background noise—the bots that blindly probe port 22 and move on if it's closed.

Pick a port above 1024 and below 65535. Avoid common alternatives like 2222. I've used ports in the 49152–65535 range (the ephemeral range) without issue. Edit sshd_config:

Port 34567

If you're running a firewall (and you should be), allow the new port:

sudo ufw allow 34567/tcp

Restart SSH and reconnect on the new port:

ssh -p 34567 user@your-server-ip

Once you've confirmed it works, remove the old port 22 rule from your firewall.

4. Restrict SSH Access by IP or Subnet

If you connect from a fixed IP or a known range—an office network, a VPN, or a home connection that rarely changes—lock SSH down to those addresses. It's the single most effective filter you can apply.

Using UFW:

sudo ufw allow from 203.0.113.0/24 to any port 34567 proto tcp

Using iptables directly:

sudo iptables -A INPUT -p tcp -s 203.0.113.0/24 --dport 34567 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 34567 -j DROP

If your IP isn't static, consider a VPN or a dynamic DNS setup paired with firewall automation. The trade-off is convenience versus exposure; decide based on how sensitive the server is.

5. Limit User Access and Disable Unused Accounts

Not every user on the system needs SSH access. Service accounts, application users, and legacy accounts left over from old deployments are all potential footholds.

Review the list of users with login shells:

grep -vE '(nologin|false)' /etc/passwd

For any user who shouldn't log in, set their shell to /usr/sbin/nologin:

sudo usermod -s /usr/sbin/nologin serviceuser

You can also restrict SSH access to specific users or groups in sshd_config:

AllowUsers alice bob

Or limit to members of a group:

AllowGroups sshusers

Lock or delete accounts that aren't needed:

sudo passwd -l olduser
sudo userdel -r olduser

6. Install and Configure Fail2Ban

Fail2Ban watches your authentication logs and bans IP addresses that rack up too many failed login attempts. It's reactive rather than preventive, but it cuts down repeat offenders and slows down distributed attacks.

Install it:

sudo apt install fail2ban -y   # Debian/Ubuntu
sudo yum install fail2ban -y   # CentOS/RHEL

Create a local config file to avoid your changes being overwritten on updates:

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local

Find the [sshd] section and adjust the settings:

[sshd]
enabled = true
port = 34567
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600

This configuration bans an IP for one hour after three failed attempts within ten minutes. Restart Fail2Ban:

sudo systemctl restart fail2ban
sudo systemctl enable fail2ban

Check banned IPs:

sudo fail2ban-client status sshd

7. Enable Two-Factor Authentication for SSH

Key-based authentication is strong, but it assumes your private key stays private. If your laptop is stolen or your key is compromised, an attacker can log in as you. Two-factor authentication adds a time-based one-time password (TOTP) as a second layer.

Install Google Authenticator's PAM module:

sudo apt install libpam-google-authenticator -y

Run the setup as the user who will log in:

google-authenticator

Answer the prompts (time-based tokens, update .google_authenticator file, disallow reuse, allow three concurrent codes, enable rate limiting).

Edit /etc/pam.d/sshd and add this line at the top:

auth required pam_google_authenticator.so

In /etc/ssh/sshd_config, enable challenge-response and set the auth methods:

ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive

Restart SSH. On your next login, you'll need both your private key and the six-digit TOTP code from your authenticator app.

8. Monitor SSH Logs and Set Up Alerts

Hardening SSH is not a one-time task. You need visibility into who's connecting, who's failing, and when something unusual happens. The auth log holds every login attempt—successful or not.

Tail the log in real time:

sudo tail -f /var/log/auth.log

Grep for failed attempts:

sudo grep 'Failed password' /var/log/auth.log | tail -20

For automated alerting, tools like Logwatch or OSSEC can email you daily summaries or trigger alerts on suspicious patterns. A simple cron job paired with a shell script can also send you a notification if the failed-login count crosses a threshold.

Sample script to count recent failures:

#!/bin/bash
FAILS=$(grep 'Failed password' /var/log/auth.log | wc -l)
if [ $FAILS -gt 50 ]; then
  echo "Warning: $FAILS failed SSH attempts detected" | mail -s "SSH Alert" [email protected]
fi

Schedule it hourly in cron:

0 * * * * /usr/local/bin/ssh-alert.sh

What Happens After the Hardening?

Once these eight steps are in place, the stream of brute-force attempts drops to near zero. The few that remain hit Fail2Ban and get blocked after a couple of tries. Your auth logs stay quiet.

But SSH hardening isn't the finish line. Keep your system patched, review user accounts periodically, rotate keys if someone leaves your team, and watch for new SSH vulnerabilities. The OpenSSH project has a strong security track record, but configuration mistakes and unpatched bugs still create risk.

Run through this checklist on every new server before it goes live. Automate the steps with Ansible or shell scripts if you're managing a fleet. The goal is to make SSH a boring, reliable service that doesn't show up in your incident reports.

How do I recover if I lock myself out after changing SSH settings?

If you have console access through your hosting provider's control panel (KVM, VNC, or serial console), log in there and revert the changes in /etc/ssh/sshd_config. Always test config changes in a separate terminal session before closing your working connection.

Can I use password authentication for one specific user while keeping keys mandatory for everyone else?

Yes. Use a Match User block at the end of sshd_config:

Match User backupuser
  PasswordAuthentication yes

This overrides the global setting for that user. It's a trade-off; weigh the convenience against the added risk.

Does moving SSH to a non-standard port actually improve security?

It reduces noise and eliminates casual scans, but an attacker who runs a full port scan will find it. Treat port changes as an annoyance filter, not a security control. The real defenses are key-based auth, IP restrictions, and monitoring.

Should I disable SSH entirely and use a bastion host instead?

If you're managing multiple servers, a bastion (or jump host) centralizes SSH access and lets you lock down individual servers to internal-only connections. It's a strong architectural pattern for production environments but adds complexity. For a single VPS or a small cluster, properly hardened direct SSH is sufficient.

Start with the Basics and Layer Up

Disable root login and enforce key authentication first—those two steps close the biggest holes. Then add Fail2Ban, move the port, and restrict by IP if your network allows it. Two-factor auth and log monitoring finish the stack.

Each layer compounds the difficulty for an attacker. The goal isn't perfect security; it's making your server a harder target than the thousand others that haven't changed the default config. That's usually enough.