Skip to content
Back to Blog
Security10 min read

Server Security Hardening Checklist: 12 Config Changes

Lock down your Linux server with twelve essential configuration changes that prevent unauthorized access and catch intruders early.

Written by Abdul AbrorTechnical Hosting Support Engineer
Server Security Hardening Checklist: 12 Config Changes
On this page

A fresh server feels like a clean slate. It's also a wide-open door. Default configurations prioritize convenience over security, and attackers know it—automated bots start probing SSH within minutes of a new IP going live. I've restored servers after breaches that could have been prevented with thirty minutes of hardening work.

This checklist covers twelve configuration changes that form the security baseline for any Linux server. Skip them and you're gambling. Apply them and you close the easy entry points.

1. Disable root SSH login

Root access over SSH is the first thing brute-force scripts try. Disable it.

Edit /etc/ssh/sshd_config and set:

PermitRootLogin no

Restart SSH:

systemctl restart sshd

From now on you'll SSH as a regular user and escalate with sudo. If you don't have a non-root user yet, create one first:

adduser deployuser
usermod -aG sudo deployuser

Test the new user's SSH access before closing your root session. I've locked myself out by skipping this step.

2. Use SSH key authentication only

Passwords can be guessed. Keys cannot.

Generate an SSH key pair on your local machine if you don't have one:

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

Copy the public key to your server:

ssh-copy-id deployuser@your-server-ip

Now disable password authentication. Back in /etc/ssh/sshd_config:

PasswordAuthentication no
ChallengeResponseAuthentication no
PubkeyAuthentication yes

Restart SSH again. Verify you can still connect with your key before logging out.

3. Change the default SSH port

Port 22 gets hammered constantly. Moving SSH to a non-standard port won't stop a determined attacker but it will eliminate 99% of the automated noise in your logs.

In /etc/ssh/sshd_config:

Port 2849

Pick any unused port above 1024. Restart SSH and update your firewall rules to allow the new port before you close your session. Then connect using:

ssh -p 2849 deployuser@your-server-ip

Your auth logs will get quieter immediately.

4. Configure a firewall with default-deny

A firewall should block everything except the specific ports your services need. On Ubuntu and Debian, ufw makes this simple.

Install it:

apt install ufw

Set default policies:

ufw default deny incoming
ufw default allow outgoing

Allow your SSH port:

ufw allow 2849/tcp

If you're running a web server:

ufw allow 80/tcp
ufw allow 443/tcp

Enable the firewall:

ufw enable

Check status:

ufw status verbose

On RHEL-based systems use firewalld instead. The principle is the same: deny everything, then poke specific holes.

5. Install fail2ban

Fail2ban watches your logs and bans IPs that show malicious behavior—repeated failed logins, scanning attempts, exploit probes.

Install:

apt install fail2ban

Copy the default config:

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

Edit /etc/fail2ban/jail.local and enable the SSH jail:

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

Restart fail2ban:

systemctl restart fail2ban

Check banned IPs:

fail2ban-client status sshd

In support tickets I handled, servers without fail2ban averaged thousands of failed login attempts per day. With it, attackers get three tries and then they're gone.

6. Enable automatic security updates

Patching is not optional. Unpatched servers get compromised.

On Debian/Ubuntu, install unattended-upgrades:

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

Edit /etc/apt/apt.conf.d/50unattended-upgrades and ensure security updates are enabled:

Unattended-Upgrade::Allowed-Origins {
    "${distro_id}:${distro_codename}-security";
};

On RHEL/CentOS, use yum-cron or dnf-automatic. The goal is the same: security patches apply automatically without waiting for manual intervention.

You still need to reboot for kernel updates. Check /var/run/reboot-required on Debian systems or look for a new kernel in /boot.

7. Disable unused services

Every running service is a potential attack surface. List all enabled services:

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

Do you really need Avahi? Bluetooth? Cups? Disable what you don't use:

systemctl disable avahi-daemon
systemctl stop avahi-daemon

I've seen fresh installs running a dozen services the server will never need. Each one is code that could have a vulnerability.

8. Configure audit logging with auditd

Audit logs record who did what and when. If someone compromises your server, these logs show you how.

Install:

apt install auditd audispd-plugins

Add rules to watch critical files. Create /etc/audit/rules.d/custom.rules:

-w /etc/passwd -p wa -k passwd_changes
-w /etc/shadow -p wa -k shadow_changes
-w /etc/sudoers -p wa -k sudoers_changes
-w /var/log/auth.log -p wa -k auth_log_changes

Reload rules:

augenrules --load

Search logs:

ausearch -k passwd_changes

Audit logs catch privilege escalation attempts and unauthorized file modifications that regular logs miss.

9. Set up log rotation and remote backup

Logs fill disks and attackers delete logs. Fix both problems.

Logrotate is usually installed by default. Check /etc/logrotate.d/rsyslog:

/var/log/syslog
/var/log/auth.log
{
    rotate 7
    daily
    missingok
    compress
    delaycompress
}

For remote backup, forward logs to a separate syslog server or a hosted logging service. In /etc/rsyslog.conf:

*.* @@logserver.example.com:514

Restart rsyslog:

systemctl restart rsyslog

If an attacker wipes your local logs, you still have copies somewhere they can't reach.

10. Harden kernel parameters with sysctl

The kernel has dozens of security-relevant settings. Most distributions ship with reasonable defaults but a few tweaks help.

Edit /etc/sysctl.conf and add:

# Disable IP forwarding
net.ipv4.ip_forward = 0

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

# Ignore source-routed packets
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

# Enable SYN cookies
net.ipv4.tcp_syncookies = 1

# Log martians
net.ipv4.conf.all.log_martians = 1

Apply:

sysctl -p

These settings protect against IP spoofing and some DoS attacks.

11. Restrict cron and at to authorized users

Scheduled jobs are a common persistence mechanism for attackers. Limit who can create them.

Create /etc/cron.allow and /etc/at.allow with just the usernames that need cron access:

root
deployuser

If these files exist, only listed users can use cron and at. Everyone else is denied by default.

Also review existing cron jobs:

ls -la /etc/cron.*
crontab -l -u root

Unfamiliar entries? Investigate them.

12. Enable and configure AppArmor or SELinux

Mandatory access control (MAC) systems confine processes even if they're compromised. AppArmor ships with Ubuntu, SELinux with RHEL.

Check AppArmor status:

aa-status

If profiles are in complain mode, switch them to enforce:

aa-enforce /etc/apparmor.d/*

SELinux status:

getenforce

If it's permissive, set it to enforcing in /etc/selinux/config:

SELINUX=enforcing

Then reboot. MAC systems have a learning curve but they limit what an attacker can do even if they get shell access.

What happens after hardening?

You'll notice quieter logs, fewer alerts, and—most importantly—no evidence of compromise. Hardening doesn't make your server invincible but it raises the bar high enough that automated attacks move on to easier targets.

Schedule time every quarter to review configurations, check for new CVEs affecting your stack, and audit user accounts. Security is not a one-time checklist; it's a maintenance task. But these twelve changes give you the foundation everything else builds on.

FAQ

Do I need all twelve changes or can I skip some?
You need all of them. Each one closes a different attack vector. Skipping steps leaves gaps.

Will changing the SSH port break my automation?
Only if your scripts hard-code port 22. Update your SSH configs and automation to use the new port. It takes five minutes.

Can I run fail2ban and a firewall together?
Yes. They work at different layers. The firewall controls which ports are open; fail2ban bans IPs that abuse those open ports.

What if automatic updates break something?
Security updates are tested heavily before release. Breakage is rare. If you're worried, configure automatic updates for security patches only and handle feature upgrades manually.

How do I know if my server was compromised before I hardened it?
Check auth logs for successful logins you don't recognize, review running processes, examine cron jobs, and scan for unusual network connections. If you find anything suspicious, assume compromise and rebuild from a clean image.

What to check first

Start with SSH and the firewall—those are your biggest attack surfaces. Then add fail2ban and automatic updates. The rest of the checklist can follow over the next day or two, but get the first four done in the first hour. Your server is under active scan right now. Don't give attackers time to find an opening.

Every server I manage follows this baseline. The ones that don't are the ones that eventually appear in my inbox with "urgent: server compromised" in the subject line.