This isn't a complete security guide - it's the small set of changes worth making on essentially every internet-facing machine, gathered in one place with links to the fuller explanation of each.

1. Disable password SSH login, use keys instead

Password authentication is exposed to brute-force attempts from anywhere on the internet the moment SSH is reachable. Set up key-based authentication first, confirm it works, then disable passwords:

PasswordAuthentication no
PermitRootLogin no

in /etc/ssh/sshd_config, followed by sudo systemctl reload sshd. See SSH for the rest of the server config.

2. Keep packages up to date

Most real-world compromises exploit a known, already-patched vulnerability - not a novel attack. Enable automatic security updates rather than relying on remembering to run updates manually:

sudo apt install unattended-upgrades   # Debian/Ubuntu
sudo dnf install dnf-automatic          # Fedora/RHEL

See APT and dpkg and DNF and RPM for the manual update commands these automate.

3. Run a firewall, default-deny

Only open ports something is actually meant to be listening on:

sudo ufw default deny incoming
sudo ufw allow ssh
sudo ufw allow 443/tcp
sudo ufw enable

See Firewalls: iptables and nftables for the underlying mechanism and the firewalld equivalent.

4. Rate-limit or ban repeated failed logins

fail2ban watches log files for repeated authentication failures (SSH, web app logins, anything with a parseable log format) and temporarily firewalls the offending address:

sudo apt install fail2ban
sudo systemctl enable --now fail2ban

Meaningfully reduces the noise and risk from automated brute-force scanning, on top of - not instead of - disabling password auth entirely.

5. Don't run services as root that don't need to be

Most well-packaged services (nginx, PostgreSQL, etc.) already drop to a dedicated unprivileged user after binding to any low-numbered ports they need - verify this rather than assuming, especially for anything you've built or configured yourself:

ps -eo user,comm | grep nginx
root     nginx
www-data nginx

The root line here is normal - it's the master process, which needs root briefly to bind port 443; the actual worker processes handling traffic run as www-data. A service where every process runs as root is worth double-checking.

6. Disable services you're not using

Every running service is attack surface. List what's enabled and turn off anything unnecessary:

systemctl list-unit-files --state=enabled
sudo systemctl disable --now avahi-daemon

See systemd and Services.

7. Check for setuid binaries you didn't expect

Setuid binaries run with their owner's privileges regardless of who executes them - a small, security-relevant set on any system, worth knowing what's actually there:

find / -xdev -perm -4000 -type f 2>/dev/null

See File Permissions and Ownership for what the setuid bit does.

8. Know that SELinux / AppArmor exist

Beyond standard user/group permissions, RHEL/Fedora ship SELinux and Debian/Ubuntu ship AppArmor - mandatory access control systems that confine what a process can do even if it's compromised or misconfigured, beyond what its Unix permissions alone would prevent. Both are enabled by default on their respective distros; disabling either to "fix" a permission problem (a common but poor troubleshooting shortcut) removes a real layer of defense - check sudo sealert or journalctl for denials and fix the underlying policy instead, where practical.

What this list deliberately leaves out

Full security hardening also covers things like kernel parameter tuning, intrusion detection, TLS configuration, and application-level security - this page is the baseline that applies almost universally, not a complete guide for any specific threat model.