Implement Linux security hardening with CIS benchmarks and automated compliance scanning

Intermediate 45 min Apr 26, 2026 572 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Harden your Linux systems using CIS benchmarks with automated compliance scanning and continuous monitoring. Learn to implement security controls for filesystem permissions, user authentication, network policies, and maintain ongoing compliance with industry standards.

Prerequisites

  • Root or sudo access
  • Basic Linux command line knowledge
  • Understanding of system administration concepts

What this solves

CIS (Center for Internet Security) benchmarks provide industry-standard security configurations for Linux systems. This tutorial implements comprehensive security hardening following CIS guidelines and sets up automated compliance scanning to detect configuration drift. You'll secure filesystem permissions, strengthen authentication controls, configure network security policies, and establish continuous monitoring for compliance violations.

Step-by-step implementation

Update system packages

Start with fully updated packages to ensure you have the latest security patches.

sudo apt update && sudo apt upgrade -y
sudo apt install -y wget curl gnupg software-properties-common
sudo dnf update -y
sudo dnf install -y wget curl gnupg2 yum-utils

Install CIS-CAT assessment tool

Download and configure the CIS-CAT Lite assessment tool for automated benchmark scanning.

cd /opt
sudo mkdir -p cis-cat
cd cis-cat

# Download CIS-CAT Lite (replace with latest version URL)
sudo wget https://learn.cisecurity.org/e/799323/CIS-CAT-Lite-v4-22-0-zip/1234567/1234567890
sudo unzip CIS-CAT-Lite-v4-22-0.zip
sudo chmod +x CIS-CAT-Lite/cis-cat-lite.sh

Configure filesystem security hardening

Implement secure mount options and filesystem permissions according to CIS benchmarks.

# Add secure mount options to existing filesystems
# Example for /tmp with nodev,nosuid,noexec
tmpfs /tmp tmpfs defaults,rw,nosuid,nodev,noexec,relatime 0 0

# For existing filesystems, add security options:
# /dev/sda1 / ext4 defaults,nodev 0 1
</code><h2>`/dev/sda2 /home ext4 defaults,nodev,nosuid 0 1`</h2></pre>

<pre class="terminal"><code># Create separate partition for /var/log if not exists
sudo mkdir -p /var/log/audit

# Set secure permissions on system directories
sudo chmod 700 /root
sudo chmod 750 /home
sudo chmod 755 /var/log
sudo chmod 750 /var/log/audit

# Secure boot directory
sudo chmod 700 /boot
sudo chown root:root /boot

Configure user account and authentication controls

Strengthen password policies, account lockout, and user session controls.

# Password complexity requirements
minlen = 14
minclass = 4
maxrepeat = 2
maxclassrepeat = 2
dcredit = -1
ucredit = -1
lcredit = -1
ocredit = -1
difok = 8
reject_username
enforcetype
# Add account lockout policy (insert after auth required pam_unix.so)
auth required pam_faillock.so preauth silent audit deny=3 unlock_time=900 fail_interval=900
auth [default=die] pam_faillock.so authfail audit deny=3 unlock_time=900 fail_interval=900
auth sufficient pam_faillock.so authsucc audit deny=3 unlock_time=900 fail_interval=900
# Configure secure login defaults
PASS_MAX_DAYS 90
PASS_MIN_DAYS 1
PASS_WARN_AGE 7
LOGIN_RETRIES 3
LOGIN_TIMEOUT 60
SHA_CRYPT_MIN_ROUNDS 5000
SHA_CRYPT_MAX_ROUNDS 10000
# Remove unused user accounts and groups
sudo userdel games 2>/dev/null || true
sudo userdel news 2>/dev/null || true
sudo groupdel games 2>/dev/null || true

# Lock system accounts
sudo usermod -L -s /sbin/nologin bin
sudo usermod -L -s /sbin/nologin daemon
sudo usermod -L -s /sbin/nologin adm
sudo usermod -L -s /sbin/nologin lp

Configure SSH hardening

Secure SSH access with CIS-compliant configuration settings.

# SSH hardening configuration
Protocol 2
Port 22
AddressFamily inet
ListenAddress 0.0.0.0

# Authentication settings
PermitRootLogin no
MaxAuthTries 3
MaxSessions 4
MaxStartups 10:30:60
LoginGraceTime 60

# Key-based authentication
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
KerberosAuthentication no
GSSAPIAuthentication no

# Security options
X11Forwarding no
PermitUserEnvironment no
Compression no
UseDNS no
PermitTunnel no
AllowTcpForwarding no
ClientAliveInterval 300
ClientAliveCountMax 2

# Restrict users and groups
# AllowUsers user1 user2
# AllowGroups ssh-users

# Logging
SyslogFacility AUTHPRIV
LogLevel INFO
sudo systemctl restart sshd
sudo systemctl status sshd

Configure network and firewall security

Implement network hardening with secure kernel parameters and firewall rules.

# Network security hardening
# IP forwarding
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0

# Source routing
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.ipv6.conf.all.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0

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

# Secure ICMP redirects
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

# Ignore ICMP ping requests
net.ipv4.icmp_echo_ignore_all = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1

# TCP SYN flood protection
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 5

# IPv6 router advertisements
net.ipv6.conf.all.accept_ra = 0
net.ipv6.conf.default.accept_ra = 0
# Apply sysctl settings
sudo sysctl -p /etc/sysctl.d/99-cis-hardening.conf

# Configure UFW firewall
sudo ufw --force reset
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw --force enable
# Apply sysctl settings
sudo sysctl -p /etc/sysctl.d/99-cis-hardening.conf

# Configure firewalld
sudo systemctl enable --now firewalld
sudo firewall-cmd --set-default-zone=public
sudo firewall-cmd --permanent --remove-service=dhcpv6-client
sudo firewall-cmd --permanent --remove-service=cockpit
sudo firewall-cmd --reload

Configure audit logging

Enable comprehensive audit logging for security events and compliance monitoring.

sudo apt install -y auditd audispd-plugins
sudo dnf install -y audit audit-libs
# Audit daemon configuration
log_file = /var/log/audit/audit.log
log_format = RAW
log_group = adm
priority_boost = 4
flush = INCREMENTAL_ASYNC
freq = 50
num_logs = 5
disp_qos = lossy
dispatcher = /sbin/audispd
name_format = HOSTNAME
max_log_file = 50
max_log_file_action = ROTATE
space_left = 75
space_left_action = SYSLOG
verify_email = yes
admin_space_left = 50
admin_space_left_action = SUSPEND
disk_full_action = SUSPEND
disk_error_action = SUSPEND
use_libwrap = yes
tcp_listen_queue = 5
tcp_max_per_addr = 1
tcp_client_max_idle = 0
enable_krb5 = no
# CIS Audit Rules
# Remove any existing rules
-D

# Buffer size
-b 8192

# Failure mode
-f 1

# Audit the auditd configuration files
-w /etc/audit/ -p wa -k auditconfig
-w /etc/libaudit.conf -p wa -k auditconfig

# Monitor for changes to user and group files
-w /etc/group -p wa -k identity
-w /etc/passwd -p wa -k identity
-w /etc/gshadow -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/security/opasswd -p wa -k identity

# Monitor login and logout events
-w /var/log/lastlog -p wa -k logins
-w /var/run/faillock/ -p wa -k logins

# Monitor network configuration changes
-a always,exit -F arch=b64 -S sethostname -S setdomainname -k system-locale
-a always,exit -F arch=b32 -S sethostname -S setdomainname -k system-locale
-w /etc/issue -p wa -k system-locale
-w /etc/issue.net -p wa -k system-locale
-w /etc/hosts -p wa -k system-locale
-w /etc/network/ -p wa -k system-locale

# Monitor system administration events
-w /var/log/sudo.log -p wa -k actions
-w /etc/sudoers -p wa -k actions
-w /etc/sudoers.d/ -p wa -k actions

# Monitor kernel modules
-w /sbin/insmod -p x -k modules
-w /sbin/rmmod -p x -k modules
-w /sbin/modprobe -p x -k modules
-a always,exit -F arch=b64 -S init_module -S delete_module -k modules

# Monitor file permission changes
-a always,exit -F arch=b64 -S chmod -S fchmod -S fchmodat -F auid>=1000 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b32 -S chmod -S fchmod -S fchmodat -F auid>=1000 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S chown -S fchown -S fchownat -S lchown -F auid>=1000 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b32 -S chown -S fchown -S fchownat -S lchown -F auid>=1000 -F auid!=4294967295 -k perm_mod

# Make the configuration immutable
-e 2
sudo systemctl enable --now auditd
sudo augenrules --load
sudo systemctl restart auditd

Set up automated compliance monitoring

Create scripts for continuous CIS benchmark compliance checking and reporting.

#!/bin/bash
# CIS Compliance Monitoring Script

LOG_DIR="/var/log/cis-compliance"
REPORT_FILE="$LOG_DIR/cis-report-$(date +%Y%m%d-%H%M%S).json"
CIS_CAT_DIR="/opt/cis-cat/CIS-CAT-Lite"

# Create log directory
mkdir -p "$LOG_DIR"

# Function to log messages
log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_DIR/compliance.log"
}

log_message "Starting CIS compliance check"

# Run CIS-CAT assessment
if [ -d "$CIS_CAT_DIR" ]; then
    cd "$CIS_CAT_DIR"
    ./cis-cat-lite.sh -a -r "$REPORT_FILE" -rft json
    log_message "CIS-CAT assessment completed: $REPORT_FILE"
else
    log_message "ERROR: CIS-CAT directory not found at $CIS_CAT_DIR"
    exit 1
fi

# Parse results and check for failures
FAILED_CHECKS=$(jq '.TestResult[] | select(.result == "fail") | .ruleId' "$REPORT_FILE" | wc -l)
TOTAL_CHECKS=$(jq '.TestResult | length' "$REPORT_FILE")
SCORE=$(jq '.score' "$REPORT_FILE")

log_message "Compliance Score: $SCORE%"
log_message "Failed Checks: $FAILED_CHECKS/$TOTAL_CHECKS"

# Send alert if compliance score is below threshold
THRESHOLD=80
if (( $(echo "$SCORE < $THRESHOLD" | bc -l) )); then
    log_message "ALERT: Compliance score ($SCORE%) is below threshold ($THRESHOLD%)"
    
    # Extract failed checks
    echo "Failed CIS Controls:" >> "$LOG_DIR/failed-controls.txt"
    jq -r '.TestResult[] | select(.result == "fail") | "\(.ruleId): \(.description)"' "$REPORT_FILE" >> "$LOG_DIR/failed-controls.txt"
    
    # Optional: Send email notification (requires mail setup)
    # mail -s "CIS Compliance Alert - Score: $SCORE%" admin@example.com < "$LOG_DIR/failed-controls.txt"
fi

# Cleanup old reports (keep last 30 days)
find "$LOG_DIR" -name "cis-report-*.json" -mtime +30 -delete

log_message "CIS compliance check completed"
sudo mkdir -p /opt/cis-monitoring
sudo chmod +x /opt/cis-monitoring/cis-compliance-check.sh
sudo chown root:root /opt/cis-monitoring/cis-compliance-check.sh

# Install bc for calculations
sudo apt install -y bc jq || sudo dnf install -y bc jq

Configure automated compliance reporting

Set up systemd timers for regular compliance checks and automated reporting.

[Unit]
Description=CIS Compliance Check
Wants=cis-compliance-check.timer

[Service]
Type=oneshot
User=root
ExecStart=/opt/cis-monitoring/cis-compliance-check.sh
StandardOutput=journal
StandardError=journal
[Unit]
Description=Run CIS compliance check daily
Requires=cis-compliance-check.service

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now cis-compliance-check.timer
sudo systemctl status cis-compliance-check.timer

Configure centralized logging integration

Forward security events and compliance reports to centralized logging system.

# CIS Security Event Logging
# Forward audit logs to central server (optional)
# *.* @@log.example.com:514

# Local security log aggregation
auth,authpriv.*                 /var/log/auth.log
kern.*                          /var/log/kern.log
mail.*                          /var/log/mail.log
cron.*                          /var/log/cron.log

# CIS compliance logs
:programname,isequal,"cis-compliance" /var/log/cis-compliance/compliance.log
& stop
sudo systemctl restart rsyslog

# Set up log rotation for compliance logs
sudo tee /etc/logrotate.d/cis-compliance << EOF
/var/log/cis-compliance/*.log {
    weekly
    missingok
    rotate 12
    compress
    delaycompress
    notifempty
    create 644 root root
    postrotate
        systemctl reload rsyslog > /dev/null 2>&1 || true
    endscript
}
EOF

Verify your setup

Test the security hardening implementation and compliance monitoring system.

# Check SSH configuration
sudo sshd -t
sudo systemctl status sshd

# Verify firewall status
sudo ufw status verbose || sudo firewall-cmd --list-all

# Check audit daemon
sudo systemctl status auditd
sudo auditctl -s

# Test audit rules
sudo ausearch -k identity -ts today

# Verify sysctl security settings
sudo sysctl net.ipv4.ip_forward
sudo sysctl net.ipv4.conf.all.send_redirects

# Check compliance monitoring timer
sudo systemctl status cis-compliance-check.timer
sudo systemctl list-timers cis-compliance-check.timer

# Run manual compliance check
sudo /opt/cis-monitoring/cis-compliance-check.sh

# Check compliance logs
sudo tail -f /var/log/cis-compliance/compliance.log
Note: The initial CIS-CAT scan may take 10-15 minutes depending on system size. Monitor /var/log/cis-compliance/compliance.log for progress updates.

Common issues

SymptomCauseFix
SSH login fails after hardeningDisabled password authentication without SSH keysAdd SSH public key to ~/.ssh/authorized_keys before disabling passwords
Applications can't write to logsRestrictive file permissionsCreate dedicated log directories with chmod 755 and correct ownership
CIS-CAT scan failsMissing Java or incorrect permissionsInstall OpenJDK and ensure cis-cat-lite.sh is executable
Audit logs filling diskHigh audit rule verbosityConfigure log rotation and adjust audit rules granularity
Network services unreachableOverly restrictive firewall rulesAdd specific service rules before enabling firewall
System performance degradedExcessive audit loggingTune audit rules to focus on critical events only
Warning: Always test security hardening in a non-production environment first. Some CIS controls may break applications that rely on specific system behaviors. Plan for gradual implementation and thorough testing.

Next steps

Running this in production?

Need compliance handled automatically? Setting this up once is straightforward. Keeping it patched, monitored, backed up and compliant across environments is the harder part. See how we run infrastructure like this for European teams.

Automated install script

Run this to automate the entire setup

¿Prefiere no gestionarlo usted mismo?

Gestionamos la infraestructura de empresas que dependen del tiempo de actividad. Totalmente gestionada, con un contacto fijo que conoce su entorno.

Tiene un contacto fijo que conoce su entorno

Róterdam 06:01 · accesible por mensaje, sin formulario de tickets