A practical Linux security hardening checklist
The specific, prioritized changes that matter most on a real internet-facing server. · 12 min
Hardening is never a single switch — it's a series of specific, mostly independent controls, each closing off one category of risk. In rough priority order for a freshly provisioned internet-facing server: (1) SSH hardening from Level 9 — key-based auth only, root login disabled, ideally combined with `fail2ban` or a cloud-level rate limit against brute-force attempts. (2) Least-privilege sudo from Level 4 — named individual accounts with scoped sudo access, not shared root logins. (3) A default-deny firewall from Level 10 — only explicitly needed ports open, everything else blocked by default rather than trying to enumerate everything to block.
(4) Keep SELinux or AppArmor enforcing (Level 11) rather than disabled "to make things easier" — diagnose specific denials instead. (5) Regular, ideally automated security updates — a server that's never patched accumulates known, publicly documented vulnerabilities over time; most distributions offer unattended-upgrade mechanisms for at least security patches specifically, reducing the window between a vulnerability disclosure and your exposure to it. (6) Correct file permissions on anything sensitive (Level 4's SUID/SGID/permission model) — private keys, credential files, and configuration containing secrets should never be group- or world-readable.
(7) Centralized, retained logging (Level 12) — logs that only exist on the compromised server itself are logs an attacker can delete to cover their tracks; shipping logs to a separate system, even a simple one, preserves an audit trail independent of the host being investigated. (8) Regular, tested backups, stored separately from the server itself — a server that's compromised or fails hardware-wise is a very different problem if a recent, verified-restorable backup exists versus if it doesn't. None of these are exotic — the discipline is applying all of them consistently, not discovering one gap during an actual incident.
- • SSH: key-based auth only, root login disabled
- • Sudo: named accounts with scoped access, not shared root
- • Firewall: default-deny, only explicitly needed ports open
- • SELinux/AppArmor: kept enforcing, denials diagnosed rather than disabled
- • Updates: automated security patching where possible
- • Permissions: secrets and keys never group/world-readable
- • Logging: shipped off the host, not solely stored locally
- • Backups: regular, tested, and stored separately from the server
Common Mistakes
- ⚠ Treating hardening as a one-time setup step instead of an ongoing discipline (unpatched software accumulates risk continuously, not just at initial provisioning)
- ⚠ Disabling a security control (SELinux, a firewall rule) to unblock a deploy under time pressure, and never re-enabling it afterward
- ⚠ Backing up to the same server or same disk being protected — a real disaster (disk failure, ransomware, compromise) takes the backup down with the original
Takeaway: Hardening is a checklist of independent, mostly cumulative controls, not a single setting — and the actual risk in practice is usually a control that was correctly configured once and then quietly disabled or left unpatched, not one that was never considered at all.