Security checklist: 10 things to verify on any Linux server

An actionable checklist of 10 essential security checks to run on any production Linux server.

Why a systematic security checklist?

Securing a Linux server is about far more than installing a firewall. It is a set of complementary layers that, taken individually, look harmless, but missing even one of them can compromise your entire infrastructure. A misconfigured server is an open door for attackers, whether automated bots or targeted intrusions.

This checklist brings together 10 critical checks to run on any production Linux server. For each item you will find the verification command, the expected result and the fix to apply if the check fails. The goal is to have a repeatable process you can run on every new server or during a periodic audit.


1. sécuriser SSH: keys only, custom port, root disabled

The SSH service is the first target attackers go for. A server exposed on port 22 with password authentication receives, on average, thousands of brute-force attempts per day. Three measures are essential: disable password authentication, change the default port and forbid direct root login.

# Check the SSH configuration
grep -E "^(PasswordAuthentication|PermitRootLogin|Port)" /etc/ssh/sshd_config

Expected: PasswordAuthentication no, PermitRootLogin no, and a port other than 22.

Fix: Edit /etc/ssh/sshd_config with the values above, then reload the service with systemctl reload sshd. Make sure you have placed your public key in ~/.ssh/authorized_keys before disabling passwords. See the complete OpenSSH Server guide for a detailed configuration.


2. Active firewall with a default-deny policy

A properly configured firewall blocks all traffic that is not explicitly allowed. Without a deny by default policy, every service you start is potentially reachable from the outside.

# Check the UFW status and the default policy
sudo ufw status verbose

Expected: Status active, default policy deny (incoming), and only the necessary ports open (SSH, HTTP/HTTPS).

Fix: Enable UFW and configure the default policy:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 2222/tcp  # your SSH port
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

For a complete setup, refer to the UFW tutorial.


3. Automatic updates configured

Known security flaws are exploited within hours of being published. Automatic security updates drastically shrink that window of exposure.

# Check that unattended-upgrades is active (Debian/Ubuntu)
systemctl is-enabled unattended-upgrades
cat /etc/apt/apt.conf.d/20auto-upgrades

Expected: The service is enabled and the file contains APT::Periodic::Unattended-Upgrade "1";.

Fix: Install and configure automatic updates:

sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

Verify in /etc/apt/apt.conf.d/50unattended-upgrades that the Unattended-Upgrade::Allowed-Origins line does include your distribution's security updates.


4. Non-root user with sudo

Working as root day to day multiplies the risks: a mistyped command, a malicious script or a session compromise will have maximum impact. A dedicated user with limited sudo privileges is the norm.

# Check the members of the sudo group
getent group sudo
# Check that root has no active password
sudo passwd -S root

Expected: At least one non-root user in the sudo group. The root password status should be L (locked).

Fix: Create a dedicated user and lock root:

sudo adduser deployer
sudo usermod -aG sudo deployer
sudo passwd -l root

5. Permissions on sensitive files

Overly broad permissions on critical system files let any local user read sensitive information, such as password hashes or the server's SSH private keys.

# Check the permissions of critical files
stat -c "%a %U %G %n" /etc/shadow /etc/gshadow /etc/ssh/sshd_config
ls -la /etc/ssh/ssh_host_*_key

Expected: /etc/shadow at 640 (owner root, group shadow). SSH private keys at 600, owned exclusively by root.

Fix:

sudo chmod 640 /etc/shadow /etc/gshadow
sudo chmod 600 /etc/ssh/ssh_host_*_key
sudo chmod 644 /etc/ssh/ssh_host_*_key.pub
sudo chmod 600 /etc/ssh/sshd_config

6. Unnecessary services disabled

Every running service is an additional attack surface. A production server should only run the services strictly required for its role. The systemd manager makes it easy to list and disable superfluous services.

# List all active services
systemctl list-unit-files --type=service --state=enabled
# List listening ports
ss -tlnp

Expected: Only the necessary services are enabled. No unexpected port listening.

Fix: Disable the unnecessary services:

# Example: disable a service that is not needed
sudo systemctl stop cups.service
sudo systemctl disable cups.service
sudo systemctl mask cups.service

Services commonly useless on a server: cups (printing), avahi-daemon (network discovery), bluetooth, ModemManager.


7. Fail2ban or equivalent active

Fail2ban monitors authentication logs and automatically bans IP addresses that repeatedly fail to connect. It is an indispensable layer of defense against brute force, even though it does not replace SSH keys.

# Check the status of Fail2ban
sudo systemctl is-active fail2ban
sudo fail2ban-client status
sudo fail2ban-client status sshd

Expected: The service is active, the sshd jail is enabled with a Currently banned count greater than or equal to zero.

Fix: Install and configure Fail2ban:

sudo apt install fail2ban
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo systemctl enable --now fail2ban

See the Fail2ban guide to configure jails, ban durations and email notifications.


8. Centralized and monitored logs

Unmonitored logs are useless. You must be able to detect an intrusion or abnormal behavior within a reasonable time. Centralizing logs and setting up alerts are essential.

# Check that journald is working and persisting logs
journalctl --disk-usage
cat /etc/systemd/journald.conf | grep Storage
# Check for recent logs
journalctl --since "1 hour ago" --no-pager | tail -20

Expected: Storage=persistent in the journald configuration. Logs are recent and storage is not saturated.

Fix: Configure log persistence and a suitable rotation:

# Enable persistence
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
# Configure rotation
sudo journalctl --vacuum-size=500M

For a production server, consider a centralization solution such as rsyslog to a remote server, or an ELK/Loki stack for analysis.


9. Backups tested and working

Having backups is not enough: you have to test them regularly. A backup that has never been restored is only a promise, not a guarantee. Verify the existence, freshness and integrity of your backups.

# Check the date of the last backup (adjust the path)
ls -lh /var/backups/ | head -10
# Check the integrity of an archive
tar -tzf /var/backups/latest-backup.tar.gz > /dev/null 2>&1 && echo "OK" || echo "CORRUPTED"
# Check available space
df -h /var/backups/

Expected: The last backup is less than 24 hours old (or per your policy). The archive is readable without errors. Disk space is sufficient for at least two backup cycles.

Fix: Set up an automated backup script via cron and test restoration on a staging environment at least once a month. Document the restoration procedure and the time it takes (RTO).


10. Regular audit with Lynis

Lynis is an open-source security auditing tool that analyzes your system in depth and assigns a hardening score. It covers hundreds of checks that this checklist cannot all list. It is the ideal complement to your manual checks.

# Install Lynis
sudo apt install lynis
# Run a full audit
sudo lynis audit system
# Review the score and the suggestions
grep "Hardening index" /var/log/lynis.log
grep "Suggestion" /var/log/lynis-report.dat | head -20

Expected: A hardening score (Hardening Index) above 70 for a properly configured server. No critical alert (Warning) left unaddressed.

Fix: Go through the Lynis suggestions and handle them in order of priority. Schedule a monthly automated audit via cron and compare scores over time to catch regressions. The Lynis tutorial details how to interpret the results and the most common fixes.


Automating with an audit script

Running this checklist manually on every server is tedious and error-prone. The good practice is to create a Bash script that automates these 10 checks and generates a structured report.

#!/bin/bash
# security-audit.sh - Automated checklist
echo "=== Security audit - $(hostname) - $(date) ==="

echo "[1] SSH Configuration"
grep -E "^(PasswordAuthentication|PermitRootLogin|Port)" /etc/ssh/sshd_config

echo "[2] Firewall"
sudo ufw status | head -5

echo "[3] Automatic updates"
systemctl is-enabled unattended-upgrades 2>/dev/null || echo "NOT CONFIGURED"

echo "[4] Sudo user"
getent group sudo

echo "[5] Sensitive file permissions"
stat -c "%a %n" /etc/shadow /etc/ssh/sshd_config

echo "[6] Active services"
systemctl list-unit-files --type=service --state=enabled --no-pager | wc -l

echo "[7] Fail2ban"
sudo systemctl is-active fail2ban 2>/dev/null || echo "INACTIVE"

echo "[8] Logs"
journalctl --disk-usage 2>/dev/null

echo "[9] Backups"
ls -lh /var/backups/ 2>/dev/null | head -5

echo "[10] Lynis"
lynis --version 2>/dev/null || echo "NOT INSTALLED"

echo "=== End of audit ==="

Make this script executable with chmod +x security-audit.sh and schedule it as a weekly cron job. Redirect the output to a file or send it by email to keep a history. Over time, enrich this script with checks specific to your infrastructure: TLS certificates, the state of Docker containers, the versions of deployed applications.

Security is a continuous process, not a final state. By applying this checklist systematically and automating it, you build a solid foundation that significantly reduces your attack surface and lets you catch regressions before they are exploited.

Did you enjoy this article?

Comments

Morgann Riu

Cybersecurity and Linux administration expert. I help companies secure and optimize their critical infrastructures.

Back to the blog

Checklist Sécurité Linux

30 points essentiels pour sécuriser un serveur Linux. Recevez aussi les nouveaux tutoriels par email.

Pas de spam. Désabonnement en 1 clic.