Linux Server Security Hardening: A Complete Practitioner Checklist
Every Linux server you deploy starts with a configuration optimized for compatibility and ease of use -- not security. Default settings allow root SSH login, leave unused services running, and configure kernel parameters that benefit performance over attack surface reduction. This guide provides a prioritized, command-level hardening checklist aligned with CIS Benchmarks Level 1 and Level 2, with guidance for RHEL/CentOS/Rocky, Ubuntu, and Debian. Apply these controls in order of risk reduction, starting with SSH and authentication before moving to kernel-level hardening.
User Account and Authentication Hardening
Authentication controls are the first thing attackers test. Misconfigured accounts are the primary initial access vector on Linux servers.
Disable root login and enforce sudo:
# Lock the root account password
passwd -l root
# Verify root shell is set to nologin (belt-and-suspenders)
usermod -s /sbin/nologin root
# Create an admin group and restrict sudo to it
groupadd -r admins
usermod -aG admins your-admin-user
# /etc/sudoers.d/admins
%admins ALL=(ALL) ALL
Remove default and unused accounts:
# List accounts with shells (non-system accounts)
awk -F: '$7 ~ /bash|sh|zsh/ {print $1}' /etc/passwd
# Lock unused service accounts
for acct in games news lp sync shutdown halt operator; do
usermod -L -s /sbin/nologin $acct 2>/dev/null
done
Enforce strong password policy:
For systems using PAM (Ubuntu/RHEL):
# Install libpam-pwquality (Ubuntu) or pam_pwquality (RHEL)
apt install libpam-pwquality # Ubuntu
dnf install libpwquality # RHEL
# /etc/security/pwquality.conf
minlen = 14
dcredit = -1
ucredit = -1
ocredit = -1
lcredit = -1
maxrepeat = 3
Set account lockout policy:
# /etc/security/faillock.conf (RHEL 8+/Ubuntu 20.04+)
deny = 5
unlock_time = 900
fail_interval = 900
Password aging:
# /etc/login.defs
PASS_MAX_DAYS 90
PASS_MIN_DAYS 1
PASS_WARN_AGE 14
# Apply to existing accounts
chage --maxdays 90 --mindays 1 --warndays 14 username
SSH Hardening
SSH is the primary remote access method and the primary attack vector. A properly hardened sshd_config eliminates most automated attack vectors.
Full /etc/ssh/sshd_config hardened configuration:
# Protocol and key exchange
Protocol 2
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group14-sha256
HostKeyAlgorithms ecdsa-sha2-nistp256,ssh-ed25519
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
# Authentication
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM yes
MaxAuthTries 3
MaxSessions 4
LoginGraceTime 30
# Access controls
AllowGroups admins # only members of admins group can SSH
AllowUsers specific-user@10.0.0.0/8 # optionally restrict by IP
# Hardening
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no # unless required for bastion use case
PrintLastLog yes
Banner /etc/issue.net
ClientAliveInterval 300
ClientAliveCountMax 2
Change the default SSH port (defense-in-depth against automated scanners):
# sshd_config
Port 22022 # non-default port reduces automated scan noise by ~80%
# Update firewalld / ufw
firewall-cmd --permanent --remove-service=ssh
firewall-cmd --permanent --add-port=22022/tcp
firewall-cmd --reload
# or
ufw delete allow ssh
ufw allow 22022/tcp
Generate strong host keys:
# Remove weak RSA and DSA keys
rm /etc/ssh/ssh_host_rsa_key* /etc/ssh/ssh_host_dsa_key* 2>/dev/null
# Generate Ed25519 host key
ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""
Implement fail2ban or firewalld for SSH brute-force protection:
# Ubuntu
apt install fail2ban
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# /etc/fail2ban/jail.local [sshd] section:
# enabled = true
# maxretry = 3
# bantime = 1h
# findtime = 10m
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
Kernel and Network Parameter Hardening (sysctl)
The Linux kernel ships with parameters that favor compatibility over security. The sysctl settings below harden the kernel against network-based attacks and information disclosure.
Create /etc/sysctl.d/99-hardening.conf:
# Network: IP spoofing protection
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Network: Disable source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
# Network: Disable ICMP redirect acceptance and sending
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
# Network: Enable SYN cookies (SYN flood protection)
net.ipv4.tcp_syncookies = 1
# Network: Ignore ICMP broadcast requests
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Network: Log suspicious packets
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
# Kernel: Restrict core dumps
fs.suid_dumpable = 0
# Kernel: Enable ASLR
kernel.randomize_va_space = 2
# Kernel: Restrict kernel pointer exposure
kernel.kptr_restrict = 2
# Kernel: Restrict dmesg access
kernel.dmesg_restrict = 1
# Kernel: Restrict ptrace (prevents process injection)
kernel.yama.ptrace_scope = 1
# Apply immediately
sysctl --system
File System and Service Hardening
Mount options for temporary and shared file systems:
# /etc/fstab additions -- prevent execution from world-writable mounts
tmpfs /tmp tmpfs defaults,nodev,nosuid,noexec 0 0
tmpfs /var/tmp tmpfs defaults,nodev,nosuid,noexec 0 0
tmpfs /dev/shm tmpfs defaults,nodev,nosuid,noexec 0 0
Find and remediate dangerous SUID/SGID binaries:
# List all SUID binaries not in standard paths
find / -perm -4000 -type f -not -path "/usr/*" -not -path "/bin/*" 2>/dev/null
# Remove SUID from binaries that do not require it
chmod u-s /usr/bin/at # if job scheduling not needed
chmod u-s /usr/bin/mount # if users should not mount
Disable unnecessary services:
# List running services
systemctl list-units --type=service --state=running
# Disable common unnecessary services on a minimal server
for svc in cups avahi-daemon bluetooth postfix rpcbind nfs-server; do
systemctl disable --now $svc 2>/dev/null
done
AppArmor (Ubuntu) or SELinux (RHEL) enforcement:
# Ubuntu -- verify AppArmor is enforcing
aa-status | grep "profiles are in enforce mode"
# RHEL -- verify SELinux is enforcing
getenforce # should return "Enforcing"
sestatus
# If permissive, enable enforcement:
setenforce 1
sed -i 's/SELINUX=permissive/SELINUX=enforcing/' /etc/selinux/config
Firewall baseline (minimal server):
# Ubuntu (ufw)
ufw default deny incoming
ufw default allow outgoing
ufw allow from 10.0.0.0/8 to any port 22022 # SSH from internal only
ufw enable
# RHEL (firewalld)
firewall-cmd --set-default-zone=drop
firewall-cmd --permanent --zone=trusted --add-source=10.0.0.0/8
firewall-cmd --permanent --zone=trusted --add-port=22022/tcp
firewall-cmd --reload
Audit Logging with auditd
Auditd is the Linux kernel audit framework. It provides tamper-resistant logging of security-relevant events -- file access, privilege use, system calls -- that cannot be manipulated by processes running as root (logs go to a kernel ring buffer before being written to disk).
Install and enable auditd:
apt install auditd audispd-plugins # Ubuntu
dnf install audit # RHEL
systemctl enable --now auditd
CIS-aligned audit rules (/etc/audit/rules.d/hardening.rules):
# Delete all existing rules
-D
# Buffer size (increase for high-throughput servers)
-b 8192
# Failure mode: 1=print, 2=panic (use 2 in PCI/high-security environments)
-f 1
# Log all authentication events
-w /etc/passwd -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/sudoers -p wa -k sudoers
-w /var/log/faillog -p wa -k logins
-w /var/log/lastlog -p wa -k logins
# Monitor sudo usage
-w /usr/bin/sudo -p x -k sudo_usage
# Monitor SSH configuration changes
-w /etc/ssh/sshd_config -p wa -k sshd_config
# Track privileged command use
-a always,exit -F arch=b64 -S execve -F euid=0 -F auid>=1000 -k privileged
# Monitor kernel module loading (rootkit indicator)
-w /sbin/insmod -p x -k modules
-w /sbin/rmmod -p x -k modules
-w /sbin/modprobe -p x -k modules
-a always,exit -F arch=b64 -S init_module -S delete_module -k modules
# Make rules immutable until reboot
-e 2
Reload rules: augenrules --load or service auditd restart
Forward auditd to your SIEM:
Use audisp-remote or Filebeat's auditd module to ship logs. Key fields to alert on: key=sudo_usage for all privilege escalation, key=identity for any passwd/shadow modification, key=modules for kernel module operations (rootkit indicator).
CIS Benchmark Compliance Assessment
The CIS Benchmarks for RHEL and Ubuntu provide ~200+ controls organized into Level 1 (broad compatibility, significant security improvement) and Level 2 (defense-in-depth, may impact functionality). Start with Level 1 and plan Level 2 for your highest-sensitivity servers.
Automated assessment tools:
| Tool | License | Notes |
|---|---|---|
| OpenSCAP / oscap | Open source | CIS-certified, generates HTML/XML reports |
| Lynis | Open source | Broader than CIS, fast for quick audits |
| CIS-CAT Pro | Commercial | Official CIS tool, remediation guidance |
| Wazuh | Open source | SIEM + FIM + CIS benchmark checking combined |
Run OpenSCAP against CIS benchmark (RHEL):
dnf install scap-security-guide openscap-scanner
oscap xccdf eval \
--profile xccdf_org.ssgproject.content_profile_cis_server_l1 \
--report /tmp/cis-report.html \
/usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml
Priority Level 1 controls by impact:
| Control | CIS Reference | Implementation |
|---|---|---|
| SSH root login disabled | 5.2.8 | PermitRootLogin no |
| Password authentication disabled | 5.2.11 | PasswordAuthentication no |
| auditd enabled and running | 4.1.1 | systemctl enable auditd |
| SUID dumpable = 0 | 1.5.1 | kernel sysctl |
| AppArmor/SELinux enforcing | 1.6.1 | setenforce 1 |
| No unconfined services | 1.6.1.4 | aa-status / sestatus |
| Firewall enabled | 3.4.1 | ufw/firewalld active |
| ASLR enabled | 1.5.3 | randomize_va_space = 2 |
The bottom line
Linux server hardening is most effective when treated as a baseline image process rather than a per-server checklist. Build a hardened golden image (AMI, VMware template, or container base image) with all controls applied, validate it against CIS Level 1 with OpenSCAP, and use that as the foundation for every new deployment. Auditd and SSH hardening deliver the fastest risk reduction for the least operational friction -- start there and work outward.
Frequently asked questions
What is the most impactful first step for Linux server hardening?
SSH hardening provides the highest return on investment. Disable password authentication (PasswordAuthentication no), disable root login (PermitRootLogin no), restrict allowed users or groups (AllowGroups admins), and set MaxAuthTries 3. These four settings eliminate the most common Linux server attack vector -- automated credential stuffing and brute-force against SSH -- with zero functionality impact for any properly managed server environment.
Does changing the SSH port actually improve security?
Changing the SSH port to a non-standard value (such as 22022) reduces automated bot scanning noise by roughly 80% -- most internet-wide scanners target port 22 specifically. It is not a substitute for key-based authentication or proper hardening, but it is a low-effort control that reduces log noise and gives you time to respond to targeted scans. Treat it as defense-in-depth, not a primary control.
What is the difference between AppArmor and SELinux?
Both are Linux mandatory access control (MAC) frameworks that enforce security policies beyond discretionary (user/group) permissions. SELinux uses a label-based model where every process, file, and socket has a security context; policies define which context transitions are allowed. It is more comprehensive but also more complex. AppArmor uses file path-based profiles that are easier to write and understand. Ubuntu ships with AppArmor; RHEL/Fedora use SELinux. Both in enforcing mode provide significant protection against privilege escalation and container escapes.
How do I harden Linux servers at scale without manual configuration?
Use configuration management tools to enforce hardening as code: Ansible has the `devsec.hardening` role (formerly dev-sec.io) that implements CIS Benchmark controls; Chef InSpec has CIS profiles for compliance checking; Terraform can bake hardened AMIs via Packer. The workflow is: apply hardening role to a base image, run OpenSCAP or Lynis to validate, publish the hardened image, and use that image for all new deployments. Drift detection (Wazuh, Osquery, or cloud-native tools like AWS Config) then monitors for configuration changes post-deployment.
Should I use fail2ban or firewalld/ufw for SSH protection?
Use both as complementary layers. A firewall (ufw or firewalld) should restrict SSH access to known source IP ranges (VPN, bastion, management network) -- this is the most effective control and requires no pattern matching. Fail2ban adds dynamic blocking of IPs that generate authentication failures, which catches attackers coming from within your allowed ranges (compromised VPN clients, lateral movement). If you can restrict SSH to known source IPs via firewall rules, the fail2ban need is reduced but not eliminated.
What auditd events should I forward to my SIEM for Linux server monitoring?
Priority audit keys to forward: (1) sudo_usage -- all privilege escalation; (2) identity -- passwd/shadow/sudoers file modifications; (3) modules -- kernel module loading (rootkit indicator); (4) privileged -- any exec by root-effective UID from a non-root real UID; (5) sshd_config -- SSH configuration changes. Set up alerts for: any key=modules event, any key=identity event outside a change window, and multiple key=sudo_usage events from the same user in a short window (possible session hijack or automated tool running as that user).
Sources & references
Free resources
Critical CVE Reference Card 2025–2026
25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.
Ransomware Incident Response Playbook
Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.
Get threat intel before your inbox does.
50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, weekly, no spam.
Unsubscribe anytime. We never sell your data.

Founder & Cybersecurity Evangelist, Decryption Digest
Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals weekly.
The Mythos Brief is free.
AI that finds 27-year-old zero-days. What it means for your security program.
