Linux Server Security Hardening
Complete guide to hardening Linux servers including SSH security, firewall configuration, kernel hardening, audit logging, and compliance with CIS Linux benchmarks.
# Linux Server Security Hardening
Introduction
Linux powers the majority of internet-facing servers, cloud infrastructure, and enterprise workloads. This makes Linux servers a primary target for attackers. Security hardening reduces the attack surface by removing unnecessary services, configuring strict access controls, and implementing continuous monitoring.
This tutorial provides a comprehensive guide to hardening Linux servers against common attack vectors. The procedures apply to Ubuntu 24.04 LTS, RHEL 9, and Debian 12, with distribution-specific notes where applicable.
Learning Objectives
By the end of this tutorial, you will be able to:
- Harden SSH configuration against brute-force and credential attacks
- Configure netfilter/iptables or nftables firewalls with default-deny policies
- Apply kernel hardening via sysctl parameters
- Implement mandatory access control with SELinux or AppArmor
- Configure auditd for comprehensive system auditing
- Apply CIS Linux Benchmark recommendations
- Automate security updates and patch management
Technical Background
Linux Security Architecture
Linux security operates at multiple layers:
1. **Discretionary Access Control (DAC):** Traditional Unix file permissions (owner/group/other) 2. **Mandatory Access Control (MAC):** SELinux, AppArmor, Smack enforce policy-based access 3. **Linux Security Modules (LSM):** Kernel framework supporting multiple MAC modules 4. **Namespaces and Cgroups:** Container isolation and resource control 5. **Capabilities:** Fine-grained privilege separation beyond root/non-root
Common Linux Attack Vectors
| Vector | Description | Mitigation | |--------|-------------|------------| | SSH brute-force | Automated password guessing against SSH | Key-based auth, fail2ban, port change | | Outdated software | Known vulnerabilities in unpatched packages | Automated security updates | | Misconfigured services | Services listening on all interfaces | Bind to localhost, firewall rules | | Privilege escalation | Kernel exploits, sudo misconfiguration | Kernel hardening, restricted sudo | | Weak file permissions | World-readable sensitive files | umask 077, strict file ownership |
Prerequisites
- Root or sudo access to a Linux server
- Familiarity with Linux command line and text editors (vim/nano)
- Understanding of Linux file permissions and ownership
- Basic networking knowledge (iptables/nftables, TCP/UDP ports)
Hardware and Software Requirements
| Component | Requirement | |-----------|-------------| | OS | Ubuntu 24.04 LTS, RHEL 9, AlmaLinux 9, or Debian 12 | | SSH | OpenSSH server | | Firewall | UFW (Ubuntu), firewalld (RHEL), or nftables | | MAC | AppArmor (Ubuntu/Debian) or SELinux (RHEL/AlmaLinux) | | Auditing | auditd | | Intrusion Prevention | fail2ban | | Vulnerability Scanning | Lynis, OpenSCAP |
Step-by-Step Implementation
Phase 1: SSH Hardening
#### 1.1 Disable Password Authentication
Edit `/etc/ssh/sshd_config`:
# Disable password authentication
PasswordAuthentication no
PermitEmptyPasswords no
PermitRootLogin no
# Use only protocol 2 (default in modern OpenSSH)
Protocol 2
# Limit authentication attempts
MaxAuthTries 3
MaxSessions 2
# Disable X11 forwarding
X11Forwarding no
AllowTcpForwarding no
# Set login grace time
LoginGraceTime 60
# Use key-based authentication only
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
# Restrict allowed users
AllowUsers admin deploy
# Disable agent forwarding
AllowAgentForwarding noApply changes:
sudo sshd -t && sudo systemctl reload ssh
# or on RHEL-based systems:
sudo systemctl reload sshd#### 1.2 Configure SSH Key-Based Authentication
Generate key pair on client:
ssh-keygen -t ed25519 -a 100 -f ~/.ssh/id_ed25519_serverCopy public key to server:
ssh-copy-id -i ~/.ssh/id_ed25519_server.pub admin@server-ipVerify key-based login works, then disable password authentication.
#### 1.3 Install and Configure fail2ban
# Ubuntu/Debian
sudo apt update && sudo apt install -y fail2ban
# RHEL/AlmaLinux
sudo dnf install -y fail2banCreate `/etc/fail2ban/jail.local`:
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600
[sshd-ddos]
enabled = true
port = ssh
filter = sshddos
logpath = /var/log/auth.log
maxretry = 5
bantime = 86400
findtime = 600sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshdPhase 2: Firewall Configuration
#### 2.1 UFW (Ubuntu/Debian)
# Reset to defaults
sudo ufw reset
# Default deny incoming, allow outgoing
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH (from specific IP ranges if possible)
sudo ufw allow from 10.0.0.0/8 to any port 22 proto tcp
# or for specific IP:
# sudo ufw allow from 203.0.113.10 to any port 22 proto tcp
# Allow HTTPS
sudo ufw allow 443/tcp
# Allow HTTP (if needed)
sudo ufw allow 80/tcp
# Enable firewall
sudo ufw enable
# Verify
sudo ufw status verbose#### 2.2 firewalld (RHEL/AlmaLinux)
# Set default zone to drop
sudo firewall-cmd --set-default-zone=drop
# Add SSH service (restrict to specific source if possible)
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-service=http
# Reload firewall
sudo firewall-cmd --reload
# Verify
sudo firewall-cmd --list-all-zones#### 2.3 nftables (Advanced)
Create `/etc/nftables.conf`:
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
iifname "lo" accept
ct state established,related accept
ct state invalid drop
ip protocol icmp accept
tcp dport { 22, 80, 443 } ct state new accept
ip saddr 10.0.0.0/8 tcp dport 22 accept
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}sudo nft -f /etc/nftables.conf
sudo systemctl enable --now nftablesPhase 3: Kernel Hardening
#### 3.1 sysctl Configuration
Create `/etc/sysctl.d/99-security.conf`:
# IP spoofing protection
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Ignore ICMP broadcasts
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Ignore ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
# Log suspicious packets
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
# TCP SYN cookie protection
net.ipv4.tcp_syncookies = 1
# Disable IPv6 if not used
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
# Protect kernel pointers
kernel.kptr_restrict = 2
# Restrict dmesg
kernel.dmesg_restrict = 1
# Enable ASLR
kernel.randomize_va_space = 2
# Protect against symlink attacks
fs.protected_symlinks = 1
fs.protected_hardlinks = 1
# Increase ephemeral port range
net.ipv4.ip_local_port_range = 32768 65535Apply changes:
sudo sysctl --systemVerify key settings:
sysctl net.ipv4.conf.all.rp_filter
sysctl net.ipv4.tcp_syncookiesPhase 4: Mandatory Access Control
#### 4.1 AppArmor (Ubuntu/Debian)
AppArmor is enabled by default on Ubuntu. Verify status:
sudo aa-status
sudo apparmor_statusEnforce profiles for critical services:
# Set nginx to enforce mode
sudo aa-enforce /etc/apparmor.d/usr.sbin.nginx
# Set all profiles to enforce
sudo aa-enforce /etc/apparmor.d/*Create custom profile for a service:
# Generate profile from logs
sudo aa-genprof /usr/sbin/custom-service#### 4.2 SELinux (RHEL/AlmaLinux)
Verify SELinux status:
sestatus
getenforceSet to enforcing mode:
sudo setenforce 1Make persistent in `/etc/selinux/config`:
SELINUX=enforcing
SELINUXTYPE=targetedRestore default file contexts:
sudo restorecon -Rv /etc/sshPhase 5: Audit Logging
#### 5.1 Install and Configure auditd
# Ubuntu/Debian
sudo apt install -y auditd audispd-plugins
# RHEL/AlmaLinux
sudo dnf install -y auditConfigure `/etc/audit/rules.d/custom.rules`:
# Track all changes to /etc
-w /etc/ -p wa -k etc_changes
# Track SSH configuration
-w /etc/ssh/sshd_config -p wa -k sshd_config
# Track sudo usage
-w /var/log/sudo.log -p wa -k sudo_actions
# Track user account changes
-w /etc/passwd -p wa -k account_changes
-w /etc/group -p wa -k account_changes
-w /etc/shadow -p wa -k shadow_changes
# Track system calls
-a always,exit -F arch=b64 -S adjtimex -S settimeofday -k time_change
-a always,exit -F arch=b64 -S mount -k mountsLoad rules:
sudo augenrules --load
sudo systemctl restart auditdVerify rules loaded:
sudo auditctl -lView audit logs:
sudo ausearch -k etc_changes
sudo aureport --file-reportPhase 6: Automatic Security Updates
#### 6.1 Ubuntu/Debian - unattended-upgrades
sudo apt install -y unattended-upgrades apt-listchangesConfigure `/etc/apt/apt.conf.d/50unattended-upgrades`:
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
"${distro_id}:${distro_codename}-updates";
};
Unattended-Upgrade::Automatic-Reboot "false";
Unattended-Upgrade::Mail "admin@ityantra.co.in";Enable:
sudo dpkg-reconfigure -plow unattended-upgrades#### 6.2 RHEL/AlmaLinux - dnf-automatic
sudo dnf install -y dnf-automaticEdit `/etc/dnf/automatic.conf`:
[commands]
upgrade_type = security
apply_updates = yes
[emitters]
emit_via = stdio
[emitters.email]
email_from = root@ityantra.co.in
email_to = admin@ityantra.co.in
email_host = localhostsudo systemctl enable --now dnf-automatic.timerVerification and Expected Outputs
| Check | Method | Expected Result | |-------|--------|-----------------| | SSH password auth disabled | `ssh -o PasswordAuthentication=yes user@host` | Permission denied | | Root login disabled | `ssh root@host` | Permission denied | | Firewall active | `sudo ufw status` or `firewall-cmd --state` | Status: active/running | | Default deny | Attempt connection to blocked port | Connection refused | | SELinux/AppArmor enforcing | `getenforce` or `aa-status` | Enforcing/loaded | | Audit rules loaded | `sudo auditctl -l` | Rules listed | | Auto-updates enabled | Check service status | Service active |
Troubleshooting Scenarios
| Issue | Diagnosis | Resolution | |-------|-----------|------------| | Locked out after SSH changes | Misconfigured sshd_config | Maintain existing session, test with `sshd -t` before reload | | fail2ban not blocking | Wrong log path | Verify logpath matches distribution (`/var/log/auth.log` vs `/var/log/secure`) | | Firewall blocking legitimate traffic | Overly restrictive rules | Add specific allow rules before reload | | SELinux blocking service | SELinux in enforcing mode | Check `ausearch -m avc` and set proper file contexts | | auditd not logging | Rules not loaded | Run `augenrules --load` and restart auditd | | Package updates breaking service | Automated updates applied | Pin critical packages, test updates in staging first |
Security Best Practices
- **Maintain an out-of-band access method** (console access, IPMI, cloud serial console) before applying SSH or firewall changes
- **Test changes in a staging environment** before applying to production
- **Keep a rollback plan** for every security change
- **Use configuration management** (Ansible, Puppet, Chef) for consistent hardening across all servers
- **Document all deviations** from the hardening standard with approved exceptions
- **Run compliance scans monthly** using Lynis or OpenSCAP
- **Monitor audit logs** daily and forward to SIEM
Enterprise Considerations
- **Baseline configuration:** Maintain a hardened system image as the baseline for all deployments
- **Configuration drift:** Use tools like Ansible or Chef to detect and remediate drift
- **Change management:** All hardening changes must go through change advisory board (CAB)
- **Separation of environments:** Apply stricter controls in production than development
- **Asset inventory:** Maintain inventory of all Linux servers with OS version, patch level, and owner
Conclusion
Linux server security hardening is a foundational practice that significantly reduces the attack surface. By combining SSH hardening, firewall rules, kernel parameters, mandatory access control, audit logging, and automated updates, you create a defensible server environment. Hardening must be maintained continuously—servers drift from their secure baseline over time and require regular compliance validation.
Frequently Asked Questions
**Q: Should I change the default SSH port?** A: Changing SSH port reduces noise from automated scanners but does not provide real security. Focus on key-based authentication, fail2ban, and firewall restrictions instead.
**Q: SELinux or AppArmor?** A: Use whichever is native to your distribution. SELinux (RHEL family) offers finer-grained control but has a steeper learning curve. AppArmor (Ubuntu/Debian) is easier to configure and maintain.
**Q: How often should I run vulnerability scans?** A: Run authenticated vulnerability scans at least monthly, and after any significant change to the server configuration or installed software.
Official Technical References
- CIS Linux Benchmark: https://www.cisecurity.org/benchmark/linux/
- NSA Linux Hardening: https://www.nsa.gov/what-we-do/cybersecurity/defensive-cyber-operations/
- OpenSCAP: https://www.open-scap.org/
- Lynis: https://cisofy.com/lynis/
- Linux Security Modules: https://kernel.org/doc/html/latest/admin-guide/LSM/index.html
Related ITYantra Tutorials
- Enterprise Cybersecurity Fundamentals (cybersecurity)
- SSH Security and Access Control (cybersecurity)
- Firewall Architecture and Network Security (cybersecurity)
- Linux Network Configuration and Troubleshooting (linux)
- Linux Systemd Services and Boot Process (linux)