Skip to content

System Hardening: CIS Benchmarks & STIG

Chapter 15: System Hardening: CIS Benchmarks & STIG

Section titled “Chapter 15: System Hardening: CIS Benchmarks & STIG”
  • Understand what system hardening means and why it’s required
  • Know the CIS Benchmarks framework and how to apply it
  • Understand STIG and DoD security requirements
  • Know how to automate hardening with tools like Lynis, OpenSCAP
  • Apply production-grade hardening scripts

System hardening is the process of locking down a server to reduce its ‘attack surface’. Think of it as securing a house: you lock the doors, close the windows, turn off unnecessary outside lights, and ensure only trusted people have the keys.

System hardening is the process of reducing a system’s attack surface by eliminating unnecessary services, applying secure configurations, and enforcing security policies.

Hardening Philosophy: Reduce Attack Surface
─────────────────────────────────────────────
Default Server Installation:
┌────────────────────────────────────────────────┐
│ Running services: 47 │
│ Open ports: 23 │
│ Installed packages: 1,247 │
│ SUID binaries: 22 │
│ World-writable files: 134 │
│ Attack Surface: LARGE │
└────────────────────────────────────────────────┘
After Hardening:
┌────────────────────────────────────────────────┐
│ Running services: 8 (only what's needed) │
│ Open ports: 3 (SSH, app, monitoring) │
│ Installed packages: 312 (minimal) │
│ SUID binaries: 4 (essential only) │
│ World-writable files: 2 (/tmp, /var/tmp) │
│ Attack Surface: MINIMAL │
└────────────────────────────────────────────────┘
Principle: If it's not needed, remove it.
If it runs, restrict it.
If it exists, audit it.

CIS (Center for Internet Security) Benchmarks are internationally recognized security configuration guidelines developed by cybersecurity professionals.

CIS Benchmark Levels
─────────────────────
Level 1 (L1): Essential Security
- Minimal performance impact
- Can be applied to most environments
- Quick wins with high impact
- Examples: disable unused services, enforce passwords
Level 2 (L2): Advanced Security
- May impact functionality
- Suitable for high-security environments
- Stricter controls
- Examples: strict filesystem permissions, SELinux enforcing
Terminal window
# ── Section 1: Initial Setup ──────────────────────────────
# 1.1: Filesystem Configuration
# Disable unused filesystems
cat >> /etc/modprobe.d/blacklist.conf << 'EOF'
install cramfs /bin/true # Old filesystem
install freevxfs /bin/true # Rare filesystem
install jffs2 /bin/true # Embedded filesystem
install hfs /bin/true # Apple HFS
install hfsplus /bin/true # Apple HFS+
install squashfs /bin/true # Snap packages (if not used)
install udf /bin/true # DVD filesystem
EOF
# 1.2: Separate filesystem partitions (prevent one area filling another)
# In production, use separate LVM volumes or partitions:
# /tmp - separate, noexec, nosuid, nodev
# /var - separate (prevents log fills from crashing system)
# /var/log - separate (audit logs isolated)
# /home - separate
# Mount /tmp with restrictions
cat >> /etc/fstab << 'EOF'
tmpfs /tmp tmpfs defaults,nodev,nosuid,noexec 0 0
EOF
# Or if /tmp is on separate partition:
# UUID=... /tmp ext4 defaults,nodev,nosuid,noexec 0 2
# ── Section 2: Services ───────────────────────────────────
# 2.1: Disable unused services
UNUSED_SERVICES=(
"avahi-daemon" # Zeroconf/mDNS - not needed on servers
"cups" # Printer service - not needed on servers
"nfs-server" # NFS server - only if not an NFS server
"rpcbind" # RPC - not needed without NFS
"rsync" # Rsync daemon - only if needed
"talk" # Chat service - obsolete
"telnet" # Telnet - NEVER on production (cleartext!)
"xinetd" # Super-server - obsolete
)
for service in "${UNUSED_SERVICES[@]}"; do
if systemctl is-active "$service" &>/dev/null; then
systemctl stop "$service"
systemctl mask "$service"
echo "Masked: $service"
fi
done
# ── Section 3: Network Configuration ─────────────────────
# 3.1: Disable unused network protocols
cat >> /etc/modprobe.d/blacklist.conf << 'EOF'
install dccp /bin/true # DCCP protocol
install sctp /bin/true # SCTP protocol
install rds /bin/true # RDS protocol
install tipc /bin/true # TIPC protocol
EOF
# 3.2: IP forwarding (disable unless this is a router)
echo "net.ipv4.ip_forward = 0" >> /etc/sysctl.d/99-cis.conf
# 3.3: ICMP and redirect settings
cat >> /etc/sysctl.d/99-cis.conf << 'EOF'
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
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
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.tcp_syncookies = 1
net.ipv6.conf.all.accept_ra = 0
net.ipv6.conf.default.accept_ra = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
EOF
sysctl -p /etc/sysctl.d/99-cis.conf
# ── Section 4: Logging and Auditing ──────────────────────
# 4.1: Configure auditd (see Chapter 19 for deep dive)
systemctl enable --now auditd
# 4.2: Configure rsyslog
cat >> /etc/rsyslog.conf << 'EOF'
# Send auth logs to separate file
auth,authpriv.* /var/log/auth.log
# Send all logs to secure remote server
*.* @@siem.example.com:514
EOF
# ── Section 5: Access, Authentication & Authorization ────
# 5.1: Configure sudo
# Restrict sudo to wheel group only
echo "%wheel ALL=(ALL) ALL" > /etc/sudoers.d/99-wheel
echo "Defaults requiretty" >> /etc/sudoers.d/99-wheel
echo "Defaults logfile=/var/log/sudolog" >> /etc/sudoers.d/99-wheel
# 5.2: SSH configuration (see Chapter 20 for full SSH hardening)
# 5.3: Password policies (see Chapter 22)
# ── Section 6: System Maintenance ───────────────────────
# 6.1: Verify permissions on critical files
chmod 644 /etc/passwd
chmod 000 /etc/shadow # Only root should ever access
chmod 644 /etc/group
chmod 700 /root
chmod 700 /root/.ssh
chmod 600 /root/.ssh/authorized_keys 2>/dev/null || true
# 6.2: Find unowned files (potential leftovers from deleted users)
find / -xdev \( -nouser -o -nogroup \) 2>/dev/null | head -20

15.3 STIG (Security Technical Implementation Guides)

Section titled “15.3 STIG (Security Technical Implementation Guides)”

STIG is the DoD (Department of Defense) security standard, more stringent than CIS.

CIS vs STIG Comparison
─────────────────────────────────────────────────────────
Feature CIS Benchmark STIG
─────────────────────────────────────────────────────────
Publisher Center for Internet DISA (DoD)
Security
Audience All organizations Government/DoD
contractors
Strictness Level 1 (moderate) Very strict
Level 2 (strict)
Focus Best practices Compliance
requirement
Format PDF + XCCDF XCCDF + OVAL
Tool CIS-CAT, Lynis OpenSCAP, STIG Viewer
─────────────────────────────────────────────────────────
Terminal window
# STIG V-238200: OS must use DoD PKI for authentication
# Implementation: Configure PAM for smart card auth
# STIG V-238201: Password must meet complexity
# /etc/security/pwquality.conf
cat >> /etc/security/pwquality.conf << 'EOF'
minlen = 15
dcredit = -1
ucredit = -1
ocredit = -1
lcredit = -1
maxrepeat = 3
dictcheck = 1
EOF
# STIG V-238210: SSH must use FIPS-compliant algorithms
# /etc/ssh/sshd_config
# See Chapter 20 for full SSH STIG hardening
# STIG V-238217: All audit records must contain user info
# Configure auditd:
echo "-a always,exit -F arch=b64 -S execve" >> /etc/audit/rules.d/audit.rules
# STIG V-238314: OS must disable CTRL-ALT-DELETE
systemctl mask ctrl-alt-del.target

Terminal window
# Install Lynis
apt-get install lynis # Debian/Ubuntu
# or download from cisofy.com
# Run security audit
lynis audit system
# Output: hardening score + recommendations
# Hardening index: 68 [############# ]
# Check specific categories
lynis audit system --tests-from-group authentication
lynis audit system --tests-from-group malware
# Generate report
lynis audit system --quiet --report-file /tmp/lynis-report.dat
# Continuous monitoring
# Run lynis via cron weekly, compare report for changes
Terminal window
# Install OpenSCAP
apt-get install openscap-scanner ssg-debian # Debian/Ubuntu
yum install openscap-scanner scap-security-guide # RHEL
# Available profiles
oscap info /usr/share/xml/scap/ssg/content/ssg-ubuntu2204-ds.xml | grep "Profile"
# Run CIS benchmark scan
oscap xccdf eval \
--profile xccdf_org.ssgproject.content_profile_cis_level1_server \
--results /tmp/scan-results.xml \
--report /tmp/scan-report.html \
/usr/share/xml/scap/ssg/content/ssg-ubuntu2204-ds.xml
# View HTML report
# Open /tmp/scan-report.html in browser
# Remediation (apply fixes automatically)
oscap xccdf eval \
--profile xccdf_org.ssgproject.content_profile_cis_level1_server \
--remediate \
/usr/share/xml/scap/ssg/content/ssg-ubuntu2204-ds.xml
Section titled “Ansible-based Hardening (Production Recommended)”
hardening-playbook.yml
---
- name: CIS Level 1 Server Hardening
hosts: all
become: yes
roles:
- { role: devsec.os_hardening }
- { role: devsec.ssh_hardening }
# Install roles:
# ansible-galaxy install devsec.os_hardening devsec.ssh_hardening
# Run:
# ansible-playbook -i inventory hardening-playbook.yml --check # Dry run
# ansible-playbook -i inventory hardening-playbook.yml # Apply

15.5 Hardening Checklist for Production Servers

Section titled “15.5 Hardening Checklist for Production Servers”
✅ Production Server Hardening Checklist
─────────────────────────────────────────
OS Configuration:
□ Minimal OS installation (no X11, no games, no compilers)
□ Remove unused packages: apt autoremove / yum autoremove
□ Separate /tmp, /var, /var/log partitions
□ /tmp mounted with noexec,nosuid,nodev
□ Disable unused filesystems
□ Disable unused network protocols (DCCP, SCTP, etc.)
User Management:
□ No empty passwords (cat /etc/shadow | awk -F: '$2 == "" {print}')
□ No duplicate UIDs/GIDs
□ Root login disabled (PermitRootLogin no)
□ Minimum password length: 12+
□ Password expiry configured
□ Lock inactive accounts after 30 days
SSH:
□ Protocol 2 only
□ Key-based auth only (no passwords)
□ Allowed users/groups restricted
□ Port changed (or firewall restricts to known IPs)
□ AllowTcpForwarding disabled (unless needed)
Services:
□ Only required services running
□ All services running as non-root
□ firewalld/iptables configured (default deny)
Logging:
□ auditd running and configured
□ All auth events logged
□ Logs forwarded to central SIEM
□ Log rotation configured
Updates:
□ Automatic security updates enabled
□ Process for emergency patching documented

#!/bin/bash
# cis-harden.sh — CIS Level 1 Hardening Script
# Test in staging first! Some settings may break applications.
set -euo pipefail
LOG=/var/log/hardening-$(date +%Y%m%d).log
log() { echo "[$(date)] $*" | tee -a "$LOG"; }
log "=== Starting CIS Level 1 Hardening ==="
# --- Remove unused packages ---
log "Removing unused packages..."
apt-get purge -y telnet rsh-client rsh-redone-client talk xserver-xorg* \
2>/dev/null || true
# --- Disable unused filesystems ---
log "Disabling unused filesystems..."
for fs in cramfs freevxfs jffs2 hfs hfsplus udf; do
echo "install $fs /bin/true" >> /etc/modprobe.d/blacklist.conf
done
# --- Configure /tmp ---
log "Configuring /tmp..."
if ! grep -q "tmpfs /tmp" /etc/fstab; then
echo "tmpfs /tmp tmpfs defaults,rw,nosuid,nodev,noexec,relatime 0 0" >> /etc/fstab
mount -o remount /tmp 2>/dev/null || true
fi
# --- Disable unused services ---
log "Disabling unused services..."
for svc in avahi-daemon cups isc-dhcp-server isc-dhcp-server6 slapd \
nfs-kernel-server rpcbind bind9 vsftpd apache2 dovecot exim4; do
systemctl disable --now "$svc" 2>/dev/null || true
done
# --- Network hardening ---
log "Applying network hardening..."
cat > /etc/sysctl.d/99-cis.conf << 'SYSCTL'
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.log_martians = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.tcp_syncookies = 1
kernel.randomize_va_space = 2
SYSCTL
sysctl -p /etc/sysctl.d/99-cis.conf
# --- File permissions ---
log "Setting critical file permissions..."
chmod 644 /etc/passwd /etc/group
chmod 640 /etc/shadow /etc/gshadow
chmod 700 /root
chown root:root /etc/passwd /etc/shadow /etc/group /etc/gshadow
# --- SUID audit ---
log "Auditing SUID files..."
find / -xdev -perm -4000 -type f 2>/dev/null >> "$LOG"
log "=== Hardening Complete. Review $LOG ==="
log "=== Run 'lynis audit system' to verify score ==="

Q1: What is the difference between CIS Benchmark Level 1 and Level 2?

Answer: CIS Benchmark Level 1 is the “essential security” tier — controls that have minimal performance or functionality impact and are recommended for all environments. These are the quick wins: disabling unused services, configuring password policies, setting appropriate file permissions, enabling firewall. Level 2 is “advanced security” — more restrictive controls that may impact functionality or require significant configuration, suitable for high-security environments. Examples: SELinux in Enforcing mode, strict auditing of all file system events, stricter kernel parameters. Level 2 often requires more customization per environment.

Q2: What is STIG and who must comply with it?

Answer: STIG (Security Technical Implementation Guide) is published by DISA (Defense Information Systems Agency) and represents the DoD’s security hardening requirements. It’s mandatory for: US federal government IT systems, DoD contractors, systems handling classified information, and many state/local government systems. STIGs are stricter than CIS Benchmarks — they specify exact configuration values, have specific vulnerability IDs (V-numbers), and often require formal documentation of compliance or exceptions. STIGs are evaluated using tools like OpenSCAP with SCAP content, and compliance is often audited by government auditors.

Q3: Walk me through how you would harden a new production web server.

Answer: (1) Minimal installation: Start with a server-only OS profile, no GUI, no compilers. (2) Update: Apply all patches immediately. (3) Remove unused: Purge telnet, FTP, X11, unused services. (4) Filesystem: Separate /tmp, /var with noexec/nosuid mounts. (5) Network: Configure firewall (deny all in, allow only needed ports), apply sysctl hardening (syncookies, rp_filter, disable redirects). (6) SSH: Key-only auth, disable root login, restrict to known IPs. (7) User management: Strong password policy, no shared accounts. (8) Services: Disable unnecessary services, run all services as non-root. (9) Audit: Enable auditd, configure logging to SIEM. (10) Verify: Run Lynis or OpenSCAP to confirm compliance. (11) Automate: Apply the same hardening via Ansible to all servers in the fleet.


FrameworkOrganizationUse Case
CIS BenchmarkCISGeneral enterprise hardening
STIGDISA/DoDGovernment and defense
PCI-DSSPCI CouncilPayment card environments
HIPAA SecurityHHSHealthcare systems
ToolPurpose
LynisLinux security audit
OpenSCAPSCAP/CIS/STIG compliance scanning
CIS-CATCIS Benchmark scanner
Ansible + devsecAutomated hardening

Chapter 14 (Security Fundamentals).


Advantages: Drastically reduces attack surface, stops automated worms and script kiddies. Disadvantages: Time-consuming, over-hardening can break legitimate applications.


  • Applying STIGs blindly without testing — can break production applications.
  • Leaving default passwords or default services enabled.
  • Forgetting to harden the bootloader (GRUB password).

SymptomCauseDiagnosisFix
Application breaks after hardeningToo strict permissions or disabled featuresReview hardening audit logsRevert specific controls or add exceptions

Objective: Perform basic hardening checks.

Terminal window
# 1. Check for empty passwords
awk -F: '($2 == "") {print}' /etc/shadow
# 2. Check root login via SSH
grep "^PermitRootLogin" /etc/ssh/sshd_config
# 3. List enabled services
systemctl list-unit-files --state=enabled

  1. Run a basic CIS benchmark script (like Lynis) on a test system and review the score.
  2. Disable unused network protocols (e.g., DCCP, SCTP, RDS) via modprobe blacklisting.

  • Hardening is about reducing the attack surface.
  • CIS Benchmarks and STIGs provide standardized checklists for securing systems.
  • Disable unused services, enforce strong passwords, and restrict network access.



Last Updated: July 2026