Changelog

DateChange
2026-05-04Initial Version: Full audit script with comprehensive explanations.

Introduction to the Server Security Audit Script

When you provision a new server, especially in a cloud environment using tools like cloud-init, itโ€™s crucial to verify that all intended security measures have been correctly applied. Manual checks can be time-consuming and error-prone. This Bash script automates a significant portion of this auditing process, providing a quick overview of your serverโ€™s security posture on Debian or Ubuntu-based systems.

This script is designed to be run with root privileges and will:

  • Check System Fundamentals: Hostname, timezone, kernel, pending updates, and reboot status.
  • Audit User & Authentication: Verify the existence and sudo privileges of a dedicated admin user, check root password status, and identify accounts with UID 0 or empty passwords.
  • Harden SSH: Scrutinize your SSH daemonโ€™s configuration for best practices like disabled root login, password authentication, X11 forwarding, and the use of strong key algorithms. It also checks for the presence of SSH keys for your admin user.
  • Firewall Configuration (UFW): Confirm UFWโ€™s active status, default policies, specific rule sets (e.g., rate-limiting for SSH), and log redirection.
  • Intrusion Prevention (Fail2Ban): Validate that Fail2Ban is running, its SSH jail is active, and itโ€™s monitoring the correct SSH port.
  • Automated Updates: Ensure unattended-upgrades is installed and configured to keep your system patched.
  • Docker Security: If Docker is installed, it checks daemon status, Docker Compose presence, user group membership, and critically, Dockerโ€™s log rotation settings and network configuration.
  • Log Rotation: Verify persistent journaling and logrotate configurations for Traefik and UFW logs.
  • Network & Open Ports: List actively listening ports and check for known insecure legacy services.
  • Filesystem Security: Scan for world-writable files in critical directories and report disk usage.

At the end, it provides a summary of PASS, WARN, and FAIL counts, giving you an immediate understanding of areas needing attention.

Why is this important? Even with cloud-init or automated provisioning, misconfigurations can occur. This script acts as your second line of defense, ensuring that your server adheres to a baseline of security best practices before it goes into production.


How to Use the Script

  1. Save the script: Copy the entire script content into a file, for example, server-audit.sh.
  2. Make it executable:
    chmod +x server-audit.sh
  3. Run with sudo: The script requires root privileges to access system configurations and logs.
    sudo ./server-audit.sh
  4. Review the output: Pay close attention to [FAIL] and [WARN] messages, which indicate potential security vulnerabilities or areas for improvement.

The Server Security Audit Script

Here is the complete script. You can customize the ADMIN_USER variable at the beginning to match your dedicated administrative username.

#!/bin/bash
# =============================================================================
# Server Security Audit Script
# =============================================================================
# Checks if the cloud-init setup has been correctly applied and if current
# security standards are met.
#
# Usage: sudo bash server-audit.sh
# =============================================================================

set -u

# --- Colors & Symbols --------------------------------------------------------
GREEN="\033[0;32m"
RED="\033[0;31m"
YELLOW="\033[0;33m"
CYAN="\033[0;36m"
BOLD="\033[1m"
NC="\033[0m"

PASS="${GREEN}PASS${NC}"
FAIL="${RED}FAIL${NC}"
WARN="${YELLOW}WARN${NC}"
INFO="${CYAN}INFO${NC}"

pass_count=0
fail_count=0
warn_count=0

log_pass() { echo -e "  [${PASS}]  $1"; ((pass_count++)); }
log_fail() { echo -e "  [${FAIL}]  $1"; ((fail_count++)); }
log_warn() { echo -e "  [${WARN}]  $1"; ((warn_count++)); }
log_info() { echo -e "  [${INFO}]  $1"; }

section() { echo -e "\n${BOLD}=== $1 ===${NC}"; }

# --- Root Check --------------------------------------------------------------
if [[ $EUID -ne 0 ]]; then
    echo -e "${RED}This script must be run with sudo.${NC}"
    exit 1
fi

ADMIN_USER="delightfuldude" # <--- CUSTOMIZE THIS TO YOUR ADMIN USERNAME

echo -e "${BOLD}"
echo "============================================================"
echo "  Server Security Audit - $(date '+%Y-%m-%d %H:%M:%S')"
echo "============================================================"
echo -e "${NC}"

# =============================================================================
# 1. SYSTEM FUNDAMENTALS
# =============================================================================
section "1. System Fundamentals"

# Hostname
CURRENT_HOSTNAME=$(hostnamectl --static 2>/dev/null || hostname)
if [[ "$CURRENT_HOSTNAME" != "localhost" && -n "$CURRENT_HOSTNAME" ]]; then
    log_pass "Hostname set: ${CURRENT_HOSTNAME}"
else
    log_warn "Hostname is still on default"
fi

# Timezone
CURRENT_TZ=$(timedatectl show -p Timezone --value 2>/dev/null || cat /etc/timezone 2>/dev/null || echo "unknown")
if [[ "$CURRENT_TZ" == "Europe/Berlin" ]]; then
    log_pass "Timezone: ${CURRENT_TZ}"
else
    log_warn "Timezone is ${CURRENT_TZ} (expected: Europe/Berlin)"
fi

# Kernel & OS
log_info "OS: $(grep PRETTY_NAME /etc/os-release 2>/dev/null | cut -d= -f2 | tr -d '"')"
log_info "Kernel: $(uname -r)"

# Uptime
log_info "Uptime: $(uptime -p)"

# Pending Updates
UPDATES=$(apt list --upgradable 2>/dev/null | grep -c upgradable || true)
if [[ "$UPDATES" -eq 0 ]]; then
    log_pass "No pending package updates"
else
    log_warn "${UPDATES} package update(s) available"
fi

# Reboot required?
if [[ -f /var/run/reboot-required ]]; then
    log_warn "System reboot required"
else
    log_pass "No reboot required"
fi

# =============================================================================
# 2. USER & AUTHENTICATION
# =============================================================================
section "2. User & Authentication"

# Admin user exists
if id "$ADMIN_USER" &>/dev/null; then
    log_pass "User '${ADMIN_USER}' exists"
else
    log_fail "User '${ADMIN_USER}' does NOT exist"
fi

# sudo group
if groups "$ADMIN_USER" 2>/dev/null | grep -qw sudo; then
    log_pass "'${ADMIN_USER}' is in the sudo group"
else
    log_fail "'${ADMIN_USER}' is NOT in the sudo group"
fi

# NOPASSWD in sudoers (accepted for cloud-init without password)
if grep -r "NOPASSWD" /etc/sudoers /etc/sudoers.d/ 2>/dev/null | grep -v "^#" | grep -q "$ADMIN_USER"; then
    log_info "'${ADMIN_USER}' has NOPASSWD (intended, as cloud-init doesn't set a password)"
else
    log_pass "No NOPASSWD for '${ADMIN_USER}' - sudo requires password"
fi

# Root password (should be locked or random, not empty)
ROOT_PW_STATUS=$(passwd -S root 2>/dev/null | awk '{print $2}')
case "$ROOT_PW_STATUS" in
    L)  log_pass "Root account is locked" ;;
    P)  log_info "Root has a password set (randomized by cloud-init)" ;;
    NP) log_fail "Root has NO password!" ;;
    *)  log_info "Root password status: ${ROOT_PW_STATUS}" ;;
esac

# Accounts with empty password
EMPTY_PW=$(awk -F: '($2 == "" ) { print $1 }' /etc/shadow 2>/dev/null || true)
if [[ -z "$EMPTY_PW" ]]; then
    log_pass "No accounts with empty password"
else
    log_fail "Accounts with empty password: ${EMPTY_PW}"
fi

# UID-0 accounts (only root should have UID 0)
ROOT_ACCOUNTS=$(awk -F: '($3 == 0) { print $1 }' /etc/passwd)
if [[ "$ROOT_ACCOUNTS" == "root" ]]; then
    log_pass "Only 'root' has UID 0"
else
    log_fail "Multiple accounts with UID 0: ${ROOT_ACCOUNTS}"
fi

# =============================================================================
# 3. SSH HARDENING
# =============================================================================
section "3. SSH Hardening"

# Read effective SSH configuration
SSHD_CONFIG=$(sshd -T 2>/dev/null || true)

if [[ -z "$SSHD_CONFIG" ]]; then
    log_fail "Cannot read SSH configuration (sshd -T failed)"
else
    # Port
    SSH_PORT=$(echo "$SSHD_CONFIG" | grep "^port " | awk '{print $2}')
    if [[ "$SSH_PORT" != "22" ]]; then
        log_pass "SSH port changed: ${SSH_PORT}"
    else
        log_warn "SSH is running on default port 22"
    fi

    # Root Login
    ROOT_LOGIN=$(echo "$SSHD_CONFIG" | grep "^permitrootlogin " | awk '{print $2}')
    if [[ "$ROOT_LOGIN" == "no" ]]; then
        log_pass "Root login disabled"
    else
        log_fail "Root login is '${ROOT_LOGIN}' (should be 'no')"
    fi

    # Password Auth
    PW_AUTH=$(echo "$SSHD_CONFIG" | grep "^passwordauthentication " | awk '{print $2}')
    if [[ "$PW_AUTH" == "no" ]]; then
        log_pass "Password authentication disabled"
    else
        log_fail "Password authentication is enabled!"
    fi

    # Pubkey Auth
    PUBKEY_AUTH=$(echo "$SSHD_CONFIG" | grep "^pubkeyauthentication " | awk '{print $2}')
    if [[ "$PUBKEY_AUTH" == "yes" ]]; then
        log_pass "Public-key authentication enabled"
    else
        log_fail "Public-key authentication is disabled!"
    fi

    # MaxAuthTries
    MAX_AUTH=$(echo "$SSHD_CONFIG" | grep "^maxauthtries " | awk '{print $2}')
    if [[ "$MAX_AUTH" -le 3 ]]; then
        log_pass "MaxAuthTries: ${MAX_AUTH}"
    else
        log_warn "MaxAuthTries is ${MAX_AUTH} (recommended: <=3)"
    fi

    # LoginGraceTime
    GRACE_TIME=$(echo "$SSHD_CONFIG" | grep "^logingracetime " | awk '{print $2}')
    if [[ "$GRACE_TIME" -le 60 ]]; then
        log_pass "LoginGraceTime: ${GRACE_TIME}s"
    else
        log_warn "LoginGraceTime is ${GRACE_TIME}s (recommended: <=60)"
    fi

    # X11Forwarding
    X11=$(echo "$SSHD_CONFIG" | grep "^x11forwarding " | awk '{print $2}')
    if [[ "$X11" == "no" ]]; then
        log_pass "X11Forwarding disabled"
    else
        log_warn "X11Forwarding is enabled"
    fi

    # TCP Forwarding
    TCP_FWD=$(echo "$SSHD_CONFIG" | grep "^allowtcpforwarding " | awk '{print $2}')
    if [[ "$TCP_FWD" == "no" ]]; then
        log_pass "TCP-Forwarding disabled"
    else
        log_warn "TCP-Forwarding is enabled"
    fi

    # Agent Forwarding
    AGENT_FWD=$(echo "$SSHD_CONFIG" | grep "^allowagentforwarding " | awk '{print $2}')
    if [[ "$AGENT_FWD" == "no" ]]; then
        log_pass "Agent-Forwarding disabled"
    else
        log_warn "Agent-Forwarding is enabled"
    fi

    # AllowUsers set
    ALLOW_USERS=$(echo "$SSHD_CONFIG" | grep "^allowusers " | awk '{$1=""; print $0}' | xargs)
    if [[ -n "$ALLOW_USERS" ]]; then
        log_pass "AllowUsers restricted to: ${ALLOW_USERS}"
    else
        log_warn "AllowUsers is not set (all users can use SSH)"
    fi

    # Check if insecure Key Algorithms are accepted
    ACCEPTED_ALGOS=$(echo "$SSHD_CONFIG" | grep "^pubkeyacceptedalgorithms " | awk '{$1=""; print $0}' | xargs)
    if [[ -n "$ACCEPTED_ALGOS" ]]; then
        if echo "$ACCEPTED_ALGOS" | grep -qw "ssh-rsa"; then
            log_fail "ssh-rsa (SHA-1) is still allowed - use rsa-sha2-* only!"
        else
            log_pass "Only modern Key Algorithms allowed"
        fi
        log_info "Allowed Algorithms: ${ACCEPTED_ALGOS}"
    else
        log_info "PubkeyAcceptedAlgorithms: System Default (SHA-1 blocked since OpenSSH 8.8)"
    fi
fi

# SSH Key present for Admin User
ADMIN_HOME=$(eval echo ~"$ADMIN_USER")
if [[ -f "${ADMIN_HOME}/.ssh/authorized_keys" ]]; then
    KEY_COUNT=$(grep -c "^ssh-" "${ADMIN_HOME}/.ssh/authorized_keys" 2>/dev/null || true)
    if [[ "$KEY_COUNT" -gt 0 ]]; then
        log_pass "${KEY_COUNT} SSH key(s) stored for '${ADMIN_USER}'"
        while IFS= read -r line; do
            KEY_TYPE=$(echo "$line" | awk '{print $1}')
            KEY_COMMENT=$(echo "$line" | awk '{print $3}')
            KEY_BITS=$(ssh-keygen -l -f /dev/stdin <<< "$line" 2>/dev/null | awk '{print $1}' || true)
            log_info "  Key: ${KEY_TYPE} ${KEY_BITS:-?} bit (${KEY_COMMENT:-no comment})"
        done < <(grep "^ssh-" "${ADMIN_HOME}/.ssh/authorized_keys")
    else
        log_fail "No SSH keys in authorized_keys!"
    fi
else
    log_fail "No authorized_keys file for '${ADMIN_USER}'!"
fi

SSH_VERSION=$(ssh -V 2>&1)
log_info "SSH Version: ${SSH_VERSION}"

# =============================================================================
# 4. FIREWALL (UFW)
# =============================================================================
section "4. Firewall (UFW)"

# SSH_PORT for later use (if sshd -T failed)
SSH_PORT=${SSH_PORT:-8496} # Fallback to a common custom port

if command -v ufw &>/dev/null; then
    UFW_STATUS=$(ufw status 2>/dev/null || true)
    if echo "$UFW_STATUS" | grep -q "Status: active"; then
        log_pass "UFW is active"

        # Default Policies
        UFW_VERBOSE=$(ufw status verbose 2>/dev/null || true)
        if echo "$UFW_VERBOSE" | grep -q "deny (incoming)"; then
            log_pass "Default policy incoming: deny"
        else
            log_fail "Default policy incoming is NOT deny!"
        fi

        if echo "$UFW_VERBOSE" | grep -q "allow (outgoing)"; then
            log_pass "Default policy outgoing: allow"
        else
            log_info "Default policy outgoing is not 'allow'"
        fi

        # List open ports
        echo ""
        log_info "Active rules:"
        ufw status numbered 2>/dev/null | grep -E "^\[" | while IFS= read -r rule; do
            echo -e "         ${rule}"
        done

        # SSH port with rate-limit?
        if echo "$UFW_STATUS" | grep -q "${SSH_PORT}.*LIMIT"; then
            log_pass "SSH port ${SSH_PORT} has Rate-Limiting"
        elif echo "$UFW_STATUS" | grep -q "${SSH_PORT}"; then
            log_warn "SSH port ${SSH_PORT} is open, but WITHOUT Rate-Limiting"
        else
            log_warn "SSH port ${SSH_PORT} is not explicitly in UFW"
        fi
    else
        log_fail "UFW is NOT active"
    fi

    # UFW Logging Redirect (VNC Console fix)
    if [[ -f /etc/rsyslog.d/20-ufw.conf ]]; then
        log_pass "UFW logs are redirected to /var/log/ufw.log (console remains clean)"
    else
        log_warn "UFW logs not redirected - VNC console will be flooded with [UFW BLOCK]"
    fi
else
    log_fail "UFW is not installed"
fi

# =============================================================================
# 5. FAIL2BAN
# =============================================================================
section "5. Fail2Ban"

if command -v fail2ban-client &>/dev/null; then
    if systemctl is-active --quiet fail2ban; then
        log_pass "Fail2Ban is running"

        # SSH Jail active?
        if fail2ban-client status sshd &>/dev/null; then
            log_pass "SSH Jail is active"
            F2B_STATUS=$(fail2ban-client status sshd 2>/dev/null || true)
            BANNED=$(echo "$F2B_STATUS" | grep "Currently banned" | awk '{print $NF}')
            TOTAL_BANNED=$(echo "$F2B_STATUS" | grep "Total banned" | awk '{print $NF}')
            log_info "Currently banned: ${BANNED:-0}, Total banned: ${TOTAL_BANNED:-0}"
        else
            log_fail "SSH Jail is NOT active"
        fi

        # Check configuration
        F2B_PORT=$(grep -r "^port" /etc/fail2ban/jail.local 2>/dev/null | head -1 | awk '{print $NF}' || true)
        if [[ "$F2B_PORT" == "$SSH_PORT" ]]; then
            log_pass "Fail2Ban monitors correct SSH port (${F2B_PORT})"
        elif [[ -n "$F2B_PORT" ]]; then
            log_fail "Fail2Ban monitors port ${F2B_PORT}, SSH runs on ${SSH_PORT}!"
        fi
    else
        log_fail "Fail2Ban is installed, but NOT active"
    fi
else
    log_fail "Fail2Ban is not installed"
fi

# =============================================================================
# 6. AUTOMATIC UPDATES
# =============================================================================
section "6. Automatic Updates"

if dpkg -l unattended-upgrades &>/dev/null; then
    log_pass "unattended-upgrades is installed"

    if [[ -f /etc/apt/apt.conf.d/20auto-upgrades ]]; then
        if grep -q 'Unattended-Upgrade "1"' /etc/apt/apt.conf.d/20auto-upgrades; then
            log_pass "Automatic upgrades are enabled"
        else
            log_warn "20auto-upgrades exists, but upgrades appear disabled"
        fi
    else
        log_warn "20auto-upgrades configuration is missing"
    fi
else
    log_fail "unattended-upgrades is not installed"
fi

# =============================================================================
# 7. DOCKER
# =============================================================================
section "7. Docker"

if command -v docker &>/dev/null; then
    log_pass "Docker is installed: $(docker --version 2>/dev/null | head -1)"

    if systemctl is-active --quiet docker; then
        log_pass "Docker daemon is running"
    else
        log_warn "Docker daemon is not active"
    fi

    # Docker Compose
    if docker compose version &>/dev/null; then
        log_pass "Docker Compose: $(docker compose version 2>/dev/null | head -1)"
    else
        log_warn "Docker Compose Plugin not found"
    fi

    # User in docker group
    if groups "$ADMIN_USER" 2>/dev/null | grep -qw docker; then
        log_pass "'${ADMIN_USER}' is in the docker group"
    else
        log_warn "'${ADMIN_USER}' is NOT in the docker group"
    fi

    # Log rotation configured?
    if [[ -f /etc/docker/daemon.json ]]; then
        if grep -q "max-size" /etc/docker/daemon.json; then
            log_pass "Docker Log rotation configured"
            MAX_SIZE=$(grep "max-size" /etc/docker/daemon.json | tr -d ' ",' | cut -d: -f2)
            MAX_FILE=$(grep "max-file" /etc/docker/daemon.json | tr -d ' ",' | cut -d: -f2)
            log_info "max-size: ${MAX_SIZE}, max-file: ${MAX_FILE}"
        else
            log_warn "daemon.json exists, but no Log rotation configured"
        fi
    else
        log_warn "No daemon.json - Docker logs can grow indefinitely!"
    fi

    # Proxy network
    if docker network ls 2>/dev/null | grep -q "proxy"; then
        log_pass "Docker network 'proxy' exists"
    else
        log_warn "Docker network 'proxy' is missing"
    fi
else
    log_fail "Docker is not installed"
fi

# =============================================================================
# 8. TRAEFIK PREPARATION
# =============================================================================
section "8. Traefik Stack Preparation"

TRAEFIK_DIR="/opt/containers/traefik-stack"
if [[ -d "$TRAEFIK_DIR" ]]; then
    log_pass "Directory ${TRAEFIK_DIR} exists"

    # Subdirectories
    for dir in traefik/dynamic traefik/logs traefik/certs crowdsec/config crowdsec/data; do
        if [[ -d "${TRAEFIK_DIR}/${dir}" ]]; then
            log_pass "  ${dir}/ is present"
        else
            log_fail "  ${dir}/ is missing"
        fi
    done

    # acme.json Permissions
    if [[ -f "${TRAEFIK_DIR}/traefik/certs/acme.json" ]]; then
        ACME_PERMS=$(stat -c "%a" "${TRAEFIK_DIR}/traefik/certs/acme.json")
        if [[ "$ACME_PERMS" == "600" ]]; then
            log_pass "acme.json has correct permissions (600)"
        else
            log_fail "acme.json has permissions ${ACME_PERMS} (should be 600)"
        fi
    else
        log_warn "acme.json does not exist yet"
    fi
else
    log_fail "Directory ${TRAEFIK_DIR} is missing"
fi

# =============================================================================
# 9. LOG ROTATION
# =============================================================================
section "9. Log Rotation"

# Persistent Journal
if [[ -d /var/log/journal ]]; then
    log_pass "Persistent Journal activated"
    JOURNAL_SIZE=$(journalctl --disk-usage 2>/dev/null | grep -oP '[\d.]+\w+' | head -1 || true)
    log_info "Journal size: ${JOURNAL_SIZE:-unknown}"
else
    log_warn "Journal is NOT persistent (logs are lost on reboot)"
fi

# Logrotate: Traefik
if [[ -f /etc/logrotate.d/traefik ]]; then
    log_pass "Logrotate for Traefik configured"
else
    log_warn "Logrotate for Traefik is missing"
fi

# Logrotate: UFW
if [[ -f /etc/logrotate.d/ufw-custom ]]; then
    log_pass "Logrotate for UFW logs configured"
elif [[ -f /etc/logrotate.d/ufw ]]; then
    log_pass "Logrotate for UFW logs configured (system default)"
else
    log_warn "Logrotate for UFW logs is missing - /var/log/ufw.log can grow indefinitely!"
fi

# Docker Log Rotation (Summary)
if [[ -f /etc/docker/daemon.json ]] && grep -q "max-size" /etc/docker/daemon.json 2>/dev/null; then
    log_pass "Docker Container logs rotated"
else
    log_warn "Docker Container logs are not rotated"
fi

# =============================================================================
# 10. NETWORK & OPEN PORTS
# =============================================================================
section "10. Network & Open Ports"

log_info "Listening ports (externally accessible):"
ss -tlnp 2>/dev/null | grep -v "127.0.0" | grep "LISTEN" | while IFS= read -r line; do
    PORT=$(echo "$line" | awk '{print $4}' | rev | cut -d: -f1 | rev)
    PROC=$(echo "$line" | grep -oP 'users:\(\("\K[^"]+' || echo "unknown")
    echo -e "         Port ${PORT} (${PROC})"
done

# Check for known insecure services
INSECURE_FOUND=0
for svc in telnetd rshd rlogind vsftpd proftpd; do
    if systemctl is-active --quiet "$svc" 2>/dev/null; then
        log_fail "Insecure service running: ${svc}"
        INSECURE_FOUND=1
    fi
done
if [[ "$INSECURE_FOUND" -eq 0 ]]; then
    log_pass "No insecure legacy services active"
fi

# =============================================================================
# 11. FILESYSTEM SECURITY
# =============================================================================
section "11. Filesystem"

# World-writable files in critical directories
WW_COUNT=$(find /etc /usr -xdev -type f -perm -o+w 2>/dev/null | wc -l)
if [[ "$WW_COUNT" -eq 0 ]]; then
    log_pass "No world-writable files in /etc and /usr"
else
    log_warn "${WW_COUNT} world-writable file(s) in /etc or /usr"
fi

# SUID Binaries (informative)
SUID_COUNT=$(find / -xdev -type f -perm -4000 2>/dev/null | wc -l)
log_info "SUID binaries found: ${SUID_COUNT} (manual review if needed)"

# Disk Usage
DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}' | tr -d '%')
if [[ "$DISK_USAGE" -lt 80 ]]; then
    log_pass "Disk usage: ${DISK_USAGE}%"
elif [[ "$DISK_USAGE" -lt 90 ]]; then
    log_warn "Disk usage: ${DISK_USAGE}% (getting low)"
else
    log_fail "Disk usage: ${DISK_USAGE}% (critical!)"
fi

# =============================================================================
# SUMMARY
# =============================================================================
echo ""
echo -e "${BOLD}============================================================${NC}"
echo -e "${BOLD}  SUMMARY${NC}"
echo -e "${BOLD}============================================================${NC}"
echo -e "  ${GREEN}Passed:${NC}   ${pass_count}"
echo -e "  ${YELLOW}Warnings:${NC} ${warn_count}"
echo -e "  ${RED}Failed:${NC}    ${fail_count}"
echo -e "${BOLD}============================================================${NC}"

if [[ "$fail_count" -eq 0 && "$warn_count" -eq 0 ]]; then
    echo -e "\n${GREEN}${BOLD}Excellent! All checks passed.${NC}"
elif [[ "$fail_count" -eq 0 ]]; then
    echo -e "\n${YELLOW}${BOLD}Good - no critical failures, but review warnings.${NC}"
else
    echo -e "\n${RED}${BOLD}Attention - there are critical failures that should be resolved!${NC}"
fi

echo ""
exit "$fail_count"