Skip to content
Back to Blog
Security10 min read

Server Security Hardening Checklist: 12 Settings [Solved]

A practical checklist covering SSH lockdown, firewall rules, kernel tweaks, and logging you can apply in one session to harden a production server.

Written by Abdul AbrorTechnical Hosting Support Engineer
Server Security Hardening Checklist: 12 Settings [Solved]
On this page

You spin up a fresh VPS, install your stack, and move on. A month later you find brute-force attempts in the logs or worse—unauthorized access. Most breaches start with defaults left untouched.

This checklist walks through twelve server security hardening steps you can knock out in one session. Every setting here closes a common attack surface I've seen exploited in support tickets. Run these on Ubuntu, Debian, CentOS, or Rocky Linux before you consider the box production-ready.

1. Disable root SSH login

Root over SSH is the most hammered door on the internet. Create a sudo user, test it, then lock root out.

adduser deploy
usermod -aG sudo deploy

On CentOS or Rocky, the group is wheel instead of sudo. Test the new user in a second terminal before you close your root session.

Open /etc/ssh/sshd_config and set:

PermitRootLogin no

Restart SSH:

systemctl restart sshd

If you lock yourself out here, you'll need console access through your provider's panel.

2. Use SSH keys only

Password auth is slow and guessable. Generate an ed25519 key on your workstation:

ssh-keygen -t ed25519 -C "[email protected]"

Copy the public key to the server:

ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@your-server-ip

Test key login in a new terminal. Once confirmed, disable password auth in /etc/ssh/sshd_config:

PasswordAuthentication no
ChallengeResponseAuthentication no
PubkeyAuthentication yes

Restart sshd. From this point forward, only your private key opens the door.

3. Change the default SSH port

Port 22 gets scraped by every botnet on the planet. Moving SSH to a high port—say 2849 or 5122—drops log noise by ninety percent.

In /etc/ssh/sshd_config:

Port 2849

If you run a firewall (you should; see step 5), allow the new port before you restart SSH or you'll lock yourself out:

ufw allow 2849/tcp
systemctl restart sshd

Reconnect on the new port:

ssh -p 2849 deploy@your-server-ip

Security through obscurity isn't a strategy on its own, but it buys you cleaner logs and fewer failed-auth emails.

4. Set up fail2ban

Fail2ban watches logs and bans IPs after repeated failed attempts. Install it:

apt install fail2ban    # Debian/Ubuntu
yum install fail2ban    # CentOS/Rocky

Copy the default jail config:

cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Edit /etc/fail2ban/jail.local. Under [sshd], confirm:

[sshd]
enabled = true
port    = 2849
logpath = /var/log/auth.log
maxretry = 3
bantime  = 3600

Adjust port if you changed SSH's port. Start fail2ban:

systemctl enable fail2ban
systemctl start fail2ban

Check banned IPs:

fail2ban-client status sshd

In tickets I handled, fail2ban blocked thousands of brute-force attempts per day on public boxes.

5. Configure a firewall

Uncomplicated Firewall (ufw) is the easiest starting point on Debian-based systems. On CentOS or Rocky, firewalld ships by default.

ufw example

apt install ufw
ufw default deny incoming
ufw default allow outgoing
ufw allow 2849/tcp         # SSH
ufw allow 80/tcp           # HTTP
ufw allow 443/tcp          # HTTPS
ufw enable

Check rules:

ufw status verbose

firewalld example

firewall-cmd --permanent --add-port=2849/tcp
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload

Only open ports your services need. Every open port is another thing to patch.

6. Disable unused services

Default installs often enable services you'll never use. List running services:

systemctl list-unit-files --type=service --state=enabled

Stop and disable anything unnecessary. Common culprits:

systemctl disable --now avahi-daemon
systemctl disable --now cups
systemctl disable --now bluetooth

Servers rarely need Bluetooth or a print daemon. Fewer services mean fewer CVEs to track.

7. Keep the system updated

Automatic security updates prevent the low-hanging fruit exploits. On Ubuntu/Debian, install unattended-upgrades:

apt install unattended-upgrades
dpkg-reconfigure --priority=low unattended-upgrades

On CentOS/Rocky, dnf-automatic does the same job:

yum install dnf-automatic
systemctl enable --now dnf-automatic.timer

Edit /etc/dnf/automatic.conf and set apply_updates = yes if you want automatic installs. Kernel updates still need a reboot, but everything else applies overnight.

Manual check:

apt update && apt upgrade -y       # Debian/Ubuntu
yum update -y                      # CentOS/Rocky

Set a calendar reminder to review updates monthly if you disable automatic installs.

8. Harden kernel parameters with sysctl

The kernel exposes network and security knobs in /etc/sysctl.conf. Add these lines:

# Ignore ICMP ping requests
net.ipv4.icmp_echo_ignore_all = 1

# Disable IP forwarding (unless you're routing traffic)
net.ipv4.ip_forward = 0

# Enable SYN cookies to resist SYN flood attacks
net.ipv4.tcp_syncookies = 1

# Ignore ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# Disable source packet routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

# Log martian packets (packets with impossible addresses)
net.ipv4.conf.all.log_martians = 1

Apply immediately:

sysctl -p

These tweaks won't break normal traffic but make certain network-layer attacks harder.

9. Configure auditd for file integrity monitoring

The audit daemon records file access, command execution, and privilege escalation. Install it:

apt install auditd          # Debian/Ubuntu
yum install audit           # CentOS/Rocky

Add rules to watch sensitive files. Edit /etc/audit/rules.d/audit.rules:

-w /etc/passwd -p wa -k passwd_changes
-w /etc/shadow -p wa -k shadow_changes
-w /etc/ssh/sshd_config -p wa -k sshd_config_changes
-w /var/log/auth.log -p wa -k auth_log_changes

Reload rules:

auditctl -R /etc/audit/rules.d/audit.rules

Search audit logs:

ausearch -k passwd_changes

Auditd won't stop an attack in progress, but it tells you exactly what happened afterward.

10. Lock down file permissions

World-readable config files leak credentials. Fix common permission mistakes:

chmod 600 /etc/ssh/sshd_config
chmod 600 ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 640 /etc/shadow
chmod 644 /etc/passwd

Find files with overly permissive modes:

find /etc -type f -perm -002

Anything world-writable in /etc is a red flag.

11. Enable and configure logging

Centralized logging catches issues before they snowball. rsyslog ships with most distros. Confirm it's running:

systemctl status rsyslog

Edit /etc/rsyslog.conf to log auth attempts separately:

auth,authpriv.*  /var/log/auth.log

Restart rsyslog:

systemctl restart rsyslog

For multi-server setups, forward logs to a central syslog server or a cloud SIEM. Logs on the compromised box itself can be tampered with.

Rotate logs so they don't fill the disk. Check /etc/logrotate.d/rsyslog is present and configured.

12. Set up automated security scanning

Lynis is an open-source security auditing tool. Install and run it:

apt install lynis           # Debian/Ubuntu
yum install lynis           # CentOS/Rocky via EPEL

Run a full audit:

lynis audit system

Lynis scores your setup and flags weak spots—open ports, missing patches, unsafe permissions. Review the report in /var/log/lynis.log and fix high-priority warnings.

Schedule monthly audits in cron:

0 2 1 * * /usr/sbin/lynis audit system --cronjob

The cronjob flag suppresses interactive prompts.

What happens if you skip these steps?

Every default you leave in place is a door left unlocked. I've seen root accounts compromised in under six hours on a fresh box with SSH password auth still enabled. Botnets scan the entire IPv4 space constantly.

Hardening won't make you invincible—zero-days exist, supply-chain attacks happen—but it raises the bar high enough that automated scripts move on to softer targets. Most breaches exploit known weaknesses, not novel exploits.

Apply this checklist on every new server before you point DNS at it. Treat it like your deployment runbook: SSH lockdown, firewall rules, kernel tweaks, logging, scanning. An hour of setup prevents weeks of incident response.

How often should I re-audit server security?

Run Lynis monthly and review auth logs weekly. After major software updates or config changes, re-run the audit to catch regressions.

Can I automate this checklist with Ansible or a script?

Yes. Ansible roles for SSH hardening, firewall setup, and fail2ban exist in Ansible Galaxy. Script the post-install steps so every new box starts secure.

Do I need all twelve steps on a private network server?

If the box has no public IP, skip the SSH port change and tune fail2ban settings. Keep the firewall, updates, auditd, and kernel hardening—they defend against lateral movement if another host is compromised.

What if a hardening step breaks my application?

Test each change on a staging server first. The most common breakage is kernel parameter tweaks interfering with routing or container networking. Roll back the sysctl setting and research the conflict.

Where to start

If you can only do three things right now: disable root SSH login, set up key-only auth, and enable a firewall. Those three stop the majority of opportunistic attacks. Add fail2ban next, then work through the rest of the list as time permits.

Security isn't a one-time checklist—it's a baseline you maintain. But this baseline is where every hardened production server begins.