Pages

RHEL 9 CIS Benchmark Audit Script for Sysadmins

The script checks CIS recommendations from Sections 1 to 7, including system setup, services, networking, firewall configuration, access control, logging, and file permissions.

Requirements:
Script must run as root because many system files and settings require elevated permissions.
Output should be written to a log file and a JSON file for reporting.
Each check should show one of the following results:
  • PASS – configuration is correct
  • FAIL – configuration does not meet CIS recommendation
  • MANUAL – requires manual verification

1. Initial Setup:
The script verifies that important security packages are installed and running.
Examples:
  • AIDE for file integrity monitoring
  • Chrony for time synchronization
Some items such as subscription registration and global GPG checks must be verified manually.

2.Services
Checks that unnecessary services are disabled. These services can increase the attack surface if they are not required.
Examples checked:
  • autofs
  • avahi-daemon
  • telnet
  • vsftpd
  • rpcbind
  • snmpd
The script also verifies that the cron service is enabled for scheduled jobs.

3.Network Security
Validates kernel network parameters using sysctl settings.
These settings help protect the system against common network attacks.
Examples:
  • IP forwarding disabled
  • ICMP redirect protection
  • TCP SYN cookies enabled
  • Source route protection
4.Firewall
Ensures that firewall protection is enabled.
Checks include:
  • nftables package installed
  • firewalld service enabled
5.Access Control
Validates secure configuration of user access and SSH settings.
Checks include:
  • sudo package installed
  • SSH configuration file permissions
  • Root login disabled via SSH
  • MaxAuthTries configured
  • ClientAliveInterval configured
Multi-factor authentication for SSH must be verified manually.

6.Logging and Auditing
Ensures logging and auditing services are available.
Checks include:
  • audit package installed
  • auditd service enabled
  • rsyslog service enabled
Audit rule configuration must be reviewed manually.

7.File Permissions
Checks permissions for critical system files.
Files verified:
  • /etc/passwd
  • /etc/shadow
  • /etc/group
  • /etc/gshadow
The script also checks:
  • world-writable files on the system
  • existence of home directories for regular users
  • SUID files (manual review required)
Output:
The script generates two reports:
Log file
/var/log/cis_rhel9_report.log
Contains human-readable scan results.

JSON report
/var/log/cis_rhel9_report.json
Can be used for integration with monitoring tools or automation systems.

Summary:
After the scan completes, the script prints a summary showing:
  • number of passed checks
  • number of failed checks
  • number of manual checks required
Usage:
Script:
-------------------------------------------------------------------------------------------------------------------------
#!/bin/bash
# Enterprise CIS RHEL 9 Compliance Scanner
# Sections CIS 1–7 Coverage
# Output: Log + JSON
# Author: adminCtrlX
# --------------------------
# ROOT CHECK
if [ "$EUID" -ne 0 ]; then
  echo "Error: Run this script as root (sudo)."
  exit 1
fi

# --------------------------
# CONFIG & FILES
# --------------------------
REPORT="/var/log/cis_rhel9_report.log"
JSON="/var/log/cis_rhel9_report.json"
COL_WIDTH=50

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

# Initialize Files
> $REPORT
echo "[" > $JSON

PASS=0
FAIL=0
MANUAL=0
FIRST_ENTRY=true

# --------------------------
# LOGGER FUNCTIONS
# --------------------------

log_json() {
  local check="$1"
  local status="$2"
  if [ "$FIRST_ENTRY" = true ]; then
    echo "  {\"check\":\"$check\", \"status\":\"$status\"}" >> $JSON
    FIRST_ENTRY=false
  else
    # Prepend comma to the previous line to ensure valid JSON array
    sed -i '$ s/$/ ,/' $JSON
    echo "  {\"check\":\"$check\", \"status\":\"$status\"}" >> $JSON
  fi
}

pass() {
  printf "${GREEN}[PASS]${NC} %-${COL_WIDTH}s\n" "$1" | tee -a $REPORT
  log_json "$1" "PASS"
  ((PASS++))
}

fail() {
  printf "${RED}[FAIL]${NC} %-${COL_WIDTH}s\n" "$1" | tee -a $REPORT
  log_json "$1" "FAIL"
  ((FAIL++))
}

manual() {
  printf "${YELLOW}[WARN]${NC} %-${COL_WIDTH}s\n" "$1" | tee -a $REPORT
  log_json "$1" "MANUAL"
  ((MANUAL++))
}

# --------------------------
# FUNCTIONS
# --------------------------

check_pkg() {
  rpm -q "$1" &>/dev/null && pass "Package $1 is installed" || fail "Package $1 is missing"
}

check_service_disabled() {
  if systemctl is-enabled "$1" 2>/dev/null | grep -q 'enabled'; then
    fail "Service $1 should be disabled"
  else
    pass "Service $1 is disabled/not found"
  fi
}

check_service_enabled() {
  systemctl is-enabled "$1" &>/dev/null && pass "Service $1 is enabled" || fail "Service $1 is NOT enabled"
}

check_perm() {
  [ ! -e "$1" ] && { fail "$1 missing"; return; }
  local current=$(stat -c "%a" "$1")
  # Matches exact or more restrictive permissions
  [ "$current" -le "$2" ] && pass "$1 perm is $current (<= $2)" || fail "$1 perm is $current (expected $2)"
}

check_sysctl() {
  local val=$(sysctl -n "$1" 2>/dev/null)
  [ "$val" = "$2" ] && pass "Sysctl $1 is $2" || fail "Sysctl $1 is $val (expected $2)"
}

# --------------------------
# EXECUTION
# --------------------------

echo -e "${BLUE}Starting CIS RHEL 9 Compliance Scan...${NC}\n"

# SECTION 1: INITIAL SETUP
echo -e "${BLUE}--- SECTION 1: INITIAL SETUP ---${NC}"
check_pkg aide
check_pkg chrony
check_service_enabled chronyd
manual "Confirm System Registration (subscription-manager status)"

# SECTION 2: SERVICES
echo -e "${BLUE}--- SECTION 2: SERVICES ---${NC}"
for s in autofs avahi-daemon dhcpd dnsmasq smb vsftpd telnet tftp rpcbind nfs-server snmpd; do
  check_service_disabled "$s"
done
check_service_enabled crond

# SECTION 3: NETWORK
echo -e "${BLUE}--- SECTION 3: NETWORK ---${NC}"
check_service_disabled bluetooth
check_sysctl net.ipv4.ip_forward 0
check_sysctl net.ipv4.conf.all.send_redirects 0
check_sysctl net.ipv4.icmp_ignore_bogus_error_responses 1
check_sysctl net.ipv4.tcp_syncookies 1

# SECTION 4: FIREWALL
echo -e "${BLUE}--- SECTION 4: FIREWALL ---${NC}"
check_pkg nftables
check_service_enabled firewalld

# SECTION 5: ACCESS CONTROL
echo -e "${BLUE}--- SECTION 5: ACCESS CONTROL ---${NC}"
check_pkg sudo
SSH_CONF="/etc/ssh/sshd_config"
if [ -f "$SSH_CONF" ]; then
  check_perm "$SSH_CONF" 600
  grep -Ei "^\s*PermitRootLogin\s+no" "$SSH_CONF" >/dev/null && pass "SSH: Root login disabled" || fail "SSH: Root login enabled"
  grep -Ei "^\s*MaxAuthTries\s+[0-4]" "$SSH_CONF" >/dev/null && pass "SSH: MaxAuthTries compliant" || fail "SSH: MaxAuthTries > 4"
else
  fail "SSH config not found"
fi

# Password Policy
grep -E "^PASS_MAX_DAYS\s+[0-9]+" /etc/login.defs | awk '{if($2<=365) exit 0; else exit 1}' && pass "Pass Max Days <= 365" || fail "Pass Max Days > 365"

# SECTION 6: LOGGING
echo -e "${BLUE}--- SECTION 6: LOGGING ---${NC}"
check_pkg audit
check_service_enabled auditd
check_pkg rsyslog

# SECTION 7: SYSTEM PERMISSIONS
echo -e "${BLUE}--- SECTION 7: SYSTEM PERMISSIONS ---${NC}"
check_perm /etc/passwd 644
check_perm /etc/shadow 600
check_perm /etc/group 644
check_perm /etc/gshadow 600

# World Writable Check
WW_FILES=$(find / -xdev -type f -perm -0002 2>/dev/null | wc -l)
[ "$WW_FILES" -eq 0 ] && pass "No world-writable files" || fail "$WW_FILES world-writable files found"

# --------------------------
# FINISH
# --------------------------
echo "]" >> $JSON

echo -e "\n${BLUE}===== SCAN SUMMARY =====${NC}"
echo -e "${GREEN}PASS:   $PASS${NC}"
echo -e "${RED}FAIL:   $FAIL${NC}"
echo -e "${YELLOW}MANUAL: $MANUAL${NC}"
echo "--------------------------"
echo "Detailed Logs: $REPORT"
echo "JSON Export:   $JSON"
-------------------------------------------------------------------------------------------------------------------------
Run the script with root privileges:
$ sudo ./cis_rhel9_scan.sh
The results can then be reviewed to identify security gaps and plan remediation actions.

No comments:

Post a Comment