Every 39 seconds, a cyberattack occurs. In 2025, the average cost of a data breach reached $4.88 million (IBM). Linux servers power 96% of the world's top web servers, making them the primary target for attackers worldwide.
The good news? A properly hardened Linux server is one of the most secure computing platforms available. The bad news? Most Linux servers are deployed with default configurations that leave them vulnerable.
Reality Check: If your server still allows root SSH login, runs with SELinux disabled, has no firewall rules beyond defaults, and hasn't been audited in months — this article is for you. We'll walk through every layer of Linux security, from kernel hardening to intrusion detection, with copy-paste commands for RHEL, Ubuntu, and Debian.
The 7 Layers of Linux Server Security
| Layer | Area | Key Components | Priority |
|---|---|---|---|
| 1 | Physical & Boot | BIOS/UEFI password, GRUB password, boot partition encryption | Critical |
| 2 | Network & Firewall | firewalld, nftables, TCP wrappers, port management | Critical |
| 3 | Authentication | SSH keys, MFA, PAM, password policies, sudo configuration | Critical |
| 4 | Access Control | SELinux/AppArmor, file permissions, ACLs, capabilities | High |
| 5 | Kernel & System | sysctl hardening, kernel modules, ASLR, core dumps | High |
| 6 | Monitoring & Audit | auditd, rsyslog, fail2ban, AIDE, log management | High |
| 7 | Maintenance | Patching, vulnerability scanning, backup verification, incident response | Ongoing |
Layer 1: SSH Hardening — The First Line of Defense
SSH is the primary attack surface for most Linux servers. Bots constantly scan the internet for SSH servers with weak configurations. Here's how to lock it down properly:
Essential SSH Configuration
# /etc/ssh/sshd_config — Production-grade SSH hardening
# === Authentication ===
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
MaxAuthTries 3
MaxSessions 3
LoginGraceTime 30
# === Protocol & Encryption ===
Protocol 2
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group16-sha512
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512
# === Access Control ===
AllowUsers deployer admin
AllowGroups sshusers
DenyUsers root
PermitEmptyPasswords no
# === Session Security ===
ClientAliveInterval 300
ClientAliveCountMax 2
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
# === Logging ===
LogLevel VERBOSE
SyslogFacility AUTH
# === Port (optional: change from default) ===
Port 2222
SSH Key Setup (Ed25519)
# Generate modern Ed25519 key (strongest, smallest)
ssh-keygen -t ed25519 -C "admin@company.com" -f ~/.ssh/id_ed25519
# Copy to server
ssh-copy-id -i ~/.ssh/id_ed25519.pub -p 2222 deployer@your-server.com
# Set correct permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
chmod 600 ~/.ssh/authorized_keys
# Restart SSH service
sudo systemctl restart sshd
# Verify key-only authentication works BEFORE disconnecting!
ssh -p 2222 deployer@your-server.com
Warning: ALWAYS verify you can login with your SSH key in a separate terminal session BEFORE disabling password authentication. Locking yourself out of a remote server is one of the most common and costly mistakes in system administration.
Layer 2: Firewall Configuration
firewalld (RHEL/CentOS/Fedora/AlmaLinux)
# Check status
sudo systemctl enable --now firewalld
sudo firewall-cmd --state
# Set default zone to drop (deny all incoming)
sudo firewall-cmd --set-default-zone=drop
# Allow only essential services
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
# If SSH is on custom port
sudo firewall-cmd --permanent --add-port=2222/tcp
# Remove unnecessary services
sudo firewall-cmd --permanent --remove-service=cockpit
sudo firewall-cmd --permanent --remove-service=dhcpv6-client
# Rate limit SSH connections (max 10 per minute)
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" service name="ssh" accept limit value="10/m"'
# Block specific countries (optional, requires ipset)
sudo firewall-cmd --permanent --new-ipset=blocked_countries --type=hash:net
# Add IP ranges...
# Apply changes
sudo firewall-cmd --reload
# Verify
sudo firewall-cmd --list-all
nftables (Modern Replacement for iptables)
#!/usr/sbin/nft -f
# /etc/nftables.conf — Production web server ruleset
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
# Accept established connections
ct state established,related accept
# Accept loopback
iif lo accept
# Drop invalid
ct state invalid drop
# ICMP (allow ping, limit rate)
ip protocol icmp icmp type echo-request limit rate 5/second accept
# SSH (rate limited)
tcp dport 2222 ct state new limit rate 10/minute accept
# HTTP/HTTPS
tcp dport { 80, 443 } accept
# Log dropped packets
log prefix "[nftables-drop] " flags all counter drop
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
Firewall Comparison
| Feature | firewalld | nftables | iptables (legacy) | ufw |
|---|---|---|---|---|
| Best For | RHEL/CentOS | Advanced users | Legacy systems | Ubuntu/Debian |
| Zones/Profiles | Yes | Tables | No | Profiles |
| Runtime Changes | Yes (no restart) | Atomic updates | Manual reload | Reload needed |
| Rich Rules | Yes | Native | Complex syntax | Limited |
| RHCSA Exam | Required | Not tested | Not tested | Not tested |
| Learning Curve | Moderate | Steep | Steep | Easy |
Layer 3: SELinux — Mandatory Access Control
SELinux is the single most important security feature in RHEL-based distributions — and the most frequently disabled one. Disabling SELinux is the #1 security mistake Linux administrators make.
# Check SELinux status
getenforce # Current mode
sestatus # Detailed status
# Set to enforcing (permanent)
sudo sed -i 's/SELINUX=permissive/SELINUX=enforcing/' /etc/selinux/config
sudo sed -i 's/SELINUX=disabled/SELINUX=enforcing/' /etc/selinux/config
# Common: Allow Apache to serve from non-standard directory
sudo semanage fcontext -a -t httpd_sys_content_t "/data/www(/.*)?"
sudo restorecon -Rv /data/www
# Common: Allow Apache to connect to database
sudo setsebool -P httpd_can_network_connect_db on
# Common: Allow nginx to connect to upstream
sudo setsebool -P httpd_can_network_connect on
# Troubleshoot AVC denials
sudo ausearch -m AVC -ts recent
sudo audit2why < /var/log/audit/audit.log
sudo sealert -a /var/log/audit/audit.log # Detailed analysis
# Create custom policy from denials
sudo ausearch -m AVC -ts recent | audit2allow -M mypolicy
sudo semodule -i mypolicy.pp
SELinux Quick Reference
| Task | Command |
|---|---|
| Check mode | getenforce |
| Temporary permissive | sudo setenforce 0 (reverts on reboot) |
| View file context | ls -Z /var/www/html/ |
| Change file context | sudo chcon -t httpd_sys_content_t file.html |
| Restore default context | sudo restorecon -Rv /path |
| List booleans | getsebool -a | grep httpd |
| Set boolean permanently | sudo setsebool -P httpd_can_sendmail on |
| View denials | sudo ausearch -m AVC -ts today |
| Port labeling | sudo semanage port -a -t http_port_t -p tcp 8080 |
Layer 4: Kernel Hardening with sysctl
# /etc/sysctl.d/99-security.conf — Kernel security parameters
# === Network Security ===
# Disable IP forwarding (unless router/VPN)
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0
# Prevent source routing attacks
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
# Disable ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
# Enable SYN flood protection
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
# Ignore ICMP broadcasts (smurf attack prevention)
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Log suspicious packets
net.ipv4.conf.all.log_martians = 1
# Enable reverse path filtering (anti-spoofing)
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# === Memory Security ===
# Restrict dmesg access
kernel.dmesg_restrict = 1
# Restrict kernel pointers
kernel.kptr_restrict = 2
# Enable ASLR (Address Space Layout Randomization)
kernel.randomize_va_space = 2
# Disable core dumps for SUID programs
fs.suid_dumpable = 0
# === Process Security ===
# Restrict ptrace scope (prevent process snooping)
kernel.yama.ptrace_scope = 1
# Disable SysRq key (prevent kernel-level commands from keyboard)
kernel.sysrq = 0
# Apply without reboot
sudo sysctl --system
# Verify specific setting
sysctl net.ipv4.ip_forward
sysctl kernel.randomize_va_space
Layer 5: User & Authentication Security
Password Policy Configuration
# /etc/login.defs — Global password settings
PASS_MAX_DAYS 90 # Force password change every 90 days
PASS_MIN_DAYS 7 # Minimum 7 days between changes
PASS_MIN_LEN 14 # Minimum 14 characters
PASS_WARN_AGE 14 # Warn 14 days before expiry
# /etc/security/pwquality.conf — Password complexity
minlen = 14
dcredit = -1 # At least 1 digit
ucredit = -1 # At least 1 uppercase
lcredit = -1 # At least 1 lowercase
ocredit = -1 # At least 1 special character
maxrepeat = 3 # Max 3 consecutive identical characters
maxclassrepeat = 4 # Max 4 consecutive chars from same class
reject_username # Cannot contain username
enforce_for_root # Apply even to root
Sudo Hardening
# Use visudo for safe editing
sudo visudo
# Best practices
Defaults timestamp_timeout=5 # Re-auth after 5 min idle
Defaults passwd_tries=3 # Max 3 password attempts
Defaults logfile="/var/log/sudo.log" # Log all sudo commands
Defaults log_input,log_output # Log stdin/stdout
Defaults requiretty # Require terminal
Defaults use_pty # Use pseudo-terminal
# Principle of least privilege — specific commands only
deployer ALL=(ALL) /usr/bin/systemctl restart nginx, \
/usr/bin/systemctl restart php-fpm, \
/usr/bin/journalctl -u nginx, \
/usr/bin/journalctl -u php-fpm
Account Auditing
# Find accounts with empty passwords (CRITICAL)
sudo awk -F: '($2 == "") {print $1}' /etc/shadow
# Find accounts with UID 0 (root-equivalent)
awk -F: '($3 == "0") {print}' /etc/passwd
# List users with login shells
grep -v 'nologin\|false' /etc/passwd | cut -d: -f1
# Lock unused accounts
sudo usermod -L unused_account
sudo usermod -s /sbin/nologin unused_account
# Set account expiration
sudo chage -E 2026-12-31 contractor_user
# Check password aging for all users
sudo chage -l username
Layer 6: Intrusion Detection & Monitoring
fail2ban — Automated Attack Prevention
# Install
sudo dnf install fail2ban # RHEL/CentOS
sudo apt install fail2ban # Debian/Ubuntu
# /etc/fail2ban/jail.local
[DEFAULT]
bantime = 3600 # 1 hour ban
findtime = 600 # Within 10 minutes
maxretry = 3 # After 3 failures
action = %(action_mwl)s # Ban + send email with whois + logs
[sshd]
enabled = true
port = 2222
logpath = /var/log/auth.log
maxretry = 3
bantime = 86400 # 24 hours for SSH
[nginx-http-auth]
enabled = true
logpath = /var/log/nginx/error.log
maxretry = 5
[nginx-botsearch]
enabled = true
logpath = /var/log/nginx/access.log
maxretry = 2
# Enable and start
sudo systemctl enable --now fail2ban
# Check status
sudo fail2ban-client status
sudo fail2ban-client status sshd
# Unban an IP
sudo fail2ban-client set sshd unbanip 192.168.1.100
auditd — Linux Audit Framework
# /etc/audit/rules.d/security.rules
# Monitor password file changes
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/gshadow -p wa -k identity
# Monitor SSH configuration changes
-w /etc/ssh/sshd_config -p wa -k sshd_config
# Monitor sudo configuration
-w /etc/sudoers -p wa -k sudoers
-w /etc/sudoers.d/ -p wa -k sudoers
# Monitor cron changes
-w /etc/crontab -p wa -k cron
-w /etc/cron.d/ -p wa -k cron
# Monitor login/logout
-w /var/log/lastlog -p wa -k logins
-w /var/run/faillock/ -p wa -k logins
# Monitor kernel module loading
-w /sbin/insmod -p x -k modules
-w /sbin/modprobe -p x -k modules
-w /sbin/rmmod -p x -k modules
# Monitor time changes
-a always,exit -F arch=b64 -S adjtimex -S settimeofday -k time-change
# Search audit logs
sudo ausearch -k identity -ts today
sudo aureport --auth --summary
AIDE — File Integrity Monitoring
# Install AIDE
sudo dnf install aide # RHEL
sudo apt install aide aide-common # Debian/Ubuntu
# Initialize database
sudo aide --init
sudo mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
# Check for changes
sudo aide --check
# Update database after legitimate changes
sudo aide --update
# Automate daily checks via cron
echo "0 5 * * * root /usr/sbin/aide --check | mail -s 'AIDE Report' admin@company.com" | sudo tee /etc/cron.d/aide-check
Layer 7: Automated Security Maintenance
Automatic Security Updates
# RHEL/CentOS — dnf-automatic
sudo dnf install dnf-automatic
sudo systemctl enable --now dnf-automatic-install.timer
# /etc/dnf/automatic.conf
[commands]
apply_updates = yes
upgrade_type = security # Only security updates
[emitters]
emit_via = email
email_from = server@company.com
email_to = admin@company.com
# Ubuntu/Debian — unattended-upgrades
sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
Rootkit Detection
# Install rootkit scanners
sudo dnf install rkhunter chkrootkit # RHEL
sudo apt install rkhunter chkrootkit # Debian/Ubuntu
# Run rkhunter
sudo rkhunter --update
sudo rkhunter --check --skip-keypress
# Run chkrootkit
sudo chkrootkit
# Schedule weekly scan
echo "0 3 * * 0 root /usr/bin/rkhunter --check --skip-keypress --report-warnings-only | mail -s 'rkhunter Weekly' admin@company.com" | sudo tee /etc/cron.d/rkhunter-weekly
Post-Install Hardening Checklist
Use this checklist after every new server deployment:
| ☐ | Task | Command / Action | Priority |
|---|---|---|---|
| ☐ | Update all packages | sudo dnf update -y | P0 |
| ☐ | Create admin user | useradd -m -G wheel deployer | P0 |
| ☐ | Setup SSH keys | ssh-copy-id deployer@server | P0 |
| ☐ | Harden SSH config | Disable root login, passwords | P0 |
| ☐ | Configure firewall | Drop default, allow SSH/HTTP/HTTPS | P0 |
| ☐ | Enable SELinux | setenforce 1 + config | P0 |
| ☐ | Apply sysctl hardening | Network + kernel params | P1 |
| ☐ | Install fail2ban | Configure SSH + web jails | P1 |
| ☐ | Configure auditd | Monitor critical files & logins | P1 |
| ☐ | Setup auto updates | dnf-automatic or unattended-upgrades | P1 |
| ☐ | Remove unnecessary packages | dnf remove telnet rsh | P2 |
| ☐ | Disable unused services | systemctl disable bluetooth cups | P2 |
| ☐ | Setup AIDE | File integrity monitoring | P2 |
| ☐ | Install rootkit scanner | rkhunter + weekly cron | P2 |
| ☐ | Set GRUB password | grub2-setpassword | P2 |
| ☐ | Configure log rotation | /etc/logrotate.d/ | P2 |
| ☐ | Backup strategy | Automated + verified + off-site | P1 |
| ☐ | Document everything | Config changes, access, contacts | P1 |
CIS Benchmarks: Industry Standard Hardening
The Center for Internet Security (CIS) Benchmarks are the gold standard for Linux server hardening. They provide detailed, scored configuration recommendations:
| CIS Level | Description | Best For |
|---|---|---|
| Level 1 | Essential security settings with minimal impact on functionality | All production servers |
| Level 2 | Defense-in-depth settings that may reduce functionality | High-security environments |
| STIG | DoD Security Technical Implementation Guides | Government, military, defense |
# Automated CIS compliance scanning with OpenSCAP
sudo dnf install openscap-scanner scap-security-guide
# List available profiles
oscap info /usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml
# Run CIS Level 1 scan
sudo oscap xccdf eval \
--profile xccdf_org.ssgproject.content_profile_cis \
--results /tmp/cis-results.xml \
--report /tmp/cis-report.html \
/usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml
# View HTML report
firefox /tmp/cis-report.html
Common Linux Security Mistakes
| # | Mistake | Why It's Dangerous | Fix |
|---|---|---|---|
| 1 | Disabling SELinux | Removes mandatory access control entirely | Learn to fix AVC denials instead |
| 2 | Root SSH login allowed | Direct target for brute-force attacks | PermitRootLogin no |
| 3 | Password auth for SSH | Passwords are brute-forceable | Use SSH keys exclusively |
| 4 | chmod 777 | World-writable = anyone can modify | Use 755 dirs, 644 files, ACLs |
| 5 | No firewall active | All ports exposed to internet | Drop default + whitelist services |
| 6 | Running as root | Any exploit gets full system access | Dedicated service users + sudo |
| 7 | No log monitoring | Attacks go unnoticed for months | auditd + centralized logging |
| 8 | Delayed patching | Known CVEs exploited within hours | Auto security updates enabled |
| 9 | No backup verification | Backup may be corrupted or incomplete | Monthly restore test |
| 10 | Secrets in code/env | Leaked via git, logs, or env dumps | Vault, encrypted configs, rotate keys |
Security Salary Impact
Security-skilled Linux professionals earn significantly more than their peers:
| Role | Without Security Skills | With Security Skills | Premium |
|---|---|---|---|
| Linux Sysadmin | $65,000 - $85,000 | $85,000 - $120,000 | +30-40% |
| DevOps Engineer | $90,000 - $130,000 | $120,000 - $170,000 | +25-35% |
| Security Engineer | — | $110,000 - $180,000 | — |
| SOC Analyst | — | $70,000 - $120,000 | — |
| EU Equivalent | €45,000 - €65,000 | €65,000 - €110,000 | +35-50% |
Recommended Books for Linux Security:
- Linux Security Hardening — €14.90
- Linux System Hardening — €13.90
- Linux Security Essentials — €9.90
- Linux Security Auditing — €14.90
- SELinux & AppArmor Guide — €16.90
- Firewall Configuration: The Complete Guide — €14.90
- Linux Firewall Configuration — €12.90
- SSH Mastery: Secure Remote Administration — €16.90
- OpenSSH Configuration & Tunneling Guide — €18.90
- Ethical Hacking & Penetration Testing — €22.90
- Cybersecurity Fundamentals — €24.90
- SOC Analyst Fundamentals — €11.90
- SOC Analyst Advanced: Incident Response & Forensics — €15.90
- Network Security Basics — €19.90
Further Reading on Dargslan
- RHCSA EX200 Exam Guide 2026
- SOC Analyst Career Guide 2026
- RHCSA vs LFCS vs LPIC: Linux Certification Comparison
- Ubuntu 24.04 LTS Server Administration
- Docker vs Kubernetes: What's the Difference?
- IT Certification Roadmap 2026
- Grok AI Complete Guide 2026
Final Thoughts
Linux security is not a one-time task — it's a continuous process. The hardening measures in this guide will dramatically reduce your attack surface, but security is never "done." New vulnerabilities are discovered daily, and attackers constantly evolve their techniques.
The three pillars of Linux server security are:
1. Prevention — Hardening, firewalls, SELinux, SSH keys, minimal packages
2. Detection — auditd, fail2ban, AIDE, rootkit scanners, log analysis
3. Response — Incident response plan, backups, documentation, communication
Start with the P0 items in the checklist above, then work through P1 and P2 over the following weeks. A server hardened with even the basics from this guide is already more secure than 90% of servers on the internet.
Master Linux Security
From fundamentals to advanced hardening — protect your infrastructure:
Get Linux Security Hardening →