Linux Server Security Guide
A comprehensive guide to understanding and implementing Linux server hardening.
Table of Contents
- Security Mindset
- Defense in Depth
- Attack Surface Reduction
- SSH Security
- Firewall Strategy
- Intrusion Detection & Prevention
- Kernel Hardening
- User & Access Management
- Audit & Monitoring
- Automated Updates
- Filesystem Security
- Incident Response Basics
- Ongoing Maintenance
Security Mindset
Security is not a one-time task — it's an ongoing process. Every server is a potential target, regardless of size or perceived importance. Attackers use automated tools that scan the entire internet for vulnerable systems.
Key principles:
- Least privilege: Grant only the minimum access needed
- Defense in depth: Multiple layers of security, so failure of one layer doesn't compromise the system
- Assume breach: Design systems assuming an attacker will get in, then limit what they can do
- Monitor and respond: Detection is as important as prevention
Defense in Depth
Our scripts implement multiple security layers:
┌──────────────────────────────────────────────┐
│ Layer 7: Application Security │
│ (Your application code, WAF) │
├──────────────────────────────────────────────┤
│ Layer 6: Authentication & Authorization │
│ (SSH keys, sudo, PAM, fail2ban) │
├──────────────────────────────────────────────┤
│ Layer 5: Network Security │
│ (Firewall, rate limiting, IP filtering) │
├──────────────────────────────────────────────┤
│ Layer 4: OS Hardening │
│ (Kernel params, sysctl, module blacklist) │
├──────────────────────────────────────────────┤
│ Layer 3: Filesystem Security │
│ (Permissions, mount options, SUID cleanup) │
├──────────────────────────────────────────────┤
│ Layer 2: Monitoring & Auditing │
│ (auditd, logging, alerting) │
├──────────────────────────────────────────────┤
│ Layer 1: Patch Management │
│ (Automatic security updates) │
└──────────────────────────────────────────────┘
Attack Surface Reduction
Before hardening, reduce what attackers can target:
Remove unnecessary packages
# List installed packages
dpkg -l # Debian/Ubuntu
rpm -qa # RHEL/CentOS
# Remove examples
apt purge telnet ftp rsh-client # Debian/Ubuntu
yum remove telnet ftp rsh # RHEL/CentOS
Disable unnecessary services
# List all enabled services
systemctl list-unit-files --state=enabled
# Disable services you don't need
systemctl disable --now avahi-daemon
systemctl disable --now cups
Close unnecessary ports
# Check open ports
ss -tulnp # Shows all listening ports
nmap -sT localhost # Port scan yourself
SSH Security
SSH is the primary remote access method and the #1 attack target on internet-facing servers.
Why disable password authentication?
Passwords are susceptible to brute-force attacks. An internet-facing SSH server sees thousands of brute-force attempts daily. SSH keys are cryptographically stronger and cannot be brute-forced.
Key security settings explained
| Setting | Value | Why |
|---|---|---|
PermitRootLogin no |
Disables direct root SSH | Forces use of named accounts + sudo for audit trail |
PasswordAuthentication no |
Keys only | Eliminates brute-force attacks entirely |
MaxAuthTries 3 |
3 attempts | Limits credential guessing per connection |
ClientAliveInterval 300 |
5 min | Terminates idle sessions to reduce hijacking risk |
AllowAgentForwarding no |
Disabled | Prevents agent hijacking on compromised servers |
X11Forwarding no |
Disabled | Removes attack vector through X11 protocol |
Before disabling password auth
- Generate an SSH key pair on your workstation:
ssh-keygen -t ed25519 -C "your-email@example.com"
- Copy the public key to the server:
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
Test key-based login in a new terminal before applying hardening
Only then run
harden-ssh.sh
Firewall Strategy
A firewall should implement the principle of default deny — block everything, then explicitly allow only what's needed.
Default policy
Incoming: DENY ALL (then allow specific ports)
Outgoing: ALLOW ALL (restrict if possible)
Forward: DENY ALL (unless acting as router)
Typical server rules
| Port | Protocol | Service | Who needs access |
|---|---|---|---|
| 22 | TCP | SSH | Admin IPs only |
| 80 | TCP | HTTP | World (if web server) |
| 443 | TCP | HTTPS | World (if web server) |
Rate limiting
SSH rate limiting (via ufw limit) allows 6 connections per 30 seconds per IP. This stops brute-force while allowing normal use.
Intrusion Detection & Prevention
Fail2Ban: How it works
- Fail2Ban monitors log files for authentication failures
- After
maxretryfailures withinfindtime, the IP is banned - Ban lasts for
bantime, then the IP is released - Recidive jail catches repeat offenders with escalating bans
Progressive ban strategy
First offense: 1 hour ban (sshd jail)
DDoS detection: 48 hour ban (sshd-ddos jail)
Repeat offender: 1 week ban (recidive jail)
Monitoring Fail2Ban
# Overall status
fail2ban-client status
# SSH jail details
fail2ban-client status sshd
# Currently banned IPs
fail2ban-client get sshd banip
# Unban a specific IP
fail2ban-client set sshd unbanip 192.168.1.100
Kernel Hardening
Kernel parameters control fundamental OS behavior. Our sysctl-hardened.conf addresses:
Network stack hardening
-
SYN cookies (
tcp_syncookies=1): Protects against SYN flood DoS attacks -
Reverse path filtering (
rp_filter=1): Drops packets with impossible source addresses (IP spoofing) - ICMP redirects disabled: Prevents network route poisoning
- Source routing disabled: Prevents attackers from choosing packet routes
Memory protection
-
ASLR (
randomize_va_space=2): Randomizes memory layout, making exploits unreliable -
Core dump restriction (
suid_dumpable=0): Prevents SUID programs from creating memory dumps that might contain secrets
Information disclosure prevention
- kptr_restrict=2: Hides kernel memory addresses from non-root users
- dmesg_restrict=1: Restricts kernel log access to root
- perf_event_paranoid=3: Restricts performance monitoring to root
User & Access Management
Password policy rationale
| Policy | Value | Reasoning |
|---|---|---|
| Min length | 14 chars | NIST 800-63B recommends 8+; 14 provides margin |
| Max age | 365 days | Balance between rotation and user frustration |
| Min age | 7 days | Prevents rapid cycling to reuse old passwords |
| Complexity | Mixed | Uppercase, lowercase, digit, special character |
Sudo hardening explained
- requiretty: Sudo only works from a real terminal (not from cron or scripts, which could be hijacked)
- env_reset: Clears dangerous environment variables that could alter program behavior
- timestamp_timeout=15: Re-authentication required every 15 minutes
-
Logging: All sudo commands logged to
/var/log/sudo.log
The principle of least privilege
root account → Direct login disabled, use sudo
Admin accounts → sudo access with password
Service accounts → No login shell (/usr/sbin/nologin)
Application user → No sudo, limited file access
Audit & Monitoring
Why audit?
Auditing creates a forensic trail. When (not if) a security incident occurs, audit logs tell you:
- What happened (file modified, command executed)
- When it happened (timestamp)
- Who did it (user, process)
- How it happened (system call, tool used)
Key audit categories
| Key | What it monitors | Why |
|---|---|---|
time-change |
System clock modifications | Attackers change time to hide activity |
identity |
User/group file changes | Detect unauthorized account creation |
system-locale |
Network config changes | Detect DNS/routing manipulation |
logins |
Login/logout events | Track access patterns |
perm_mod |
Permission changes | Detect privilege escalation |
access |
Failed file access | Detect reconnaissance |
scope |
Sudoers changes | Detect privilege granting |
modules |
Kernel module operations | Detect rootkit installation |
Useful audit commands
# Search by key
ausearch -k identity # User/group changes
ausearch -k privilege_escalation # Sudo usage
# Reports
aureport --summary # Overall summary
aureport --auth # Authentication report
aureport --login --summary # Login summary
aureport --failed # Failed operations
# Recent events
ausearch -ts recent # Last 10 minutes
ausearch -ts today # Today's events
Automated Updates
Why auto-update security patches?
The #1 cause of successful attacks is unpatched vulnerabilities. The average time between vulnerability disclosure and exploitation is decreasing — automated updates close this window.
What gets auto-updated?
Only security updates are auto-applied. Feature updates and major version changes require manual review.
Risks and mitigation
| Risk | Mitigation |
|---|---|
| Update breaks something | Security-only updates are low-risk; test in staging |
| Reboot required | Schedule auto-reboot during maintenance window |
| Package conflicts | Blacklist problematic packages |
Filesystem Security
Mount options
| Option | Purpose | Where to apply |
|---|---|---|
nodev |
No device files | /tmp, /var/tmp, /dev/shm |
nosuid |
No SUID/SGID | /tmp, /var/tmp, /dev/shm |
noexec |
No execution | /tmp, /var/tmp, /dev/shm |
SUID/SGID explanation
SUID (Set User ID) binaries run with the file owner's privileges, not the caller's. A vulnerable SUID-root binary = instant root access for an attacker.
# Find all SUID binaries
find / -xdev -perm -4000 -type f 2>/dev/null
# Common legitimate SUID binaries
/usr/bin/sudo # Needed
/usr/bin/passwd # Needed
/usr/bin/su # Needed (but consider removing)
/usr/bin/chsh # Often unnecessary
/usr/bin/chfn # Often unnecessary
/usr/bin/newgrp # Often unnecessary
Review each SUID binary and remove the bit if not needed:
chmod u-s /usr/bin/chfn # Remove SUID from chfn
Incident Response Basics
If you suspect a compromise:
1. Don't panic, don't power off
Powering off destroys volatile evidence (memory, network connections).
2. Capture evidence
# Current connections
ss -tulnp > /tmp/connections.txt
# Running processes
ps auxf > /tmp/processes.txt
# Logged-in users
w > /tmp/users.txt
# Recent logins
last -50 > /tmp/logins.txt
# Recent file modifications
find / -mtime -1 -type f 2>/dev/null > /tmp/modified-files.txt
3. Check audit logs
# Failed logins
aureport --auth --failed
# Privilege escalation
ausearch -k privilege_escalation -ts today
# File modifications
ausearch -k perm_mod -ts today
4. Isolate
Disconnect from the network if active compromise is confirmed.
5. Investigate and remediate
Use logs and forensic evidence to determine the attack vector and scope.
Ongoing Maintenance
Hardening is not one-and-done. Schedule regular reviews:
Weekly
- [ ] Review failed SSH login attempts:
journalctl -u sshd | grep Failed - [ ] Check Fail2Ban bans:
fail2ban-client status sshd - [ ] Review sudo usage:
cat /var/log/sudo.log
Monthly
- [ ] Run filesystem audit:
bash filesystem-hardening.sh --audit-only - [ ] Check for new SUID binaries
- [ ] Review user accounts:
awk -F: '$3 >= 1000' /etc/passwd - [ ] Verify auto-updates are working
Quarterly
- [ ] Re-run the CIS benchmark checklist
- [ ] Review and rotate SSH keys
- [ ] Test backup restoration
- [ ] Review firewall rules
Annually
- [ ] Full security assessment
- [ ] Review and update password policies
- [ ] Audit service accounts
- [ ] Update hardening scripts to latest version
Additional Resources
- CIS Benchmarks — Free PDF downloads
- NIST 800-123 — Guide to General Server Security
- NIST 800-63B — Digital Identity Authentication Guidelines
- Mozilla SSH Guidelines — Modern SSH configuration
Guide by Datanest Digital | support@datanest.dev
This is 1 of 6 resources in the DevOps Toolkit Pro toolkit. Get the complete [Linux Hardening Scripts] with all files, templates, and documentation for $XX.
Or grab the entire DevOps Toolkit Pro bundle (6 products) for $178 — save 30%.
Top comments (0)