Skip to content

Linux Server Hardening — CIS Benchmarks & Security Best Practices

DodaTech Updated 2026-06-24 8 min read

In this tutorial, you'll learn about Linux Server Hardening. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

CIS Benchmarks provide globally recognized, consensus-based guidelines for securing Linux servers, covering kernel parameters, user access, network configuration, logging, and file integrity monitoring.

What You'll Learn

How to apply CIS Benchmark controls to Linux systems: configure kernel hardening via sysctl, audit services and disable unnecessary ones, enforce password policies through PAM, set up file integrity monitoring with AIDE, blacklist unused kernel modules, and automate Compliance checks with OpenSCAP.

Why CIS Benchmarks Matter

Security hardening guides are only useful if they follow a recognized standard. CIS Benchmarks are the most widely adopted — used by banks, governments, and Compliance frameworks (PCI DSS, HIPAA, SOC 2). Following CIS controls reduces attack surface systematically rather than ad-hoc. Durga Antivirus Pro's deployment pipeline validates every build against CIS Level 2 benchmarks before promotion to production.

Learning Path

flowchart LR
  A[journalctl Guide] --> B[Server Hardening CIS
You are here] B --> C[LXC Containers] B --> D[Security Hardening] style B fill:#f90,color:#fff

Kernel Hardening (CIS Control 1)

# Hardened sysctl configuration
sudo tee /etc/sysctl.d/99-cis-hardening.conf << 'EOF'
# 1.1.1 Disable unused filesystems
fs.squashfs = 0
fs.vfat = 0
fs.cramfs = 0

# 1.2.1 Restrict core dumps
fs.suid_dumpable = 0

# 1.3.1 IP forwarding (disable unless needed)
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0

# 1.3.2 Source routed packets
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

# 1.3.3 ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# 1.3.4 Ignore broadcast pings
net.ipv4.icmp_echo_ignore_broadcasts = 1

# 1.3.5 TCP SYN cookies
net.ipv4.tcp_syncookies = 1

# 1.3.6 Log martian packets
net.ipv4.conf.all.log_martians = 1

# 1.4.1 Reverse path filtering
net.ipv4.conf.all.rp_filter = 1

# 1.5.1 Address space layout randomization
kernel.randomize_va_space = 2

# 1.5.2 Restrict perf events
kernel.perf_event_paranoid = 3

# 1.5.3 Restrict kptr
kernel.kptr_restrict = 2

# 1.6.1 Disable sysrq
kernel.sysrq = 0
EOF

sudo sysctl -p /etc/sysctl.d/99-cis-hardening.conf

Service Auditing (CIS Control 2)

# List all listening services
sudo ss -tulpn

# Disable unnecessary services
sudo systemctl disable --now avahi-daemon
sudo systemctl disable --now cups
sudo systemctl disable --now rpcbind
sudo systemctl disable --now bluetooth
sudo systemctl disable --now isc-dhcp-server
sudo systemctl disable --now slapd

# Ensure only essential services remain:
# sshd, systemd-journald, rsyslog (or journald), cron, ntp/chrony, auditd

Service Inventory Checklist

# Create a service inventory
for unit in $(systemctl list-units --type=service --state=running --no-legend | awk '{print $1}'); do
    desc=$(systemctl show "$unit" -p Description --value)
    echo "$unit | $desc"
done | sort

Filesystem Partitioning (CIS Control 3)

# Ensure separate partitions for critical directories
# /tmp — nosuid, noexec, nodev
# /var — separate partition
# /var/tmp — nosuid, noexec, nodev
# /var/log — separate partition
# /var/log/audit — separate partition
# /home — nosuid, nodev

# Apply mount options
sudo tee -a /etc/fstab << 'EOF'
tmpfs /tmp tmpfs defaults,nosuid,noexec,nodev 0 0
tmpfs /var/tmp tmpfs defaults,nosuid,noexec,nodev 0 0
EOF

# Secure /boot mount (prevent unauthorized kernel updates)
sudo mount -o remount,rw,nosuid,nodev /boot

PAM Password Policies (CIS Control 5)

# Password quality requirements
sudo tee /etc/security/pwquality.conf << 'EOF'
minlen = 14
dcredit = -1
ucredit = -1
ocredit = -1
lcredit = -1
minclass = 4
maxrepeat = 3
difok = 5
usercheck = 1
enforce_for_root
EOF

# Password expiration
sudo tee -a /etc/login.defs << 'EOF'
PASS_MAX_DAYS 90
PASS_MIN_DAYS 7
PASS_WARN_AGE 14
EOF

# Lock account after failed attempts
sudo tee /etc/pam.d/common-auth << 'EOF'
auth required pam_faillock.so preauth audit silent deny=5 unlock_time=900
auth [default=die] pam_faillock.so authfail audit deny=5 unlock_time=900
auth sufficient pam_unix.so
auth required pam_deny.so
EOF

# Ensure root PATH is safe
echo 'Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"' | \
    sudo tee /etc/sudoers.d/secure_path

File Integrity Monitoring with AIDE (CIS Control 6)

# Install AIDE
sudo apt install aide

# Initialize the database
sudo aideinit
sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db

# Custom configuration
sudo tee -a /etc/aide/aide.conf << 'EOF'
# Critical system binaries
/etc/group PERMS
/etc/gshadow PERMS
/etc/passwd PERMS
/etc/shadow PERMS
/etc/ssh/sshd_config PERMS
/bin Content
/sbin Content
/usr/bin Content
/usr/sbin Content

# Configuration files
/etc Content
# Exclude transient files
!/var/log
!/tmp
!/proc
!/sys
EOF

# Run a check
sudo aide --check

# Run daily via cron
sudo tee /etc/cron.daily/aide-check << 'EOF'
#!/bin/bash
/usr/bin/aide --check | /usr/bin/mail -s "AIDE Daily Check" root
EOF
sudo chmod +x /etc/cron.daily/aide-check

Expected AIDE check output:

AIDE found differences between database and filesystem!!
Start timestamp: 2026-06-24 03:00:00

Summary:
  Total number of files:        34567
  Added files:                  2
  Removed files:                0
  Changed files:                12
  Changed attributes:
    /etc/nginx/nginx.conf       Size, MD5, SHA256
    /etc/hostname               Content
    /var/log/syslog             ...

Kernel Module Blacklisting (CIS Control 7)

# Blacklist unused kernel modules
sudo tee /etc/modprobe.d/blacklist-cis.conf << 'EOF'
# Unused filesystem modules
blacklist cramfs
blacklist freevxfs
blacklist jffs2
blacklist hfs
blacklist hfsplus
blacklist squashfs
blacklist udf
blacklist vfat

# Unused network protocols
blacklist dccp
blacklist sctp
blacklist rds
blacklist tipc

# Unused hardware drivers
blacklist bluetooth
blacklist btusb
blacklist thunderbolt
blacklist firewire-core
EOF

# Verify no modules are loaded
lsmod | grep -E "cramfs|freevxfs|jffs2|hfs|squashfs|udf|dccp|sctp|rds|tipc"

Automated Compliance with OpenSCAP

# Install OpenSCAP
sudo apt install libopenscap8 scap-security-guide

# Run a CIS Benchmark scan
sudo oscap xccdf eval \
    --profile xccdf_org.ssgproject.content_profile_cis \
    --results results.xml \
    --report report.html \
    /usr/share/xml/scap/ssg/content/ssg-ubuntu2204-ds.xml

# Open the HTML report
xdg-open report.html

Hardening Script Generator

# Generate a remediation script from scan results
sudo oscap xccdf generate fix \
    --profile xccdf_org.ssgproject.content_profile_cis \
    --output remediation.sh \
    /usr/share/xml/scap/ssg/content/ssg-ubuntu2204-ds.xml

Automated Hardening Checklist

#!/bin/bash
# cis-audit.sh — Check CIS Level 1 and 2 compliance
# Run as root

echo "=== CIS Compliance Audit ==="
echo "Date: $(date)"
echo

# 1. File permissions
echo "--- Critical File Permissions ---"
for f in /etc/passwd /etc/shadow /etc/group /etc/gshadow /etc/sudoers; do
    perms=$(stat -c "%a %u:%g %n" "$f" 2>/dev/null)
    echo "$perms"
done

# 2. SSH config
echo
echo "--- SSH Configuration ---"
grep -E "^(PermitRootLogin|PasswordAuthentication|PubkeyAuthentication|Protocol)" /etc/ssh/sshd_config

# 3. Listening ports
echo
echo "--- Listening Ports ---"
ss -tulpn4

# 4. Kernel parameters
echo
echo "--- Kernel Security Parameters ---"
for param in kernel.randomize_va_space net.ipv4.tcp_syncookies net.ipv4.conf.all.rp_filter kernel.perf_event_paranoid; do
    val=$(sysctl -n "$param" 2>/dev/null)
    echo "$param = $val"
done

# 5. Password policy
echo
echo "--- Password Policy ---"
grep -E "PASS_MAX_DAYS|PASS_MIN_DAYS|PASS_WARN_AGE" /etc/login.defs

# 6. Running services
echo
echo "--- Non-Essential Services Running ---"
for svc in avahi-daemon cups bluetooth rpcbind; do
    if systemctl is-active --quiet "$svc" 2>/dev/null; then
        echo "WARNING: $svc is running"
    fi
done

echo
echo "=== Audit Complete ==="

Common Errors

1. sysctl "Permission denied" at Boot

Some distributions restrict sysctl writes for security-related keys. Add kernel.kptr_restrict=2 to /etc/sysctl.d/ but it may need sysctl --system after boot if systemd-sysctl does not apply it.

2. AIDE Database Out of Sync After Updates

Every package update changes system binaries. Run sudo aideinit && sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db after each update cycle.

3. OpenSCAP Profile Not Found

The SCAP security guide package version may not include the CIS profile for your distribution. Check available profiles: oscap info /usr/share/xml/scap/ssg/content/ssg-ubuntu2204-ds.xml | grep Profile.

4. PAM Lockout Locks Admin Out

If you misconfigure PAM, all users including root may be locked out. Always keep a root shell open when testing PAM changes. Have a recovery plan (rescue mode, live USB).

5. Blacklisted Modules Still Loading

Some modules load via initramfs. Rebuild initramfs after changing modprobe.d: sudo update-initramfs -u -k all.

6. Firewall Rules Ineffective Without IPv6

Many services listen on IPv6 by default. CIS requires both ip6tables and iptables rules, or use nftables with inet family that covers both protocols.

7. /tmp Mount Options Not Applied After Reboot

The tmp.mount systemd unit may override fstab entries for /tmp. Mask the unit: sudo systemctl mask tmp.mount.

Practice Questions

1. What CIS control disables core dumps? Control 1.2.1: set fs.suid_dumpable = 0 in sysctl configuration.

2. Which kernel parameter enables ASLR? kernel.randomize_va_space = 2 — full randomization for all memory mappings.

3. How does AIDE detect unauthorized file changes? It creates a baseline database of file checksums (SHA256, MD5) and compares current files against it. Any mismatch triggers an alert.

4. What is the CIS recommended minimum password length? 14 characters, with at least one uppercase, lowercase, digit, and special character each.

5. How do you generate a remediation script from an OpenSCAP scan? sudo oscap xccdf generate fix --profile cis --output remediate.sh <data-stream.xml>

Challenge: Set up a fresh Ubuntu 22.04 server and apply CIS Level 2 hardening controls. Run OpenSCAP before and after to get a Compliance score. Your target is 90%+ Compliance. Document each control you change, the rationale, and any trade-offs (e.g., controls that break specific applications). Create a cron job that runs AIDE daily and emails the report.

Do I need all CIS Level 2 controls?

Level 2 controls can break applications. Apply them selectively based on your workload. Level 1 controls are safe for most environments.

How often should I run AIDE checks?

Daily for production servers. More frequent checks on systems with high change velocity. The cron.daily script runs at 6:25 AM by default.

What is the difference between CIS and STIG?

CIS is developed by the Center for Internet Security (community consensus). STIG is developed by the US Defense Information Systems Agency. STIG is more restrictive and focused on government systems.

Can I automate 100% of CIS Compliance?

Yes — use Ansible with the devsec.hardening collection or OpenSCAP's built-in fix capability. Manual review is still needed for controls that require business decisions (e.g., audit retention periods).

Does CIS hardening affect performance?

Minimally. Controls like ASLR, AIDE, and auditd have negligible overhead. Disabling unused services and filesystem modules reduces attack surface and slightly improves boot time.

What's Next

Linux Containers — LXC & LXD Guide
Security Hardening — SSH, Firewall, fail2ban
journalctl — Querying Systemd Logs

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Updated 2026-06-24.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro