Skip to content
Back to Blog
Security10 min read

Server Hardening Checklist: 12 Steps to Lock Down Linux

A practical checklist covering SSH configuration, firewall rules, user permissions, and automated patching to secure your Linux server against common attack vectors.

Written by Abdul AbrorTechnical Hosting Support Engineer
Server Hardening Checklist: 12 Steps to Lock Down Linux
On this page

A fresh Linux server with default settings is an open door. I've seen too many compromised boxes where the first step was a dictionary attack on the root password or an exploit against an unpatched service listening on all interfaces.

This checklist walks you through twelve hardening steps that close the most common attack vectors. You won't need exotic tools or deep kernel knowledge—just SSH access and the patience to lock things down properly.

1. Disable Root Login Over SSH

Root access via SSH is the #1 brute-force target. Create a regular user with sudo privileges and turn off root SSH entirely.

Add a new user and give it sudo rights:

adduser yourname
usermod -aG sudo yourname

On CentOS or RHEL, replace sudo with wheel. Test that the new user can run sudo ls /root before you touch the SSH config.

Edit /etc/ssh/sshd_config:

PermitRootLogin no

Restart SSH:

systemctl restart sshd

Log out and back in as your regular user to confirm you can still connect and escalate privileges. Only then close the root session.

2. Use SSH Keys, Disable Password Authentication

Passwords can be guessed. SSH keys cannot. Generate a key pair on your local machine (not the server) if you haven't already:

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

Copy the public key to the server:

ssh-copy-id yourname@server_ip

Verify key-based login works, then disable password auth in /etc/ssh/sshd_config:

PasswordAuthentication no
ChallengeResponseAuthentication no

Restart sshd and test from a new terminal window before closing your existing session. If you lock yourself out at this stage, you'll need console access to fix it.

3. Change the Default SSH Port

Port 22 gets hammered by automated scanners. Moving SSH to a high port (above 1024, below 65535) drops noise in your auth logs by ninety percent or more.

Pick a port that isn't already in use—check with ss -tuln. Edit /etc/ssh/sshd_config:

Port 2849

If you're running a firewall (you should be; see step 4), open the new port before restarting SSH. Then:

systemctl restart sshd

Connect on the new port:

ssh -p 2849 yourname@server_ip

Leave your old session open until you confirm the new one works.

4. Configure a Firewall (UFW or firewalld)

A firewall blocks everything except the services you explicitly allow. On Debian and Ubuntu, ufw is the simplest choice. On CentOS and RHEL, use firewalld.

UFW (Debian/Ubuntu)

Install it:

sudo apt install ufw

Allow your SSH port (use the port you set in step 3):

sudo ufw allow 2849/tcp

If you're running a web server, allow HTTP and HTTPS:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

Enable the firewall:

sudo ufw enable

Check status:

sudo ufw status verbose

firewalld (CentOS/RHEL)

It's usually installed by default. Start and enable it:

sudo systemctl start firewalld
sudo systemctl enable firewalld

Allow SSH on your custom port:

sudo firewall-cmd --permanent --add-port=2849/tcp

Allow HTTP and HTTPS:

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https

Reload:

sudo firewall-cmd --reload

List active rules:

sudo firewall-cmd --list-all

5. Install and Configure Fail2Ban

Fail2Ban watches your logs and bans IPs that fail authentication too many times. It's your second line of defense after strong SSH config.

Install it:

sudo apt install fail2ban       # Debian/Ubuntu
sudo yum install fail2ban       # CentOS/RHEL

Copy the default config:

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

Edit /etc/fail2ban/jail.local and find the [sshd] section. Set:

enabled = true
port = 2849
maxretry = 3
bantime = 3600

Restart Fail2Ban:

sudo systemctl restart fail2ban
sudo systemctl enable fail2ban

Check banned IPs:

sudo fail2ban-client status sshd

You'll see bans start appearing within hours if your server is publicly reachable.

6. Keep the System Patched

Unpatched software is the easiest way in. Automate updates so you don't have to remember.

Debian/Ubuntu: unattended-upgrades

Install:

sudo apt install unattended-upgrades

Enable automatic security updates:

sudo dpkg-reconfigure --priority=low unattended-upgrades

Edit /etc/apt/apt.conf.d/50unattended-upgrades to configure which updates to apply. The default installs security patches automatically.

CentOS/RHEL: dnf-automatic

Install:

sudo yum install dnf-automatic

Edit /etc/dnf/automatic.conf and set:

apply_updates = yes

Enable the timer:

sudo systemctl enable --now dnf-automatic.timer

You can still run manual updates (apt update && apt upgrade or yum update) whenever you want. The automation just ensures you don't fall months behind.

7. Disable Unused Services

Every running service is a potential entry point. List what's listening:

sudo ss -tuln

You'll see port numbers and process names. If you don't recognize a service or don't need it, stop and disable it:

sudo systemctl stop service_name
sudo systemctl disable service_name

Common candidates: avahi-daemon, cups, bluetooth. On a headless server, you don't need them.

8. Configure User Permissions and sudo

Never share user accounts. Each person gets their own account and their own SSH key. If someone leaves, you remove one key—not all of them.

Add a new user:

sudo adduser newuser

Give them sudo access only if they need it:

sudo usermod -aG sudo newuser    # Debian/Ubuntu
sudo usermod -aG wheel newuser   # CentOS/RHEL

For finer control, edit /etc/sudoers with visudo. You can restrict which commands a user can run as root. Example:

newuser ALL=(ALL) /usr/bin/systemctl restart nginx

That line lets newuser restart nginx with sudo but nothing else.

9. Set Up File Integrity Monitoring with AIDE

AIDE (Advanced Intrusion Detection Environment) takes a snapshot of your file system and alerts you when critical files change.

Install:

sudo apt install aide           # Debian/Ubuntu
sudo yum install aide           # CentOS/RHEL

Initialize the database (this takes a few minutes):

sudo aideinit

On Debian/Ubuntu, copy the new database:

sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db

Run a check:

sudo aide --check

No output means no changes since the baseline. Schedule daily checks with a cron job:

sudo crontab -e

Add:

0 2 * * * /usr/bin/aide --check | mail -s "AIDE report" [email protected]

You'll get an email every morning if something changed in /etc, /bin, or other monitored directories.

10. Harden Kernel Parameters with sysctl

The Linux kernel exposes hundreds of tunable parameters. A few of them improve security with zero downside.

Edit /etc/sysctl.conf (or create a new file in /etc/sysctl.d/) and add:

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

# Disable IP forwarding (unless you're building a router)
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0

# Enable SYN cookies (defense against SYN flood)
net.ipv4.tcp_syncookies = 1

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

# Log martian packets
net.ipv4.conf.all.log_martians = 1

Apply:

sudo sysctl -p

Verify:

sysctl net.ipv4.icmp_echo_ignore_all

You should see 1.

11. Enable and Review System Logs

Logs tell you what happened before, during, and after an incident. Make sure logging is enabled and you actually look at the logs.

Key log files:

  • /var/log/auth.log (Debian/Ubuntu) or /var/log/secure (CentOS/RHEL): SSH logins, sudo usage, authentication failures
  • /var/log/syslog or /var/log/messages: general system messages
  • /var/log/fail2ban.log: Fail2Ban actions

Watch SSH auth attempts in real time:

sudo tail -f /var/log/auth.log

You'll see every connection attempt and whether it succeeded. If you see hundreds of failed logins from unfamiliar IPs, your hardening is working.

For centralized logging or long-term retention, forward logs to a remote syslog server or set up rsyslog with rotation policies.

12. Use a Mandatory Access Control System (AppArmor or SELinux)

MAC systems enforce policies that restrict what processes can do—even if they're running as root. AppArmor (Debian/Ubuntu default) and SELinux (CentOS/RHEL default) add a security layer that stops attackers who gain shell access.

AppArmor (Debian/Ubuntu)

Check status:

sudo aa-status

You'll see a list of profiles in enforce or complain mode. Enforce mode blocks violations; complain mode logs them. Most distros ship with profiles for common services (Apache, nginx, MySQL).

If a service doesn't have a profile, you can generate one with aa-genprof, but that's beyond this checklist. The important part: leave AppArmor enabled and don't disable profiles unless you have a specific reason.

SELinux (CentOS/RHEL)

Check status:

sudo sestatus

SELinux has three modes: enforcing, permissive, and disabled. Enforcing is what you want. If it's disabled, edit /etc/selinux/config:

SELINUX=enforcing

Reboot for the change to take effect. SELinux can be tricky—context errors will prevent services from starting. When that happens, check the audit log:

sudo ausearch -m avc -ts recent

You'll see what was blocked and why. Most issues are fixed by restoring correct file contexts:

sudo restorecon -Rv /path/to/directory

Don't set SELinux to permissive just because something breaks. Fix the underlying issue instead.

What have you patched this month?

These twelve steps cover the baseline. You won't stop a determined attacker with deep pockets, but you will stop opportunistic scans and automated exploit attempts—which account for the majority of compromised servers I've dealt with in support.

Hardening isn't a one-time task. New vulnerabilities appear every week. Set a calendar reminder to review your firewall rules, check for outdated packages, and rotate SSH keys every six months. The effort is small. The cost of a breach is not.

FAQ

Do I need all twelve steps, or can I skip some?
Steps 1-6 are non-negotiable: SSH hardening, firewall, Fail2Ban, and patching. The rest add defense in depth. If you're running a production server or handling customer data, do all twelve.

What if I lock myself out after changing SSH settings?
You'll need console access through your hosting provider's control panel or KVM. That's why you test each SSH change in a new terminal window before closing your existing session.

How often should I update the AIDE database?
After every intentional system change—installing software, editing configs, etc. Run sudo aideinit and replace the old database. Otherwise AIDE will flag your own changes as suspicious.

Can I automate all of this with a script?
Yes, with Ansible, Bash, or similar tools. But walk through it manually at least once so you understand what each step does and how to troubleshoot when something breaks.

Where to focus first

If you only have an hour, do steps 1-4: disable root login, switch to SSH keys, change the default port, and turn on a firewall. That blocks the lowest-hanging fruit. Schedule the remaining steps for later in the week, and don't skip the automated patching—unpatched boxes get owned faster than misconfigured ones.