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:
- Authentication — who can log in (keys vs. passwords)
- Access control — which users and from where
- 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.