• Indonesian
  • English
  • SSH Server Hardening: Step-by-Step Guide Linux 2026

    Kecepatan:
    ⏱ 10 min read

    Skip the small talk. If your SSH server still lets root log in with a password on port 22, a bot will find you — it’s just a question of when, not if. Here’s the exact routine I run on every Linux box before it ever touches production: full ssh server hardening, from key-based auth to fail2ban. No fluff, just commands you can copy and verify.

    Difficulty: Intermediate
    Last Updated: August 2026
    Tested On: Ubuntu 24.04 LTS, Debian 12, RHEL 9 / CentOS Stream 9

    I’ve cleaned up more compromised boxes than I care to count, and the root cause is almost always the same. It’s never the clever zero-day or the sophisticated malware. It’s SSH left wide open — default port, root login enabled, password auth on, no rate limiting. The bots find it within hours, and once a credential slips, it’s game over for everything behind that door.

    The damage isn’t limited to the box itself. An attacker with SSH access can move laterally: read your database dumps, steal API keys, tamper with configs, plant backdoors, mine crypto, or use your server as a relay for bigger attacks. And on a production environment, a compromise like that means downtime, data leaks, and clients asking very uncomfortable questions. I’ve seen it turn into legal headaches more than once.

    So how do you know if you’re already being scanned? Check your auth log. On most distros that’s /var/log/auth.log (Debian/Ubuntu) or /var/log/secure (RHEL). You’ll usually see a tell-tale pattern: a wall of “Failed password for invalid user” lines from a stream of different IPs. That’s bots working through their dictionary lists. If you see that, they’ve already found you — you just aren’t locked yet.

    # Debian/Ubuntu
    sudo tail -n 200 /var/log/auth.log
    
    # RHEL/CentOS
    sudo tail -n 200 /var/log/secure
    
    # Or via journald
    sudo journalctl -u sshd --since "1 hour ago" | grep -i failed | head -50

    The root causes are remarkably consistent, which is honestly kind of sad. Default port 22 still open everywhere, PermitRootLogin still set to yes, password authentication still enabled when key-based auth would do, no firewall rules limiting who can reach the port, no fail2ban to eject repeat offenders, and a pile of stale users with old keys that were never cleaned up. Fix those six things and you’ve closed the door on 99% of the attacks I see daily. Here’s exactly how.

    One rule before we start: make sure you have out-of-band access to the server — a KVM console from your provider, a hypervisor console if it’s a VM, anything. Because some of these steps can lock you out if done wrong. Never, ever run this sequence with SSH as your only way in.

    Why SSH Server Hardening Matters Before You Go Live

    SSH is the front door to your Linux server. Terminal access, file uploads, config management, deploys — it all flows through that single service. If ssh server hardening is skipped, you’re betting your whole stack on a login prompt being strong enough. And default settings aren’t strong enough; they’re designed to be convenient, not safe. This isn’t paranoia — it’s about making sure the door requires a real key and turns away people who don’t have one.

    Think of it like a house: you don’t need to turn it into a bank vault, but you do need decent locks, and you don’t leave every window wide open and announce it on a public billboard. Port 22 with password auth is exactly that billboard.

    ssh server hardening linux step by step

    Step 1: Back Up Your SSH Configuration

    A bad sshd_config is the fastest way to lose access to your server. Always back up before changing anything.

    This is boring and I still do it every single time, because one bad edit on a remote box without console access is a very bad day.

    sudo cp -a /etc/ssh /etc/ssh.bak.$(date +%F)
    ls -la /etc/ssh.bak.*

    Also copy the backup off the box — scp it to your workstation. A backup stored on the compromised box is not much of a backup.

    Step 2: Set Up ed25519 Key-Based Authentication

    Passwords are guessable, brute-forceable, phishable. Keys aren’t — as long as the private key stays with you and has a strong passphrase. I use ed25519 on every new setup: faster, shorter, and cryptographically stronger than RSA for the same level of security. Generate the keypair on your own machine, not on the server.

    ssh-keygen -t ed25519 -a 100 -C "admin@my-workstation"

    Then push the public key to the server. ssh-copy-id works while passwords are still enabled:

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

    No ssh-copy-id on your platform? Append the public key manually to ~/.ssh/authorized_keys on the server. Either way, test a fresh login with the key before moving on. This order matters — never disable password auth before you’ve confirmed the key actually works.

    Step 3: Disable Root Login and Password Authentication

    Edit the server’s sshd_config:

    sudo nano /etc/ssh/sshd_config

    Set these directives:

    PermitRootLogin no
    PasswordAuthentication no
    PubkeyAuthentication yes
    ChallengeResponseAuthentication no
    KbdInteractiveAuthentication no
    UsePAM yes

    Validate the config first, then reload. This two-step habit saves you from locking yourself out:

    sudo sshd -t
    sudo systemctl reload ssh

    On RHEL-family systems the service is sshd: sudo systemctl reload sshd. After the reload, open a second terminal and log in with your key. Only close your current session once the new one works. I cannot stress this enough — I’ve watched people lose the only session they had open mid-change.

    Step 4: Move Off Port 22 and Lock It Down with a Firewall

    Changing the port (say, to 22022) is not security by design — a determined attacker will scan and find it. What it does is kill the noise: nearly all automated bot traffic only ever targets port 22, so the signal-to-noise ratio of your logs improves dramatically.

    Set the new port in sshd_config:

    Port 22022

    Reload, then fix the firewall. With UFW (Debian/Ubuntu):

    sudo ufw allow 22022/tcp
    sudo ufw delete allow 22/tcp
    sudo ufw enable
    sudo ufw status

    On RHEL/CentOS with firewalld:

    sudo firewall-cmd --permanent --add-port=22022/tcp
    sudo firewall-cmd --permanent --remove-port=22/tcp
    sudo firewall-cmd --reload
    sudo firewall-cmd --list-ports

    And don’t forget your client: ssh -p 22022 admin@server-ip, plus any port forwards or load balancer rules still pointing at the old port.

    Step 5: Restrict Which Users Can SSH In

    Don’t let every user on the box log in over SSH. Restrict it to a dedicated group. Create the group and add your admins:

    sudo groupadd sshusers
    sudo usermod -aG sshusers admin
    sudo usermod -aG sshusers deploy

    Then, in sshd_config:

    AllowGroups sshusers
    DenyUsers root

    Anyone outside the group can’t authenticate over SSH, even with a valid password or key. It’s a cheap, effective second layer. If you’re managing several servers, this pairs well with a user management best practice workflow so stale accounts never pile up.

    Step 6: Add fail2ban for Automatic Banning

    fail2ban is the bouncer: it watches your auth logs, and when an IP fails too many times, it drops them at the firewall level automatically. I wrote a full fail2ban complete guide, but the essentials for SSH look like this:

    sudo apt install fail2ban
    sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

    In /etc/fail2ban/jail.local, make sure the sshd jail is enabled and points at your new port:

    [sshd]
    enabled = true
    port = 22022
    maxretry = 5
    bantime = 3600

    Start and check it:

    sudo systemctl enable --now fail2ban
    sudo fail2ban-client status sshd

    You’ll watch the banned counter climb, and your auth logs turn quiet again. Pair it with a properly configured UFW firewall guide and port scanning becomes a waste of an attacker’s time.

    Step 7: Harden Session Handling and Login Limits

    Last stretch. These directives stop idle sessions from hanging around forever and cap how much guesswork a bot can do per connection:

    MaxAuthTries 3
    MaxStartups 10:30:60
    LoginGraceTime 60
    ClientAliveInterval 300
    ClientAliveCountMax 0
    X11Forwarding no
    AllowTcpForwarding no

    Quick rundown: MaxAuthTries 3 limits password or key attempts per connection; MaxStartups caps unauthenticated concurrent connections so attackers can’t tie up resources; LoginGraceTime 60 gives sixty seconds to complete login; ClientAliveInterval 300 plus ClientAliveCountMax 0 kills idle sessions after five minutes; X11Forwarding and AllowTcpForwarding off shut down rarely-used features that only add attack surface.

    Troubleshooting: Common SSH Hardening Errors

    Things will go wrong at least once — that’s normal. Here’s the table I keep pinned to my desk.

    Error Likely cause Fix
    Permission denied (publickey) Key not registered, or password auth disabled before the key was verified Add the key first, confirm login, then disable password auth
    Too many authentication failures MaxAuthTries too low, or the client is offering too many keys Trim keys in your agent or raise MaxAuthTries
    Connection refused Firewall blocking the new port, or sshd not reloaded Check ufw status / firewall-cmd, verify sshd listens on the new port
    Server unexpectedly closed connection Client still hitting the old port Use ssh -p 22022 and update any port forwards
    Broken pipe after reload Reload killed your in-flight connection Reconnect; if it fails, restore the backup and use sshd -t to validate

    If you hit something outside that table, work the problem backwards: restore your last known-good config, run sudo sshd -t, and compare. When in doubt, roll back. And if you’re prepping a new box for a move, my KVM VPS migration guide covers locking down SSH as part of a full handover.

    FAQ: SSH Hardening on Linux

    Q: Will I lock myself out if I follow these steps?

    Only if you skip the order. The safe sequence is: generate the key, push it, verify a fresh login, then disable password auth, and always run sshd -t before reloading. Keep your current session open until a new one works, and have console access ready as a safety net. Follow that and lockout risk is close to zero.

    Q: Should I change the SSH port from 22?

    Do it, but understand it’s noise reduction, not a real security boundary. Automated bots only probe port 22, so moving the port clears out most of the background scanning. Combine it with key auth, a restrictive firewall, and fail2ban. Changing the port alone buys you almost nothing.

    Q: Is 2FA worth adding on top of SSH keys?

    For human access, yes. TOTP (Google Authenticator-style) or U2F adds a second factor so a stolen key alone isn’t enough. For service accounts and automation, skip 2FA and rely on tightly scoped keys instead. Enabling 2FA for humans while keeping automation on plain keys is a common middle ground.

    Q: Why ed25519 instead of RSA?

    ed25519 is faster, produces smaller keys, and its design is considered more robust than RSA for equivalent security. RSA 2048 still works, but there’s no real reason to choose it when starting fresh. One caveat: a few legacy SSH clients don’t support ed25519 yet, so check client versions if you support old boxes.

    While you’re at it, pair these changes with server log monitoring with Netdata and Grafana so you see auth spikes the moment they happen instead of finding out weeks later.

    Before you close this tab, run the checklist: keys in place and verified, root login off, password auth off, port changed with firewall updated, users restricted, fail2ban active, session limits set. Tick each one and you’re done — that’s the whole game. Go lock your doors.

    Author: Syslog Solutions — NOC & Server Management Team. We handle 500+ servers daily, from shared hosting to enterprise dedicated infrastructure.