Pages

CIS SUSE Linux Compliance Script (Sections 1–7)

CIS SUSE Linux Compliance Script (Sections 1–7)

#!/bin/bash
# CIS SUSE Linux Compliance Script (Sections 1–7)
# Automated checks + manual flags
# -------------------------------
# Colors
# -------------------------------
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# -------------------------------
# Logging & Counters
# -------------------------------
REPORT="/var/log/cis_suse_report.log"
> $REPORT
PASS=0
FAIL=0
MANUAL=0
log_pass() { echo -e "${GREEN}[PASS]${NC} $1" | tee -a $REPORT; ((PASS++)); }
log_fail() { echo -e "${RED}[FAIL]${NC} $1" | tee -a $REPORT; ((FAIL++)); }
log_manual() { echo -e "${YELLOW}[MANUAL]${NC} $1" | tee -a $REPORT; ((MANUAL++)); }
# -------------------------------
# Helper Functions
# -------------------------------
check_file_perm() {
    local file=$1
    local perm=$2
    [ -e "$file" ] || { log_fail "$file does not exist"; return; }
    actual_perm=$(stat -c "%a" "$file")
    [ "$actual_perm" == "$perm" ] && log_pass "$file permissions ($perm) correct" || log_fail "$file permissions ($actual_perm) incorrect, should be $perm"
}
check_service_disabled() {
    local svc=$1
    systemctl is-enabled "$svc" &>/dev/null || { log_pass "$svc disabled"; return; }
    systemctl is-active "$svc" &>/dev/null && log_fail "$svc is enabled/active" || log_pass "$svc disabled"
}
check_package_installed() {
    local pkg=$1
    rpm -q "$pkg" &>/dev/null && log_pass "$pkg installed" || log_fail "$pkg not installed"
}
# -------------------------------
# SECTION 1 – Initial Setup
# -------------------------------
echo -e "${YELLOW}==== SECTION 1 – Initial Setup ====${NC}" | tee -a $REPORT
log_manual "Check SUSE registration and subscription status (SUSEConnect --status)"
# -------------------------------
# SECTION 2 – Services
# -------------------------------
echo -e "${YELLOW}==== SECTION 2 – Services ====${NC}" | tee -a $REPORT
services_to_disable=(
    autofs avahi-daemon dhcpd named dnsmasq smb nfs rpcbind rsync snmpd telnet tftp xinetd vsftpd apache2
)
for svc in "${services_to_disable[@]}"; do
    check_service_disabled "$svc"
done
systemctl is-enabled cron &>/dev/null && log_pass "cron enabled" || log_fail "cron disabled"
# -------------------------------
# SECTION 3 – Network
# -------------------------------
echo -e "${YELLOW}==== SECTION 3 – Network ====${NC}" | tee -a $REPORT
check_service_disabled "bluetooth"
nmcli radio wifi off &>/dev/null && log_pass "Wireless interfaces disabled" || log_manual "Check wireless interfaces manually"
sysctl_checks=(
    "net.ipv4.ip_forward=0"
    "net.ipv4.conf.all.send_redirects=0"
    "net.ipv4.conf.default.send_redirects=0"
    "net.ipv4.icmp_ignore_bogus_error_responses=1"
    "net.ipv4.icmp_echo_ignore_broadcasts=1"
    "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.rp_filter=1"
    "net.ipv4.conf.default.rp_filter=1"
    "net.ipv4.conf.all.accept_source_route=0"
    "net.ipv4.conf.default.accept_source_route=0"
    "net.ipv4.tcp_syncookies=1"
)
for param in "${sysctl_checks[@]}"; do
    key=$(echo $param | cut -d= -f1)
    val=$(echo $param | cut -d= -f2)
    current=$(sysctl -n "$key")
    [ "$current" == "$val" ] && log_pass "$key = $val" || log_fail "$key = $current, should be $val"
done
# -------------------------------
# SECTION 4 – Host-Based Firewall
# -------------------------------
echo -e "${YELLOW}==== SECTION 4 – Host-Based Firewall ====${NC}" | tee -a $REPORT
check_package_installed "firewalld"
systemctl is-enabled firewalld &>/dev/null && log_pass "firewalld enabled" || log_fail "firewalld disabled"
# -------------------------------
# SECTION 5 – Access Control
# -------------------------------
echo -e "${YELLOW}==== SECTION 5 – Access Control ====${NC}" | tee -a $REPORT
ssh_config="/etc/ssh/sshd_config"
check_file_perm "$ssh_config" "600"
grep -q "^PermitRootLogin no" "$ssh_config" && log_pass "SSH root login disabled" || log_fail "SSH root login not disabled"
check_package_installed "sudo"
log_manual "Verify pam_pwquality, password complexity, and PAM configuration"
awk -F: '($2!="x") {print $1}' /etc/passwd | grep -v '^$' && log_fail "Some accounts do not use shadow passwords" || log_pass "All accounts use shadowed passwords"
# -------------------------------
# SECTION 6 – Logging and Auditing
# -------------------------------
echo -e "${YELLOW}==== SECTION 6 – Logging and Auditing ====${NC}" | tee -a $REPORT
check_package_installed "audit"
systemctl is-enabled auditd &>/dev/null && log_pass "auditd enabled" || log_fail "auditd disabled"
systemctl is-enabled rsyslog &>/dev/null && log_pass "rsyslog enabled" || log_fail "rsyslog disabled"
check_package_installed "aide"
log_manual "Ensure AIDE database initialized and integrity checks are scheduled"
# -------------------------------
# SECTION 7 – System Maintenance
# -------------------------------
echo -e "${YELLOW}==== SECTION 7 – System Maintenance ====${NC}" | tee -a $REPORT
declare -A files=(
    ["/etc/passwd"]="644"
    ["/etc/passwd-"]="644"
    ["/etc/group"]="644"
    ["/etc/group-"]="644"
    ["/etc/shadow"]="600"
    ["/etc/shadow-"]="600"
    ["/etc/gshadow"]="600"
    ["/etc/gshadow-"]="600"
    ["/etc/shells"]="644"
    ["/etc/security/opasswd"]="600"
)
for file in "${!files[@]}"; do
    check_file_perm "$file" "${files[$file]}"
done
find / -xdev -type f -perm -002 ! -exec stat -c "%n" {} \; 2>/dev/null | grep -vE "^/proc|^/sys|^/dev" \
    && log_fail "World-writable files found" || log_pass "No world-writable files"
find / -xdev -type d -perm -002 ! -exec stat -c "%n" {} \; 2>/dev/null | grep -vE "^/proc|^/sys|^/dev" \
    && log_fail "World-writable directories found" || log_pass "No world-writable directories"
log_manual "Review SUID/SGID files: find / -xdev \\( -perm -4000 -o -perm -2000 \\) -type f"
awk -F: '($7!="/sbin/nologin" && $7!="/usr/sbin/nologin") {print $1":"$6}' /etc/passwd | while IFS=: read user dir; do
    [ -d "$dir" ] && log_pass "Home directory exists for $user" || log_fail "Home directory missing for $user"
done
echo -e "${YELLOW}==== CIS SUSE Linux Compliance Check Complete ====${NC}" | tee -a $REPORT
echo -e "${GREEN}PASS: $PASS${NC} | ${RED}FAIL: $FAIL${NC} | ${YELLOW}MANUAL: $MANUAL${NC}" | tee -a $REPORT

CIS SUSE Linux Auto-Remediation Script

CIS SUSE Linux Auto-Remediation:

#!/bin/bash
# =====================================================
# Enhanced Level 1 CIS SUSE Linux Auto-Remediation
# Full Auto-Remediation (Sections 1–7)
# Color-coded terminal output
# =====================================================
# -------------------------------
# Color Codes
# -------------------------------
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# -------------------------------
# Logging & Counters
# -------------------------------
REPORT="/var/log/cis_suse_auto_report.log"
> $REPORT
PASS=0; FAIL=0; REMEDIATED=0; MANUAL=0
log_pass() { echo -e "${GREEN}[PASS]${NC} $1" | tee -a $REPORT; ((PASS++)); }
log_fail() { echo -e "${RED}[FAIL]${NC} $1" | tee -a $REPORT; ((FAIL++)); }
log_manual() { echo -e "${YELLOW}[MANUAL]${NC} $1" | tee -a $REPORT; ((MANUAL++)); }
log_remediated() { echo -e "${GREEN}[REMEDIATED]${NC} $1" | tee -a $REPORT; ((REMEDIATED++)); }
# -------------------------------
# Helper Functions
# -------------------------------
check_file_perm() {
    file=$1
    perm=$2
    if [ -f "$file" ]; then
        actual_perm=$(stat -c "%a" "$file")
        if [ "$actual_perm" != "$perm" ]; then
            chmod "$perm" "$file" && log_remediated "Fixed permissions on $file to $perm" || log_fail "Failed to set permissions on $file"
        else
            log_pass "$file permissions correct ($perm)"
        fi
    else
        log_fail "$file not found"
    fi
}
disable_service() {
    svc=$1
    if systemctl list-unit-files | grep -q "^$svc"; then
        systemctl disable --now "$svc" && log_remediated "Disabled $svc" || log_fail "Failed to disable $svc"
    else
        log_pass "$svc service not installed or already disabled"
    fi
}
enable_service() {
    svc=$1
    systemctl enable --now "$svc" && log_remediated "Enabled $svc" || log_fail "Failed to enable $svc"
}
set_ssh_config() {
    param=$1
    value=$2
    SSH_CONFIG="/etc/ssh/sshd_config"
    if grep -q "^$param" "$SSH_CONFIG"; then
        sed -i "s/^$param.*/$param $value/" "$SSH_CONFIG" && log_remediated "SSH $param set to $value" || log_fail "Failed to set SSH $param"
    else
        echo "$param $value" >> "$SSH_CONFIG" && log_remediated "SSH $param added as $value"
    fi
}
set_pam_password_policy() {
    AUTH_FILE="/etc/security/pwquality.conf"
    sed -i '/^minlen/d' $AUTH_FILE
    sed -i '/^dcredit/d' $AUTH_FILE
    sed -i '/^ucredit/d' $AUTH_FILE
    sed -i '/^ocredit/d' $AUTH_FILE
    sed -i '/^lcredit/d' $AUTH_FILE
    echo "minlen = 14" >> $AUTH_FILE
    echo "dcredit = -1" >> $AUTH_FILE
    echo "ucredit = -1" >> $AUTH_FILE
    echo "ocredit = -1" >> $AUTH_FILE
    echo "lcredit = -1" >> $AUTH_FILE
    log_remediated "PAM password complexity set"
}
set_pam_lockout() {
    AUTH_FILE="/etc/pam.d/common-auth"
    if ! grep -q "pam_faillock.so" $AUTH_FILE; then
        sed -i '/auth\s\+required\s\+pam_unix.so/i auth required pam_faillock.so preauth silent deny=5 unlock_time=900' $AUTH_FILE
        sed -i '/auth\s\+required\s\+pam_unix.so/a auth [default=die] pam_faillock.so authfail deny=5 unlock_time=900' $AUTH_FILE
        sed -i '/account\s\+required\s\+pam_unix.so/i account required pam_faillock.so' $AUTH_FILE
        log_remediated "PAM account lockout configured"
    else
        log_pass "PAM faillock already configured"
    fi
}
# -------------------------------
# SECTION 1 – Initial Setup
# -------------------------------
echo -e "${YELLOW}==== SECTION 1 – Initial Setup ====${NC}" | tee -a $REPORT
log_manual "Verify SUSE version, kernel, and updates manually (cat /etc/os-release, uname -r, zypper list-updates)"
# -------------------------------
# SECTION 2 – Services
# -------------------------------
echo -e "${YELLOW}==== SECTION 2 – Services ====${NC}" | tee -a $REPORT
services_to_disable=(autofs avahi dhcpd named dnsmasq smb nfs vsftpd rpcbind rsync snmpd telnet tftp squid httpd xinetd ypbind cups)
for svc in "${services_to_disable[@]}"; do
    disable_service "$svc"
done
# -------------------------------
# SECTION 3 – Network
# -------------------------------
echo -e "${YELLOW}==== SECTION 3 – Network ====${NC}" | tee -a $REPORT
sysctl_params=(
    "net.ipv4.ip_forward=0"
    "net.ipv4.conf.all.send_redirects=0"
    "net.ipv4.conf.default.send_redirects=0"
    "net.ipv4.icmp_ignore_bogus_error_responses=1"
    "net.ipv4.icmp_echo_ignore_broadcasts=1"
    "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.rp_filter=1"
    "net.ipv4.conf.default.rp_filter=1"
    "net.ipv4.tcp_syncookies=1"
)
for p in "${sysctl_params[@]}"; do
    key=${p%=*}
    val=${p#*=}
    sysctl -w $key=$val >/dev/null 2>&1 && log_remediated "Sysctl $key set to $val" || log_fail "Failed $key=$val"
done
sysctl -p >/dev/null 2>&1
# -------------------------------
# SECTION 4 – Host-Based Firewall
# -------------------------------
echo -e "${YELLOW}==== SECTION 4 – Firewall ====${NC}" | tee -a $REPORT
enable_service "firewalld"
firewall-cmd --reload >/dev/null 2>&1 && log_remediated "Firewall reloaded" || log_fail "Failed to reload firewall"
# -------------------------------
# SECTION 5 – Access Control
# -------------------------------
echo -e "${YELLOW}==== SECTION 5 – Access Control ====${NC}" | tee -a $REPORT
# SSH hardening
set_ssh_config "PermitRootLogin" "no"
set_ssh_config "PasswordAuthentication" "no"
check_file_perm "/etc/ssh/sshd_config" "600"
systemctl restart sshd
# Sudo hardening
sed -i 's/^%wheel.*NOPASSWD/%wheel ALL=(ALL) ALL/' /etc/sudoers && log_remediated "Sudo password requirement enforced"
# PAM password policy & lockout
set_pam_password_policy
set_pam_lockout
# -------------------------------
# SECTION 6 – Logging & Auditing
# -------------------------------
echo -e "${YELLOW}==== SECTION 6 – Logging & Auditing ====${NC}" | tee -a $REPORT
enable_service "auditd"
auditctl -e 1 >/dev/null 2>&1
log_remediated "Auditd enabled and running"
# Default audit rules
cat <<EOF >/etc/audit/rules.d/cis.rules
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/gshadow -p wa -k identity
-w /etc/sudoers -p wa -k scope
-w /var/log/secure -p wa -k logins
EOF
augenrules --load && log_remediated "Audit rules loaded"
# -------------------------------
# SECTION 7 – System Maintenance
# -------------------------------
echo -e "${YELLOW}==== SECTION 7 – System Maintenance ====${NC}" | tee -a $REPORT
declare -A files=(
    ["/etc/passwd"]="644"
    ["/etc/shadow"]="600"
    ["/etc/group"]="644"
    ["/etc/gshadow"]="600"
)
for file in "${!files[@]}"; do
    check_file_perm "$file" "${files[$file]}"
done
# World-writable files
world_files=$(find / -xdev -type f -perm -2 2>/dev/null)
[ -n "$world_files" ] && log_fail "World-writable files found" || log_pass "No world-writable files"
log_manual "Review SUID/SGID files manually: find / -type f \( -perm -4000 -o -perm -2000 \) -exec ls -l {} \;"
# -------------------------------
# Final Summary
# -------------------------------
echo -e "${YELLOW}==== CIS SUSE Auto-Remediation Complete ====${NC}" | tee -a $REPORT
echo -e "${GREEN}PASS: $PASS${NC} | ${RED}FAIL: $FAIL${NC} | ${GREEN}REMEDIATED: $REMEDIATED${NC} | ${YELLOW}MANUAL: $MANUAL${NC}" | tee -a $REPORT

CIS RHEL 9 Level 1 Auto-Remediation Script

Overview:
This script performs automated security hardening on a Red Hat Enterprise Linux 9 system based on CIS Level 1 benchmark recommendations.

The goal is to enforce basic security configurations and reduce the system attack surface.

The script focuses on CIS Benchmark Sections 1 through 7 and automatically remediates common security issues such as insecure services, weak system configurations, and improper file permissions.
The script also creates backups before modifying critical configuration files to reduce the risk of system issues.

Key Features
• Root execution validation
• Automatic backup of configuration files before changes
• Automatic remediation of insecure settings
• Persistent kernel parameter configuration
• Service hardening and firewall configuration
• SSH security configuration
• Audit logging configuration
• File permission validation
• Color-coded output for easier review

Log and Backup Locations
Log File
/var/log/cis_rhel9_auto_report.log
Backup Directory
/root/cis_backup_
The backup directory stores copies of configuration files modified by the script.

Script Execution
The script must be executed with root privileges.
Script:
---------------------------------------------------------------------------------------------------------------------
#!/bin/bash
# =====================================================
# CIS RHEL 9 Level 1 Auto Remediation 
# Covers CIS Sections 1–7
# Includes safety checks, backups, and persistent config
# =====================================================

# -------------------------
# Root Check
# -------------------------
if [ "$EUID" -ne 0 ]; then
 echo "Run this script as root"
 exit 1
fi

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

# -------------------------
# Logging
# -------------------------
REPORT="/var/log/cis_rhel9_auto_report.log"
BACKUP="/root/cis_backup_$(date +%F_%H-%M)"

mkdir -p $BACKUP
> $REPORT

PASS=0
FAIL=0
REMEDIATED=0
MANUAL=0

log_pass() { echo -e "${GREEN}[PASS]${NC} $1" | tee -a $REPORT; ((PASS++)); }
log_fail() { echo -e "${RED}[FAIL]${NC} $1" | tee -a $REPORT; ((FAIL++)); }
log_manual() { echo -e "${YELLOW}[MANUAL]${NC} $1" | tee -a $REPORT; ((MANUAL++)); }
log_remediated() { echo -e "${GREEN}[REMEDIATED]${NC} $1" | tee -a $REPORT; ((REMEDIATED++)); }

backup_file() {
 file=$1
 if [ -f "$file" ]; then
   cp "$file" "$BACKUP"
 fi
}

# -------------------------
# File Permission Check
# -------------------------
check_file_perm() {

file=$1
perm=$2

if [ -f "$file" ]; then

actual=$(stat -c "%a" "$file")

if [ "$actual" != "$perm" ]; then
 chmod "$perm" "$file" && log_remediated "Fixed $file permissions to $perm" || log_fail "Failed to fix $file"
else
 log_pass "$file permissions correct"
fi

else
 log_fail "$file not found"
fi

}

# -------------------------
# Service Control
# -------------------------

disable_service() {

svc=$1

if systemctl list-unit-files | grep -q "^$svc"; then

systemctl disable --now $svc &>/dev/null \
&& log_remediated "$svc disabled" \
|| log_fail "Failed disabling $svc"

else
 log_pass "$svc not installed"
fi

}

enable_service() {

svc=$1

systemctl enable --now $svc &>/dev/null \
&& log_remediated "$svc enabled" \
|| log_fail "Failed enabling $svc"

}

# -------------------------
# SSH Configuration
# -------------------------

set_ssh_config() {

param=$1
value=$2
file="/etc/ssh/sshd_config"

backup_file $file

if grep -q "^$param" $file
then
 sed -i "s/^$param.*/$param $value/" $file
else
 echo "$param $value" >> $file
fi

log_remediated "SSH $param set to $value"

}

# -------------------------
# Sysctl Configuration
# -------------------------

SYSCTL_FILE="/etc/sysctl.d/99-cis.conf"
touch $SYSCTL_FILE

set_sysctl() {

key=$1
value=$2

if grep -q "$key" $SYSCTL_FILE
then
 sed -i "s/^$key.*/$key = $value/" $SYSCTL_FILE
else
 echo "$key = $value" >> $SYSCTL_FILE
fi

sysctl -w $key=$value >/dev/null

log_remediated "Sysctl $key set to $value"

}

# -------------------------
# SECTION 1
# Initial Setup
# -------------------------

echo "==== SECTION 1 INITIAL SETUP ====" | tee -a $REPORT

log_manual "Verify OS version and updates manually"

# -------------------------
# SECTION 2
# Services
# -------------------------

echo "==== SECTION 2 SERVICES ====" | tee -a $REPORT

services=(
telnet
tftp
avahi-daemon
cups
rpcbind
)

for s in "${services[@]}"
do
 disable_service $s
done

# -------------------------
# SECTION 3
# Network Security
# -------------------------

echo "==== SECTION 3 NETWORK ====" | tee -a $REPORT

set_sysctl net.ipv4.ip_forward 0
set_sysctl net.ipv4.conf.all.send_redirects 0
set_sysctl net.ipv4.conf.default.send_redirects 0
set_sysctl net.ipv4.icmp_echo_ignore_broadcasts 1
set_sysctl net.ipv4.icmp_ignore_bogus_error_responses 1
set_sysctl net.ipv4.conf.all.accept_redirects 0
set_sysctl net.ipv4.conf.default.accept_redirects 0
set_sysctl net.ipv4.tcp_syncookies 1

sysctl --system >/dev/null

# -------------------------
# SECTION 4
# Firewall
# -------------------------

echo "==== SECTION 4 FIREWALL ====" | tee -a $REPORT

enable_service firewalld

firewall-cmd --reload &>/dev/null \
&& log_remediated "Firewall reloaded" \
|| log_fail "Firewall reload failed"

# -------------------------
# SECTION 5
# Access Control
# -------------------------

echo "==== SECTION 5 ACCESS CONTROL ====" | tee -a $REPORT

backup_file /etc/ssh/sshd_config

if [ -f /root/.ssh/authorized_keys ]
then
 set_ssh_config PermitRootLogin no
 set_ssh_config PasswordAuthentication no
else
 log_manual "SSH keys not detected. PasswordAuthentication unchanged."
fi

check_file_perm /etc/ssh/sshd_config 600

systemctl restart sshd || log_fail "SSH restart failed"

# -------------------------
# Sudo Hardening
# -------------------------

backup_file /etc/sudoers

sed -i 's/^%wheel.*NOPASSWD/%wheel ALL=(ALL) ALL/' /etc/sudoers

visudo -c >/dev/null \
&& log_remediated "Sudoers updated and validated" \
|| log_fail "Sudoers syntax error"

# -------------------------
# SECTION 6
# Audit Logging
# -------------------------

echo "==== SECTION 6 AUDIT ====" | tee -a $REPORT

enable_service auditd

RULE_FILE="/etc/audit/rules.d/99-cis.rules"

backup_file $RULE_FILE

cat <<EOF > $RULE_FILE
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/gshadow -p wa -k identity
-w /etc/sudoers -p wa -k scope
-w /var/log/secure -p wa -k logins
EOF

augenrules --load && log_remediated "Audit rules loaded"

# -------------------------
# SECTION 7
# File Permissions
# -------------------------

echo "==== SECTION 7 FILE SECURITY ====" | tee -a $REPORT

check_file_perm /etc/passwd 644
check_file_perm /etc/shadow 600
check_file_perm /etc/group 644
check_file_perm /etc/gshadow 600

if find / -xdev -type f -perm -0002 -print -quit | grep .
then
 log_fail "World writable files detected"
else
 log_pass "No world writable files"
fi

log_manual "Review SUID files manually"

# -------------------------
# SUMMARY
# -------------------------

echo
echo "===== CIS REMEDIATION COMPLETE =====" | tee -a $REPORT
echo "PASS: $PASS" | tee -a $REPORT
echo "FAIL: $FAIL" | tee -a $REPORT
echo "REMEDIATED: $REMEDIATED" | tee -a $REPORT
echo "MANUAL: $MANUAL" | tee -a $REPORT

echo
echo "Backup location: $BACKUP"
echo "Report: $REPORT"
---------------------------------------------------------------------------------------------------------------------
Example:
sudo ./cis_rhel9_hardening.sh
During execution, the script prints status messages and writes results to the log file.

Status types include:
PASS – configuration already compliant
FAIL – configuration check failed
REMEDIATED – configuration automatically corrected
MANUAL – manual verification required

The script runs sequential checks and remediation steps across seven CIS benchmark areas.

Section Details:

Section 1 – Initial Setup
This section performs basic verification tasks.
Some checks require manual review because they depend on environment-specific configuration.
Example checks include:
• Operating system version verification
• Kernel version verification
• System update status
These checks are flagged for manual review.

Section 2 – Service Hardening
Unnecessary or insecure services are disabled to reduce the system attack surface.
The following services are disabled if installed:
telnet
tftp
avahi-daemon
cups
rpcbind
The script uses systemctl to stop and disable services immediately.

Section 3 – Network Security
Kernel networking parameters are configured to improve system security.
The script writes persistent configuration to:
/etc/sysctl.d/99-cis.conf
The following parameters are configured:
net.ipv4.ip_forward = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.tcp_syncookies = 1
After configuration, the script reloads system kernel parameters using:
sysctl --system

Section 4 – Firewall Configuration
The script ensures that the system firewall is enabled and active.
Actions performed:
• Enable firewalld service
• Start firewalld
• Reload firewall configuration
Commands used:
systemctl enable --now firewalld
firewall-cmd --reload

Section 5 – Access Control and SSH Hardening
This section applies multiple access control improvements.
SSH Security
The script modifies the SSH configuration file:
/etc/ssh/sshd_config
Settings enforced:
PermitRootLogin no
PasswordAuthentication no
Before disabling password authentication, the script verifies that SSH key authentication exists for the root account to prevent accidental lockout.
File permissions for sshd_config are also validated and corrected.
After changes, the SSH service is restarted.
Sudo Hardening
The script updates the sudo configuration to ensure users in the wheel group must provide a password when executing privileged commands.
The configuration file modified:
/etc/sudoers
The script validates the configuration using:
visudo -c
This prevents syntax errors that could break sudo access.

Section 6 – Logging and Auditing
The script enables system auditing using auditd.
Actions performed:
• Enable and start auditd service
• Configure audit rules
• Load rules using augenrules
Audit rule file:
/etc/audit/rules.d/99-cis.rules
The following files are monitored for changes:
/etc/passwd
/etc/shadow
/etc/group
/etc/gshadow
/etc/sudoers
/var/log/secure
Monitoring these files helps detect unauthorized modifications to authentication and logging systems.

Section 7 – File Permission Security
Critical system files must have secure permissions.
The script validates and corrects permissions for the following files:
/etc/passwd – 644
/etc/shadow – 600
/etc/group – 644
/etc/gshadow – 600

World Writable File Check:
The script scans the system for world-writable files that may pose a security risk.
Command used:
find / -xdev -type f -perm -0002 -print -quit
If such files are found, the script reports them for review.
SUID/SGID Review
The script flags SUID and SGID files for manual inspection because these files allow elevated privilege execution.
Example review command:
find / -type f ( -perm -4000 -o -perm -2000 )

Script Summary:
At the end of execution, the script displays a summary showing the number of checks in each category.
Example output:
  • PASS – configurations already compliant
  • FAIL – issues that require attention
  • REMEDIATED – issues automatically corrected
  • MANUAL – items that require administrator review
The script also displays the location of:
• the backup directory
• the execution log file

Operational Use Cases
This script can be used for:
• Initial Linux server hardening
• CIS Level 1 compliance preparation
• Security baseline enforcement
• Security audits and compliance verification
• Automated system configuration in provisioning workflows

Limitations:
Not all CIS controls can be safely automated.
Some security configurations depend on application requirements or environment-specific policies.
Administrators should always review the log file and verify system functionality after running the script.

Post-Execution Recommendation:
After running the script, administrators should:
• Review the log file for failures
• Validate SSH access
• Confirm application services are operating normally
• Review world-writable files if reported
• Perform SUID/SGID audit checks

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.

Running IBM AIX 7.2 on x86 with QEMU POWER8 Emulation – Lab Setup Guide

This document describes the procedure to deploy IBM AIX 7.2 TL04 SP02 (7200-04-02) using QEMU POWER8 emulation on an Ubuntu Server virtual machine running inside VMware Workstation.

This environment enables administrators and engineers to build a functional AIX lab environment on x86 hardware without requiring IBM Power Systems.

The lab environment can be used for:
  • AIX system administration practice
  • Software testing and compatibility validation
  • Networking and storage configuration experiments
  • OpenSSH configuration and security testing
  • Development and educational purposes
Since the host platform is x86, QEMU uses TCG (Tiny Code Generator) for software emulation of the POWER architecture.

2. Prerequisites
2.1 Hardware Requirements
Component                       Requirement
CPU Intel                      VT-x/EPT or AMD-V/RVI
RAM Minimum                    16 GB recommended
Storage                            Minimum 60 GB free space
Hypervisor                    VMware Workstation Pro or Player

Important Notes
VT-x / AMD-V is required for Ubuntu VM performance, not for POWER acceleration.
AIX will run using QEMU TCG software emulation.

3. Create Ubuntu Server Virtual Machine
3.1 Create VM in VMware Workstation
Open VMware Workstation
Select Create New Virtual Machine
Configuration:
Setting                                                     Value
Guest OS                       Linux
Version                             Ubuntu 64-bit
ISO Image                       ubuntu-24.04.4-live-server-amd64.iso
Memory                              8–16 GB
CPU Minimum                      4 cores
Disk                              100 GB recommended
Network                             Bridged (recommended) or NAT

3.2 Enable CPU Virtualization
Navigate to:
VM → Settings → Processors
Enable:
Virtualize Intel VT-x/EPT or AMD-V/RVI
Virtualize CPU performance counters (optional)
Ensure hardware virtualization is enabled in system BIOS.

4. Install Ubuntu Server
Download Ubuntu Server ISO:
https://ubuntu.com/download/server
File:
ubuntu-24.04.4-live-server-amd64.iso
Installation Steps
4.1 Boot VM using the ISO.
4.2 Select:
Try or Install Ubuntu Server
4.3 Installation sequence:
  • Language → English
  • Ubuntu Server Installation
  • Configure Network
  • Configure Storage (LVM recommended)
  • Create User ID and Password
  • Enable OpenSSH Server
  • Start Installation
After installation completes, log in and update the system.
System Update :
$ sudo apt update
$ sudo apt upgrade -y

5. Install QEMU for POWER Architecture
Install QEMU packages required for POWER emulation.
$ sudo apt update
$ sudo apt install qemu-system-ppc qemu-utils
Verify Installation
# qemu-system-ppc64 --version
Example output:
QEMU emulator version 8.2.2 (Debian 1:8.2.2+ds-0ubuntu1.12)
Copyright (c) 2003-2023 Fabrice Bellard and the QEMU Project developers

6. Configure Br0 & Br1 for AIX VM to connect Private Network
# vi 
/etc/netplan/50-cloud-init.yaml
Example:
network:
  version: 2
  renderer: networkd
  ethernets:
    ens160:
      dhcp4: no
    ens192:
      dhcp4: no
  bridges:
    br0:
      interfaces: [ens160]
      addresses: [192.168.10.108/24]
      gateway4: 192.168.10.1
      nameservers:
        addresses: [192.168.10.100]
        search: [ppc.com]
      parameters:
        stp: false
        forward-delay: 0
    br1:
      interfaces: [ens192]
      addresses: [192.168.20.108/24]
      gateway4: 192.168.20.1
      nameservers:
        addresses: [192.168.20.100]
        search: [ppc.com]
      parameters:
        stp: false
        forward-delay: 0

Apply the change 
# netplan apply

Note: If you are using VMware ESXI then make sure below setting need to be enable security level:


7. AIX Disk Template Preparation
Install the AIX 7.2 TL04 SP02 server.
adminCtrlX – Simplifying System Administration: Install QEMU PPC64 on Ubuntu 24.04.4 & Deploy IBM AIX 7.2 TL04 SP02

After Installation AIX 7200-04-02 then Setup .profile 
# vi .profile
Example:
indaixwpr01 / # cat .profile
# Root Profile
export HOST=$(uname -n)
# Terminal & Input
export TERM=vt100
stty erase ^H
stty erase ^?
# Path & Libraries
export PATH=/usr/bin:/usr/sbin:/etc:/usr/local/bin:/usr/bin/X11:$HOME/bin:.
export LIBPATH=/usr/lib:/lib:/usr/local/lib
# Shell Behavior
export PS1="$HOST \$PWD # "
export HISTSIZE=5000
export TMOUT=1800      # 30 minute timeout
export EDITOR=vi
export FCEDIT=vi
# System Limits
ulimit -f unlimited
ulimit -d unlimited
# Localization
export LANG=en_US
export LC_ALL=en_US
indaixwpr01 / #

Error:
error: db4 error(22) from dbenv->open: A system call received a parameter that is not valid.
error: cannot open Packages index using db4 - A system call received a parameter that is not valid. (22)
error: cannot open Packages database in /opt/freeware/packages
error: db4 error(22) from dbenv->open: A system call received a parameter that is not valid.
error: cannot open Packages index using db4 - A system call received a parameter that is not valid. (22)
error: cannot open Packages database in /opt/freeware/packages

To fix:
# rm /var/lib/rpm/__db*
# rpm --rebuilddb

Install sudo rpm package 
# rpm -Uvh sudo-1.9.17-3.aix71.rpm

smitty tcpip --->  Minimum Configuration & Startup

indaixwpr01 / # ifconfig -a
en0: flags=e084863,14c0<UP,BROADCAST,NOTRAILERS,RUNNING,SIMPLEX,MULTICAST,GROUPRT,64BIT,LARGESEND,CHAIN>
        inet 192.168.20.13 netmask 0xffffff00 broadcast 192.168.20.255
         tcp_sendspace 262144 tcp_recvspace 262144 rfc1323 1
en1: flags=e084863,14c0<UP,BROADCAST,NOTRAILERS,RUNNING,SIMPLEX,MULTICAST,GROUPRT,64BIT,LARGESEND,CHAIN>
        inet 192.168.10.13 netmask 0xffffff00 broadcast 192.168.10.255
         tcp_sendspace 262144 tcp_recvspace 262144 rfc1323 1
lo0: flags=e08084b,c0<UP,BROADCAST,LOOPBACK,RUNNING,SIMPLEX,MULTICAST,GROUPRT,64BIT,LARGESEND,CHAIN>
        inet 127.0.0.1 netmask 0xff000000 broadcast 127.255.255.255
        inet6 ::1%1/64
         tcp_sendspace 131072 tcp_recvspace 131072 rfc1323 1
indaixwpr01 / #

Shutdown AIX VM:
# shutdown -h

Destination directory:
/aix/image
Example template file: which is the part of the AIX installation boot disk:
aix_7200-04.qcow2

8. Disk Preparation Script
Create the script:
# vi prepare-disks.sh
Purpose:
  • Convert the AIX disk to template disk to RAW format
  • Create additional data disks for each VM
Script
------------------------------------------------------------------------------------------------------------------
#!/usr/bin/env bash
set -euo pipefail

BASE_IMG="/aix/image/aix_7200-04.qcow2"
DISK_DIR="/aix/disk"

if [ $# -lt 1 ]; then
    echo "Usage: $0 vmname1 [vmname2 vmname3 ...]"
    exit 1
fi

echo "Preparing disks for: $@"
echo

for SERVER in "$@"; do

    BOOT_DISK="${DISK_DIR}/${SERVER}-hdisk1.raw"
    DATA1_DISK="${DISK_DIR}/${SERVER}-hdisk2.raw"
    DATA2_DISK="${DISK_DIR}/${SERVER}-hdisk3.raw"

    echo "---- $SERVER ----"

    if [ ! -f "$BOOT_DISK" ]; then
        echo "Creating boot disk..."
        qemu-img convert -p -f qcow2 -O raw "$BASE_IMG" "$BOOT_DISK"
    fi

    if [ ! -f "$DATA1_DISK" ]; then
        echo "Creating hdisk2 (30GB)"
        qemu-img create -f raw "$DATA1_DISK" 30G
    fi

    if [ ! -f "$DATA2_DISK" ]; then
        echo "Creating hdisk3 (40GB)"
        qemu-img create -f raw "$DATA2_DISK" 40G
    fi

done

echo "Disk preparation completed."
------------------------------------------------------------------------------------------------------------------
8.1 Run Disk Preparation
Example:
# ./prepare-disks.sh indaixwpr01

Example output:
/aix/disk/indaixwpr01-hdisk1.raw
/aix/disk/indaixwpr01-hdisk2.raw
/aix/disk/indaixwpr01-hdisk3.raw

Verify disks:
ls -lh /aix/disk
Example:
-rw-r--r-- indaixwpr01-hdisk1.raw 30G
-rw-r--r-- indaixwpr01-hdisk2.raw 30G
-rw-r--r-- indaixwpr01-hdisk3.raw 40G

9. Start AIX Virtual Machine
This script:
  • Creates TAP network interfaces
  • Assigns bridge interfaces
  • Allocates memory and CPU
  • Starts QEMU POWER8 virtual machine
  • Exposes console via Telnet port
VM console ports will be allocated between:
8001 – 8100

Example command:
# ./start-vm.sh indaixwpr01
=================================
Starting VM: indaixwpr01
TAP0 (br0): tap0
TAP1 (br1): tap1
Telnet port: 8001
=================================
All requested VMs started successfully.

Create script:
# vi start-vm.sh
------------------------------------------------------------------------------------------------------------------
#!/usr/bin/env bash
set -euo pipefail

DISK_DIR="/aix/disk"
BRIDGE0="br0"
BRIDGE1="br1"
MEM="4G"          # Consider increasing to 8G for better performance
CPU="2"           # Can increase to match host cores
BASE_PORT=8001
LOG_DIR="/tmp"

if [ $# -lt 1 ]; then
    echo "Usage: $0 vmname1 [vmname2 vmname3 ...]"
    exit 1
fi

# ---------------- Functions -------------------

is_port_free() {
    local port=$1
    if ! lsof -i :${port} >/dev/null 2>&1; then
        if ! netstat -tln 2>/dev/null | grep -q ":${port} "; then
            return 0
        fi
    fi
    return 1
}

get_free_port() {
    local port=${BASE_PORT}
    while [ $port -le 8100 ]; do
        if is_port_free $port; then
            echo $port
            return 0
        fi
        port=$((port + 1))
    done
    echo "ERROR: No free ports available (8001-8100)" >&2
    exit 1
}

get_free_tap() {
    local index=0
    while [ $index -lt 200 ]; do
        local tap_name="tap${index}"

        if ! ip link show "${tap_name}" >/dev/null 2>&1; then
            ip tuntap add mode tap "${tap_name}" user "$(whoami)"
            ip link set "${tap_name}" up
            echo "${tap_name}"
            return 0
        fi

        index=$((index + 1))
    done

    echo "ERROR: No free TAP interfaces found (tap0-tap199)" >&2
    exit 1
}

generate_mac() {
    printf '52:54:00:%02X:%02X:%02X' $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256))
}

# ---------------- MAIN LOOP -------------------

for SERVER in "$@"; do

    if [ -f "${LOG_DIR}/${SERVER}.pid" ]; then
        OLD_PID=$(cat "${LOG_DIR}/${SERVER}.pid")
        if ps -p "${OLD_PID}" >/dev/null 2>&1; then
            echo "Stopping existing VM ${SERVER} (PID ${OLD_PID})"
            kill -9 "${OLD_PID}" || true
            sleep 1
        fi
        rm -f "${LOG_DIR}/${SERVER}.pid"
    fi

    TELNET_PORT=$(get_free_port)
    TAP0=$(get_free_tap)
    TAP1=$(get_free_tap)

    MAC0=$(generate_mac)
    MAC1=$(generate_mac)

    # ---- RAW disks ----
    BOOT_DISK="${DISK_DIR}/${SERVER}-hdisk1.raw"
    DATA1_DISK="${DISK_DIR}/${SERVER}-hdisk2.raw"
    DATA2_DISK="${DISK_DIR}/${SERVER}-hdisk3.raw"

    if [ ! -f "$BOOT_DISK" ]; then
        echo "Boot disk not found for ${SERVER}"
        echo "Run prepare-disks.sh first."
        exit 1
    fi

    echo "================================="
    echo "Starting VM: $SERVER"
    echo "TAP0 (br0): $TAP0"
    echo "TAP1 (br1): $TAP1"
    echo "Telnet port: $TELNET_PORT"
    echo "================================="

    ip link set "${TAP0}" master "${BRIDGE0}"
    ip link set "${TAP1}" master "${BRIDGE1}"

    UUID=$(uuidgen)

    nohup qemu-system-ppc64 \
      -name "${SERVER}" \
      -cpu POWER8 \
      -uuid "${UUID}" \
      -machine pseries,ic-mode=xics,memory-backend=ram0,cap-cfpc=broken,cap-ibs=broken,cap-ccf-assist=off,cap-sbbc=broken \
      -accel tcg,thread=multi,tb-size=4096 \
      -object memory-backend-ram,id=ram0,size=${MEM},prealloc=yes \
      -m ${MEM} \
      -smp ${CPU},cores=${CPU},threads=1,sockets=1 \
      -serial "telnet:127.0.0.1:${TELNET_PORT},server,nowait" \
      -display none \
      -device virtio-scsi-pci,id=scsi0 \
      -drive file="${BOOT_DISK}",if=none,id=drive-boot,format=raw,cache=writeback,aio=threads \
      -device scsi-hd,drive=drive-boot,bus=scsi0.0,scsi-id=0,bootindex=0 \
      -drive file="${DATA1_DISK}",if=none,id=drive-data1,format=raw,cache=writeback,aio=threads \
      -device scsi-hd,drive=drive-data1,bus=scsi0.0,scsi-id=1 \
      -drive file="${DATA2_DISK}",if=none,id=drive-data2,format=raw,cache=writeback,aio=threads \
      -device scsi-hd,drive=drive-data2,bus=scsi0.0,scsi-id=2 \
      -netdev tap,id=net${SERVER}0,ifname=${TAP0},script=no,downscript=no \
      -device spapr-vlan,netdev=net${SERVER}0,mac=${MAC0} \
      -netdev tap,id=net${SERVER}1,ifname=${TAP1},script=no,downscript=no \
      -device spapr-vlan,netdev=net${SERVER}1,mac=${MAC1} \
      -prom-env boot-command="boot disk:" \
      -prom-env input-device=vty \
      -prom-env output-device=vty \
      -prom-env term-type=vt100 \
      -prom-env use-nvramrc?=true \
      > "${LOG_DIR}/${SERVER}.log" 2>&1 &

    PID=$!
    echo "${PID}" > "${LOG_DIR}/${SERVER}.pid"
    sleep 1
done
echo "All requested VMs started successfully."

------------------------------------------------------------------------------------------------------------------

10. Verify Running VM
Check QEMU process:
# ps -ef | grep ppc
Example:
qemu-system-ppc64 -name indaixwpr01

11. Access AIX Console
Connect to VM console using Telnet.
Example:
telnet localhost 8001
This connects to the AIX system console.

12. Stop AIX Virtual Machine
Purpose:
Stop QEMU process
Remove TAP interfaces
Clean PID and log files
Example usage:
# ./stop-vm.sh indaixwpr01
Create script:
# vi stop-vm.sh
------------------------------------------------------------------------------------------------------------------
#!/usr/bin/env bash
set -euo pipefail

LOG_DIR="/tmp"

if [ $# -lt 1 ]; then
    echo "Usage: $0 vmname1 [vmname2 vmname3 ...]"
    exit 1
fi

for SERVER in "$@"; do
    PID_FILE="${LOG_DIR}/${SERVER}.pid"
    LOG_FILE="${LOG_DIR}/${SERVER}.log"

    echo "================================="
    echo "Stopping VM: $SERVER"
    echo "================================="

    # 1. Kill QEMU process
    if [ -f "$PID_FILE" ]; then
        PID=$(cat "$PID_FILE")
        if kill -0 "$PID" 2>/dev/null; then
            echo "Killing PID $PID (SIGTERM)..."
            kill "$PID"
            sleep 3
            if kill -0 "$PID" 2>/dev/null; then
                echo "Force kill PID $PID (SIGKILL)..."
                kill -9 "$PID"
            fi
        fi
        rm -f "$PID_FILE"
    fi

    # Fallback kill by name
    pkill -f "qemu-system-ppc64.*-name ${SERVER}" 2>/dev/null || true
    sleep 2

    # 2. **FIND EXACT TAP used by this VM from LOG FILE** (MOST RELIABLE)
    VM_TAP=""
    if [ -f "$LOG_FILE" ]; then
        VM_TAP=$(grep -o "TAP: tap[0-9]*" "$LOG_FILE" 2>/dev/null | tail -1 | cut -d' ' -f2 || true)
    fi

    # 3. **FIND TAP from /proc filesystem** (if log missing)
    if [ -z "$VM_TAP" ]; then
        VM_TAP=$(pgrep -f "${SERVER}" || true | \
                 xargs -r cat /proc/*/cmdline 2>/dev/null | \
                 grep -o "ifname=tap[0-9]*" | cut -d= -f2 | head -1 || true)
    fi

    # 4. **Delete ONLY this specific TAP**
    if [ -n "$VM_TAP" ] && ip link show "$VM_TAP" >/dev/null 2>&1; then
        echo "Deleting VM's TAP interface: $VM_TAP"
        ip link set "$VM_TAP" down 2>/dev/null || true
        sleep 0.5
        ip link delete "$VM_TAP" 2>/dev/null || true

        # Verify deletion
        if ! ip link show "$VM_TAP" >/dev/null 2>&1; then
            echo "TAP $VM_TAP deleted successfully"
        else
            echo "TAP $VM_TAP still exists"
        fi
    else
        echo "No TAP interface found for $SERVER"
    fi

    # Final verification
    if ! pgrep -f "qemu-system-ppc64.*-name ${SERVER}" >/dev/null 2>&1; then
        echo "VM $SERVER fully stopped....................................."
    else
        echo "VM $SERVER still running....................................."
    fi
    echo "Log: $LOG_FILE"
    echo ""
done
echo "Stop completed."
------------------------------------------------------------------------------------------------------------------

13. Manual TAP Interface Cleanup
If TAP interfaces remain after VM shutdown, remove them manually.
Example:
for i in tap0 tap1 tap2 tap3
do
sudo ip link delete $i
done

14. Log Files
VM logs and PID files are stored in:
/tmp

Example files:
/tmp/indaixwpr01.log
/tmp/indaixwpr01.pid

15. Performance Recommendations
Parameter            Recommended Value
Memory                 8 GB
CPU                    4 cores
Disk format          RAW
Networking          Bridged

Running multiple AIX VMs requires additional system memory.

Conclusion:
Using QEMU POWER8 emulation on Ubuntu Server, it is possible to deploy and operate IBM AIX 7.2 environments on x86 hardware.
This setup provides a flexible platform for:
  • AIX system administration practice
  • Software development and testing
  • Training and laboratory environments
Although performance is limited due to software emulation, it provides a reliable environment for functional testing and learning purposes.