How to Harden SSH on Ubuntu: 7-Step Checklist

by Liam Foster
How to Harden SSH on Ubuntu: 7-Step Checklist

SSH is the front door to your infrastructure. A weak configuration turns it into a revolving door.

This checklist hardens SSH on Ubuntu by eliminating the most common entry points: password authentication, root login, and default ports. Each step removes an attack vector; together they create a defensible baseline.

The Model: SSH Attack Surface

SSH hardening works on three layers:

  1. Authentication — who can log in (keys vs. passwords)
  2. Access control — which users and from where
  3. Visibility — what gets logged and monitored

Breaches typically exploit weak authentication first. A hardened SSH config makes brute-force attacks prohibitively slow and forces attackers to compromise your key material instead — a much harder target.

Step 1: Backup and Verify Current Config

Before touching /etc/ssh/sshd_config, back it up:

cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak

Verify SSH is running:

sudo systemctl status ssh

On Ubuntu, the SSH daemon is ssh (not sshd). Keep a separate terminal with an active SSH session open during edits — if you lock yourself out, you can revert.

Step 2: Disable Password Authentication

Password auth is the lowest-hanging fruit for attackers. Replace it with key-pair authentication.

Generate a key pair on your local machine (not the server):

ssh-keygen -t ed25519 -C "your-email@example.com"

Choose a strong passphrase. Ed25519 is faster and more secure than RSA for new keys.

Copy the public key to the server:

ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server-ip

Verify it works before disabling passwords:

ssh -i ~/.ssh/id_ed25519 user@server-ip

Now edit /etc/ssh/sshd_config:

sudo nano /etc/ssh/sshd_config

Find and set:

PasswordAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no

Reload SSH without restarting (safer):

sudo sshd -t && sudo systemctl reload ssh

The -t flag tests the config for syntax errors. Always run it before reloading.

Step 3: Disable Root Login

Root access over SSH is a privilege escalation shortcut. Disable it:

PermitRootLogin no

Force users to log in as a standard user, then escalate with sudo if needed. This leaves an audit trail.

Alternatively, if you must allow root login (rare), use:

PermitRootLogin prohibit-password

This allows key-based root login but blocks password-based root login. Still not recommended for production.

Step 4: Change the Default Port

SSH runs on port 22 by default. Attackers scan it first. Moving to a non-standard port (e.g., 2222) doesn't add cryptographic security, but it cuts noise from automated scans by 90%+.

Port 2222

Choose a port above 1024 and below 65535. Avoid commonly-used ports like 80, 443, or 3306.

Gotcha: If you use a firewall (UFW, iptables), update the rule:

sudo ufw allow 2222/tcp
sudo ufw delete allow 22/tcp

Test the new port in a second terminal before closing the first:

ssh -p 2222 -i ~/.ssh/id_ed25519 user@server-ip

Only after confirming it works, reload SSH:

sudo sshd -t && sudo systemctl reload ssh

Step 5: Restrict User Access

Limit which users can SSH in. Create a whitelist:

AllowUsers user1 user2

Or restrict by group:

AllowGroups ssh-users
sudo usermod -aG ssh-users user1

This prevents compromised service accounts (e.g., www-data, postgres) from becoming SSH entry points.

Step 6: Enforce Key-Only Access and Timeouts

Layered restrictions slow attackers:

MaxAuthTries 3
MaxSessions 5
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
  • MaxAuthTries 3: Fail after 3 bad authentication attempts (default is 6).
  • MaxSessions 5: Allow max 5 concurrent sessions per user.
  • LoginGraceTime 30: Close unauthenticated connections after 30 seconds.
  • ClientAliveInterval 300: Send keep-alive every 5 minutes.
  • ClientAliveCountMax 2: Drop idle connections after 2 missed keep-alives (10 minutes total).

These don't stop a sophisticated attacker, but they make brute-force scanning painfully slow.

Step 7: Verify, Test, and Monitor

After all edits, run the syntax check again:

sudo sshd -t

If it passes, reload:

sudo systemctl reload ssh

Test from a new terminal with the correct port and key:

ssh -p 2222 -i ~/.ssh/id_ed25519 user@server-ip

Monitor SSH logs for failed attempts:

sudo tail -f /var/log/auth.log | grep sshd

Enable verbose logging in sshd_config if you're troubleshooting:

SyslogFacility AUTH
LogLevel VERBOSE

(Set back to INFO after debugging.)

When This Breaks

Locked out after config changes: Boot into recovery mode or use a console/serial connection to revert /etc/ssh/sshd_config.bak. This is why you backed it up.

Key-based auth fails: Verify the public key is in ~/.ssh/authorized_keys on the server and has 600 permissions:

chmod 600 ~/.ssh/authorized_keys
chmod 700 ~/.ssh

SSH_AUTH_SOCK errors with sudo: Some systems require ssh-agent to be running. Use ssh-add to load your key into the agent before connecting.

Port 2222 unreachable: Check firewall rules, cloud security groups, and confirm the port is actually listening:

sudo ss -tlnp | grep ssh

Trade-Offs

Moving SSH off port 22 trades obscurity for reduced scan noise — not security. An attacker who knows your IP can still find it.

Disabling password auth requires distributing and rotating keys. If you lose all copies of your private key, you lose access. Use a key manager (1Password, Bitwarden) or hardware security key (YubiKey) to mitigate this. It's also worth having a reliable backup strategy for your server; cara backup WordPress secara otomatis is one example of how automated backup workflows are structured, and the same discipline applies to SSH key material and config files.

Strict timeouts can disconnect long-running processes. Adjust ClientAliveInterval and ClientAliveCountMax if you run interactive jobs that go silent for hours.

One-Line Takeaway

Harden SSH by disabling passwords, blocking root login, enforcing key pairs, and restricting user access — each step removes an attack vector, and together they raise the cost of compromise to unacceptable levels.