- Fixed 4 broken markdown links (bad relative paths in See Also sections) - Corrected n8n port binding to 127.0.0.1:5678 (matches actual deployment) - Updated SnapRAID article with actual majorhome paths (/majorRAID, disk1-3) - Converted 67 Obsidian wikilinks to relative markdown links or plain text - Added YAML frontmatter to 35 articles missing it entirely - Completed frontmatter on 8 articles with missing fields Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
241 lines
6.1 KiB
Markdown
241 lines
6.1 KiB
Markdown
---
|
|
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)
|