TL;DR

Harden a new Linux server in this order: create a non-root user with sudo, set up SSH key authentication and disable password + root login, put a UFW firewall in front of everything, install Fail2ban to block brute-force attempts, turn on automatic security updates, and finish with a Lynis audit to score what you missed. That covers the attacks that actually hit internet-facing servers. Everything below is copy-paste, tested on Debian/Ubuntu (the commands map cleanly to most distros).

The first time I stood up a VPS on a public IP and tailed the auth log, I watched bots try to log in as root within about ninety seconds. They never stop. Automated scanners sweep the entire IPv4 space constantly, and a default server with password login is a door left open on a busy street.

The good news: the baseline that stops the overwhelming majority of those attacks is short, boring, and takes under an hour. This guide is that baseline. It is the same sequence I run whether the box is a cloud VPS, a home lab machine, or the Raspberry Pi pentest box I leave on networks. Do these steps in order, because a couple of them lock the door behind you and you want a working key in hand first.

⚠️ Keep your current session open. Several steps below change how you log in. Open a second terminal and confirm you can still get in with the new method before you close the first one. Locking yourself out of a remote server is the classic hardening mistake.

Step 0: Update everything first

Before hardening anything, patch the software you already have. Unpatched packages are the single most common way a server gets compromised, and no firewall rule saves you from a known hole in a service you're running.

Terminal — patch the system
sudo apt update && sudo apt full-upgrade -y sudo apt autoremove -y

On a RHEL/Fedora box that's sudo dnf upgrade --refresh. Reboot if the kernel was updated (sudo reboot). Patching isn't a one-time task, which is why Step 6 automates it.

Step 1: Create a non-root sudo user

Working as root full-time means one fat-fingered command or one compromised session owns the whole machine. Create a normal user, give it sudo for the occasional admin task, and stop logging in as root entirely.

Terminal — create your admin user
adduser kenny usermod -aG sudo kenny

On RHEL-family systems the admin group is wheel instead of sudo. Now open a new terminal, SSH in as that user, and run sudo whoami — if it prints root, your sudo access works and you can stop using the root account.

Step 2: Set up SSH key authentication

Passwords can be guessed, phished, and brute-forced. An SSH key can't be typed, so it can't be brute-forced. This is the single highest-impact change on the list. Generate a key on your own machine, not the server.

On YOUR laptop — generate and copy a key
ssh-keygen -t ed25519 -C "kenny@laptop" ssh-copy-id kenny@your.server.ip

ed25519 is the modern default — short, fast, and strong. Give the key a passphrase when prompted; that way a stolen laptop doesn't hand over your servers. ssh-copy-id installs the public key into the server's ~/.ssh/authorized_keys for you. Now test it:

Terminal — confirm key login works
ssh kenny@your.server.ip
🚨 Do not continue until key login works without a password. The next step disables password login entirely. If your key isn't working yet and you turn passwords off, you will be locked out of a remote server. Confirm the key first.

Step 3: Harden the SSH server

SSH is the front door and the most-attacked service on any Linux box, so it earns the most attention. Edit the config:

Terminal — edit sshd config
sudo nano /etc/ssh/sshd_config

Set these four directives (uncomment or add them):

/etc/ssh/sshd_config
PermitRootLogin no PasswordAuthentication no KbdInteractiveAuthentication no PubkeyAuthentication yes

That disables root login over SSH, turns off password auth (keys only), and closes the keyboard-interactive fallback that some setups leave open. Apply it by reloading the service:

Terminal — reload SSH
sudo systemctl reload ssh
ℹ️ What about changing the SSH port? Moving SSH off port 22 cuts down log noise from dumb bots, but it is obscurity, not security — a real scanner finds it in seconds. Do it if the quieter logs help you, but never treat a non-standard port as a substitute for keys and a firewall. If you do move it, update your firewall rule in the next step to match.

Step 4: Set up a firewall (UFW)

A firewall enforces a simple rule: only the ports you explicitly opened are reachable, everything else is dropped. Any service you forgot you were running is now invisible from the internet. UFW (Uncomplicated Firewall) is the friendly front-end to iptables/nftables on Debian and Ubuntu.

Terminal — deny by default, allow SSH + web
sudo apt install -y ufw sudo ufw default deny incoming sudo ufw default allow outgoing sudo ufw allow OpenSSH sudo ufw enable sudo ufw status verbose

Add sudo ufw allow 80/tcp and sudo ufw allow 443/tcp only if this box actually serves web traffic. The principle is deny everything, then allow the short list you need — not the other way around.

⚠️ Allow SSH before you enable UFW. The allow OpenSSH line above has to run before enable, or the firewall will drop your own connection the moment it turns on. If you changed the SSH port, use sudo ufw allow <port>/tcp instead of the OpenSSH profile.

Step 5: Block brute-force attempts with Fail2ban

Even with keys-only SSH, bots will keep hammering the port. Fail2ban watches your logs and temporarily bans any IP that fails to authenticate too many times, so the noise stops and a slow credential-stuffing attack has nowhere to go.

Terminal — install Fail2ban
sudo apt install -y fail2ban sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Always edit jail.local, never jail.conf — the .conf gets overwritten on package updates. Open jail.local and, in the [sshd] section, make sure the jail is on:

/etc/fail2ban/jail.local — [sshd]
[sshd] enabled = true maxretry = 4 bantime = 1h

Then start it and check the jail is watching:

Terminal — enable and inspect
sudo systemctl enable --now fail2ban sudo fail2ban-client status sshd

Give it a day and run that last command again — you'll see a list of banned IPs, which is a quietly satisfying look at how much unwanted attention a public server gets.

Step 6: Turn on automatic security updates

Step 0 patched the server once. This makes it stay patched without you remembering to. On Debian/Ubuntu, unattended-upgrades installs security fixes on its own.

Terminal — automate security patches
sudo apt install -y unattended-upgrades sudo dpkg-reconfigure -plow unattended-upgrades

Answer "Yes" to the prompt. By default it applies only security updates, which is exactly what you want on a server — low risk of a breaking change, no unpatched holes sitting for weeks. For anything that needs a reboot to take effect (a new kernel), schedule a maintenance window rather than letting it reboot unattended on a production box.

Step 7: Audit your work with Lynis

Now grade what you did. Lynis is an open-source auditing tool that scans a system against a long list of hardening checks and prints a score plus concrete suggestions. It is the fastest way to catch the things a checklist misses.

Terminal — run a full audit
sudo apt install -y lynis sudo lynis audit system

Lynis prints color-coded findings as it goes and ends with a hardening index (a score out of 100) and a list of suggestions with IDs. Don't chase a perfect 100 — some suggestions won't apply to your use case. Read each one, apply the ones that make sense, and re-run to watch the score climb. It's already installed on Kali, so it doubles as a recon tool when you're auditing a box from the other side.

💡 Tip: Run Lynis again after every major change to the server, and keep the report. Comparing this month's suggestions against last month's is a cheap, honest way to see whether your server is drifting out of a hardened state.

A few things beyond the baseline

The seven steps above stop the attacks that actually hit internet-facing servers. Once they're in place, the next layer depends on what the box does:

Shared responsibility with your provider. On a cloud VPS, set up the provider's network firewall (AWS security groups, DigitalOcean cloud firewalls) in addition to UFW — defence in depth, and it protects you even if the host firewall is misconfigured. Minimise what's installed. Every running service is attack surface; uninstall what you don't use and run sudo ss -tulpn to see exactly what's listening. Back up before you need to. Hardening reduces the odds of compromise; backups are what save you when something gets through anyway.

If you're coming at this from the offensive side — learning what these defences look like from an attacker's seat — the recon tools in my top 10 Kali Linux tools guide and the nmap walkthrough are the other half of the picture. Hardening a box and then scanning it yourself is the fastest way to understand why each step matters.

Frequently Asked Questions

What is the single most important step?

Switching SSH to key-only authentication and disabling password login (Steps 2 and 3). Passwords are what automated bots attack; a key removes that entire attack class. If you only had time for one change, it would be this one.

Do these commands work on CentOS/RHEL/Fedora?

The concepts are identical; a few commands differ. Use dnf instead of apt, the wheel group instead of sudo, and firewalld (or install UFW) instead of Debian's default. SSH hardening, Fail2ban, and Lynis all work the same way.

Is changing the SSH port worth it?

It reduces log noise from low-effort bots, but it's obscurity, not security — a real scanner finds the new port immediately. Do it if quieter logs help you, but never in place of keys, a firewall, and Fail2ban.

Will automatic updates break my server?

Unattended-upgrades applies only security updates by default, which rarely change behaviour. The one thing to control manually is reboots — a kernel update needs a reboot to take effect, so schedule that yourself on a production box rather than letting it happen unattended.

How often should I re-audit with Lynis?

After every significant change, and on a routine schedule (monthly is reasonable). Keep the reports so you can compare them — a rising hardening index means you're improving, and new suggestions flag drift or newly installed services that widened your attack surface.