Server Hardening Checklist¶
A practical lockdown sequence for a freshly provisioned Linux server. Work through it top to bottom on a new box before it carries any real traffic.
Don't lock yourself out
When changing SSH and firewall settings, keep your current session open and test the new config in a second terminal before closing the first. Confirm key-based login works before you disable passwords.
Tested on
AlmaLinux 9 / RHEL 9 (firewalld, dnf). Debian/Ubuntu equivalents
(ufw, apt) are noted inline.
1. Patch the system first¶
# RHEL/AlmaLinux
sudo dnf upgrade --refresh -y
# Debian/Ubuntu
sudo apt update && sudo apt full-upgrade -y
Enable automatic security updates so the box doesn't drift:
# RHEL/AlmaLinux
sudo dnf install -y dnf-automatic
sudo sed -i 's/^apply_updates = no/apply_updates = yes/' /etc/dnf/automatic.conf
sudo systemctl enable --now dnf-automatic.timer
# Debian/Ubuntu
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
2. Create a non-root admin user¶
Never operate day to day as root over SSH.
sudo useradd --create-home --shell /bin/bash deepak
sudo passwd deepak
sudo usermod -aG wheel deepak # 'sudo' group on Debian/Ubuntu
Set up key-based login for the new user (run on your laptop):
ssh-copy-id deepak@SERVER_IP
# then verify it works in a NEW terminal before continuing:
ssh deepak@SERVER_IP
See User & Permission Management for sudo scoping details.
3. Harden SSH¶
Edit /etc/ssh/sshd_config (or better, a drop-in in
/etc/ssh/sshd_config.d/99-hardening.conf):
# Disable direct root login over SSH
PermitRootLogin no
# Disable password auth entirely — keys only
PasswordAuthentication no
KbdInteractiveAuthentication no
# Disable legacy challenge/response
ChallengeResponseAuthentication no
# Reasonable idle timeout
ClientAliveInterval 300
ClientAliveCountMax 2
# Optional: restrict who can log in
AllowUsers deepak
Validate the config, then reload — not restart, so existing sessions hold:
Test before you trust
Open a new SSH session as your admin user now. Only once that succeeds should you close your original root session.
Changing the SSH port?
If you move SSH off 22, you must also open the new port in the firewall (Step 4) and, on RHEL with SELinux enforcing, register it:
4. Configure the firewall¶
Default-deny inbound, allow only what you serve.
# RHEL/AlmaLinux (firewalld)
sudo systemctl enable --now firewalld
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-all # verify
# Debian/Ubuntu (ufw)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 80,443/tcp
sudo ufw enable
sudo ufw status verbose
5. Block brute-force with fail2ban¶
fail2ban watches logs and temporarily bans IPs that fail auth repeatedly.
# RHEL/AlmaLinux (needs EPEL)
sudo dnf install -y epel-release
sudo dnf install -y fail2ban
# Debian/Ubuntu
sudo apt install -y fail2ban
Create /etc/fail2ban/jail.local (never edit jail.conf directly):
6. Reduce the attack surface¶
# See what's listening on the network
sudo ss -tulpn
# Disable services you don't need (example)
sudo systemctl disable --now cups avahi-daemon 2>/dev/null || true
Leave SELinux enforcing
On RHEL-family systems, resist the urge to disable SELinux. Check it's
enforcing and learn audit2allow instead:
7. Enable auditing and time sync¶
# Accurate time is a prerequisite for trustworthy logs and TLS
sudo systemctl enable --now chronyd # chrony on RHEL; systemd-timesyncd on Debian
chronyc tracking
# The audit daemon records security-relevant events
sudo systemctl enable --now auditd
Verification checklist¶
Run these to confirm the box is locked down:
sudo sshd -T | grep -Ei 'permitrootlogin|passwordauth' # no / no
sudo firewall-cmd --list-all # only intended services
sudo fail2ban-client status sshd # jail active
systemctl is-active dnf-automatic.timer chronyd auditd
getenforce # Enforcing
sudo ss -tulpn # no surprise listeners
Done
A fresh server with these steps applied has: keys-only SSH with no root login, a default-deny firewall, automatic patching, brute-force protection, accurate time, and auditing. That's a solid baseline — layer application-specific hardening on top.