--- title: "Linux Server Hardening Checklist" domain: selfhosting category: security tags: [security, hardening, linux, ssh, firewall, server] status: published created: 2026-03-08 updated: 2026-03-08 --- # Linux Server Hardening Checklist When I set up a fresh Linux server, there's a standard set of things I do before I put anything on it. None of this is exotic — it's the basics that prevent the most common attacks. Do these before the server touches the public internet. ## The Short Answer New server checklist: create a non-root user, disable root SSH login, use key-based auth only, configure a firewall, keep packages updated. That covers 90% of what matters. ## 1. Create a Non-Root User Don't work as root. Create a user, give it sudo: ```bash # Create user adduser yourname # Add to sudo group (Debian/Ubuntu) usermod -aG sudo yourname # Add to wheel group (Fedora/RHEL) usermod -aG wheel yourname ``` Log out and log back in as that user before doing anything else. ## 2. SSH Key Authentication Passwords over SSH are a liability. Set up key-based auth and disable password login. On your local machine, generate a key if you don't have one: ```bash ssh-keygen -t ed25519 -C "yourname@hostname" ``` Copy the public key to the server: ```bash ssh-copy-id yourname@server-ip ``` Or manually append your public key to `~/.ssh/authorized_keys` on the server. Test that key auth works **before** disabling passwords. ## 3. Harden sshd_config Edit `/etc/ssh/sshd_config`: ``` # Disable root login PermitRootLogin no # Disable password authentication PasswordAuthentication no # Disable empty passwords PermitEmptyPasswords no # Limit to specific users (optional but good) AllowUsers yourname # Change the port (optional — reduces log noise, not real security) Port 2222 ``` Restart SSH after changes: ```bash sudo systemctl restart sshd ``` Keep your current session open when testing — if you lock yourself out you'll need console access to fix it. ## 4. Configure a Firewall **ufw (Ubuntu/Debian):** ```bash sudo apt install ufw # Default: deny incoming, allow outgoing sudo ufw default deny incoming sudo ufw default allow outgoing # Allow SSH (use your actual port if you changed it) sudo ufw allow 22/tcp # Allow whatever services you're running sudo ufw allow 80/tcp sudo ufw allow 443/tcp # Enable sudo ufw enable # Check status sudo ufw status verbose ``` **firewalld (Fedora/RHEL):** ```bash sudo systemctl enable --now firewalld # Allow SSH sudo firewall-cmd --permanent --add-service=ssh # Allow HTTP/HTTPS sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --permanent --add-service=https # Apply changes sudo firewall-cmd --reload # Check sudo firewall-cmd --list-all ``` ## 5. Keep Packages Updated Security patches come through package updates. Automate this or do it regularly: ```bash # Ubuntu/Debian — manual sudo apt update && sudo apt upgrade # Enable unattended security upgrades (Ubuntu) sudo apt install unattended-upgrades sudo dpkg-reconfigure --priority=low unattended-upgrades # Fedora/RHEL — manual sudo dnf upgrade # Enable automatic updates (Fedora) sudo dnf install dnf-automatic sudo systemctl enable --now dnf-automatic.timer ``` ## 6. Fail2ban Fail2ban watches log files and bans IPs that fail authentication too many times. Helps with brute force noise. ```bash # Ubuntu/Debian sudo apt install fail2ban # Fedora/RHEL sudo dnf install fail2ban # Start and enable sudo systemctl enable --now fail2ban ``` Create `/etc/fail2ban/jail.local` to override defaults: ```ini [DEFAULT] bantime = 1h findtime = 10m maxretry = 5 [sshd] enabled = true ``` ```bash sudo systemctl restart fail2ban # Check status sudo fail2ban-client status sshd ``` ## 7. Disable Unnecessary Services Less running means less attack surface: ```bash # See what's running systemctl list-units --type=service --state=active # Disable something you don't need sudo systemctl disable --now servicename ``` Common ones to disable on a dedicated server: `avahi-daemon`, `cups`, `bluetooth`. ## 8. Mail Server: SpamAssassin If you're running Postfix (like on majormail), SpamAssassin filters incoming spam before it hits your mailbox. **Install (Fedora/RHEL):** ```bash sudo dnf install spamassassin sudo systemctl enable --now spamassassin ``` **Integrate with Postfix** by adding a content filter in `/etc/postfix/master.cf`. See the [full setup guide](https://www.davekb.com/browse_computer_tips:spamassassin_with_postfix:txt) for Postfix integration on RedHat-based systems. **Train the filter with sa-learn:** SpamAssassin gets better when you feed it examples of spam and ham (legitimate mail): ```bash # Train on known spam sa-learn --spam /path/to/spam-folder/ # Train on known good mail sa-learn --ham /path/to/ham-folder/ # Check what sa-learn knows sa-learn --dump magic ``` Run `sa-learn` periodically against your Maildir to keep the Bayesian filter accurate. The more examples it sees, the fewer false positives and missed spam you'll get. Reference: [sa-learn documentation](https://spamassassin.apache.org/full/3.0.x/dist/doc/sa-learn.html) ## Gotchas & Notes - **Don't lock yourself out.** Test SSH key auth in a second terminal before disabling passwords. Keep the original session open. - **If you changed the SSH port**, make sure the firewall allows the new port before restarting sshd. Block the old port after you've confirmed the new one works. - **fail2ban and Docker don't always play nicely.** Docker bypasses iptables rules in some configurations. If you're running services in Docker, test that fail2ban is actually seeing traffic. - **SELinux on RHEL/Fedora** may block things your firewall allows. Check `ausearch -m avc` if a service stops working after hardening. - **This is a baseline, not a complete security posture.** For anything holding sensitive data, also look at: disk encryption, intrusion detection (AIDE, Tripwire), log shipping to a separate system, and regular audits. ## See Also - [managing-linux-services-systemd-ansible](../../01-linux/process-management/managing-linux-services-systemd-ansible.md) - [debugging-broken-docker-containers](../docker/debugging-broken-docker-containers.md)