
# Berechtigung geben
chmod +x cis_level1_hardening.sh
#  Erst Dry-Run:
sudo ./cis_level1_hardening.sh --dry-run
# Dann produktiv:
sudo ./cis_level1_hardening.sh

# Quellcode
#!/usr/bin/env bash
#===============================================================================
# CIS Benchmark Level 1 - Server Hardening Script
# Unterstützt: Red Hat Enterprise Linux 8 & 9 (und kompatible Derivate)
# Version:     1.0
# Modus:       Automatisch für unkritische Maßnahmen
#              Interaktiv (ja/nein) für kritische Maßnahmen
#===============================================================================
set -euo pipefail

# ── Farben & Formatierung ─────────────────────────────────────────────────────
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'
CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'

# ── Globale Variablen ─────────────────────────────────────────────────────────
LOGFILE="/var/log/cis_hardening_$(date +%Y%m%d_%H%M%S).log"
BACKUP_DIR="/root/cis_backup_$(date +%Y%m%d_%H%M%S)"
RHEL_VERSION=""
CHANGES_MADE=0
SKIPPED=0
ALREADY_OK=0
ERRORS=0
DRY_RUN=false

# ── Hilfsfunktionen ──────────────────────────────────────────────────────────
log()    { echo -e "$(date '+%Y-%m-%d %H:%M:%S') $*" | tee -a "$LOGFILE"; }
info()   { log "${BLUE}[INFO]${NC}  $*"; }
ok()     { log "${GREEN}[OK]${NC}    $*"; ((ALREADY_OK++)) || true; }
fixed()  { log "${GREEN}[FIXED]${NC} $*"; ((CHANGES_MADE++)) || true; }
warn()   { log "${YELLOW}[WARN]${NC}  $*"; }
error()  { log "${RED}[ERROR]${NC} $*"; ((ERRORS++)) || true; }
skip()   { log "${YELLOW}[SKIP]${NC}  $*"; ((SKIPPED++)) || true; }
section(){ echo "" | tee -a "$LOGFILE"; log "${BOLD}${CYAN}═══ $* ═══${NC}"; }

ask_critical() {
    local desc="$1"
    echo -e "${YELLOW}[KRITISCH]${NC} ${BOLD}$desc${NC}"
    echo -en "  Umsetzen? (j/n/info): "
    while true; do
        read -r answer
        case "${answer,,}" in
            j|ja|y|yes) return 0 ;;
            n|nein|no)  skip "Übersprungen: $desc"; return 1 ;;
            i|info)     echo -e "  ${CYAN}$2${NC}"; echo -en "  Umsetzen? (j/n): " ;;
            *)          echo -en "  Bitte j oder n eingeben: " ;;
        esac
    done
}

backup_file() {
    local file="$1"
    if [[ -f "$file" ]]; then
        local target="${BACKUP_DIR}${file}"
        mkdir -p "$(dirname "$target")"
        cp -a "$file" "$target" 2>/dev/null || true
    fi
}

sysctl_set() {
    local param="$1" value="$2"
    local current
    current=$(sysctl -n "$param" 2>/dev/null || echo "NICHT_VERFÜGBAR")
    if [[ "$current" == "$value" ]]; then
        ok "$param = $value (bereits gesetzt)"
    else
        if ! $DRY_RUN; then
            sysctl -w "${param}=${value}" >/dev/null 2>&1 || { error "sysctl $param fehlgeschlagen"; return; }
            grep -qxF "${param} = ${value}" /etc/sysctl.d/99-cis-hardening.conf 2>/dev/null || \
                echo "${param} = ${value}" >> /etc/sysctl.d/99-cis-hardening.conf
        fi
        fixed "$param = $value"
    fi
}

ensure_config_line() {
    local file="$1" line="$2" desc="$3"
    backup_file "$file"
    if grep -qxF "$line" "$file" 2>/dev/null; then
        ok "$desc (bereits konfiguriert)"
    else
        if ! $DRY_RUN; then
            echo "$line" >> "$file"
        fi
        fixed "$desc"
    fi
}

disable_kernel_module() {
    local mod="$1"
    local conf="/etc/modprobe.d/cis-disable-${mod}.conf"
    if [[ -f "$conf" ]] && grep -q "install ${mod} /bin/false" "$conf" 2>/dev/null; then
        ok "Kernel-Modul $mod bereits deaktiviert"
    else
        if ! $DRY_RUN; then
            echo "install ${mod} /bin/false" > "$conf"
            echo "blacklist ${mod}" >> "$conf"
            modprobe -r "$mod" 2>/dev/null || true
            rmmod "$mod" 2>/dev/null || true
        fi
        fixed "Kernel-Modul $mod deaktiviert"
    fi
}

disable_service() {
    local svc="$1" desc="$2"
    if systemctl is-enabled "$svc" 2>/dev/null | grep -q "enabled"; then
        if ! $DRY_RUN; then
            systemctl stop "$svc" 2>/dev/null || true
            systemctl disable "$svc" 2>/dev/null || true
        fi
        fixed "$desc: $svc deaktiviert"
    else
        ok "$desc: $svc bereits deaktiviert/nicht vorhanden"
    fi
}

mask_service() {
    local svc="$1" desc="$2"
    if ! systemctl is-masked "$svc" 2>/dev/null | grep -q "masked"; then
        if ! $DRY_RUN; then
            systemctl stop "$svc" 2>/dev/null || true
            systemctl mask "$svc" 2>/dev/null || true
        fi
        fixed "$desc: $svc maskiert"
    else
        ok "$desc: $svc bereits maskiert"
    fi
}

remove_package() {
    local pkg="$1" desc="$2"
    if rpm -q "$pkg" >/dev/null 2>&1; then
        if ! $DRY_RUN; then
            dnf remove -y "$pkg" >/dev/null 2>&1 || { error "Konnte $pkg nicht entfernen"; return; }
        fi
        fixed "$desc: $pkg entfernt"
    else
        ok "$desc: $pkg nicht installiert"
    fi
}

# ── Voraussetzungen prüfen ───────────────────────────────────────────────────
preflight() {
    if [[ $EUID -ne 0 ]]; then
        echo -e "${RED}Dieses Script muss als root ausgeführt werden.${NC}"
        exit 1
    fi

    if [[ "${1:-}" == "--dry-run" ]]; then
        DRY_RUN=true
        echo -e "${YELLOW}=== DRY-RUN MODUS – es werden keine Änderungen vorgenommen ===${NC}"
    fi

    if [[ -f /etc/redhat-release ]]; then
        RHEL_VERSION=$(rpm -E %{rhel} 2>/dev/null || grep -oP '(?<=release )\d' /etc/redhat-release)
    fi

    if [[ "$RHEL_VERSION" != "8" && "$RHEL_VERSION" != "9" ]]; then
        echo -e "${RED}Nicht unterstütztes System. RHEL 8 oder 9 erforderlich.${NC}"
        echo "Erkannt: $(cat /etc/redhat-release 2>/dev/null || echo 'unbekannt')"
        exit 1
    fi

    mkdir -p "$BACKUP_DIR"
    touch "$LOGFILE"

    echo -e "${BOLD}${CYAN}"
    echo "╔══════════════════════════════════════════════════════════════╗"
    echo "║    CIS Level 1 Server Hardening – RHEL $RHEL_VERSION                    ║"
    echo "║    $(date '+%Y-%m-%d %H:%M:%S')                                    ║"
    echo "║    Backup-Dir: $BACKUP_DIR     ║"
    echo "║    Logfile:    $LOGFILE     ║"
    echo "╚══════════════════════════════════════════════════════════════╝"
    echo -e "${NC}"

    info "System: $(cat /etc/redhat-release)"
    info "Kernel: $(uname -r)"
    info "Backup-Verzeichnis: $BACKUP_DIR"
    echo ""
    echo -e "${YELLOW}Das Script wird jetzt CIS Level 1 Server Hardening durchführen.${NC}"
    echo -e "Unkritische Maßnahmen werden automatisch umgesetzt."
    echo -e "Kritische Maßnahmen erfordern Bestätigung."
    echo ""
    echo -en "Fortfahren? (j/n): "
    read -r confirm
    if [[ "${confirm,,}" != "j" && "${confirm,,}" != "ja" ]]; then
        echo "Abgebrochen."
        exit 0
    fi
}

# ══════════════════════════════════════════════════════════════════════════════
# SEKTION 1: Initial Setup
# ══════════════════════════════════════════════════════════════════════════════

harden_filesystem_modules() {
    section "1.1.1 – Dateisystem-Kernel-Module deaktivieren"

    local modules_safe=(cramfs freevxfs hfs hfsplus jffs2 udf)
    for mod in "${modules_safe[@]}"; do
        disable_kernel_module "$mod"
    done

    # squashfs – kritisch wegen Container
    if lsmod | grep -q squashfs || mount | grep -q squashfs; then
        warn "squashfs ist aktiv (Container/Snap). Wird NICHT deaktiviert."
    else
        disable_kernel_module "squashfs"
    fi

    # usb-storage – kritisch
    if ask_critical "1.1.1.8/10: USB-Storage Kernel-Modul deaktivieren" \
        "USB-Speichergeräte werden komplett blockiert. Alternative: USBGuard für granulare Kontrolle."; then
        disable_kernel_module "usb-storage"
    fi
}

harden_filesystem_partitions() {
    section "1.1.2 – Dateisystem-Partitionen & Mountoptionen"

    # /tmp Mountoptionen prüfen/setzen
    if findmnt -n /tmp >/dev/null 2>&1; then
        local tmp_opts
        tmp_opts=$(findmnt -n -o OPTIONS /tmp)
        for opt in nosuid nodev noexec; do
            if echo "$tmp_opts" | grep -q "$opt"; then
                ok "/tmp hat $opt"
            else
                warn "/tmp fehlt $opt – manuelle Anpassung in /etc/fstab empfohlen"
            fi
        done
    else
        warn "/tmp ist keine separate Partition – empfohlen: tmpfs-Eintrag in /etc/fstab"
    fi

    # /dev/shm prüfen
    if findmnt -n /dev/shm >/dev/null 2>&1; then
        local shm_opts
        shm_opts=$(findmnt -n -o OPTIONS /dev/shm)
        for opt in nosuid nodev noexec; do
            if echo "$shm_opts" | grep -q "$opt"; then
                ok "/dev/shm hat $opt"
            else
                if ! $DRY_RUN; then
                    mount -o remount,"$opt" /dev/shm 2>/dev/null || true
                    # Persistenz in fstab
                    if ! grep -q "/dev/shm" /etc/fstab; then
                        echo "tmpfs /dev/shm tmpfs defaults,nosuid,nodev,noexec 0 0" >> /etc/fstab
                    fi
                fi
                fixed "/dev/shm: $opt gesetzt"
            fi
        done
    fi

    # /home, /var, /var/tmp, /var/log, /var/log/audit – nur Status prüfen
    for mp in /home /var /var/tmp /var/log /var/log/audit; do
        if findmnt -n "$mp" >/dev/null 2>&1; then
            ok "$mp ist separate Partition"
        else
            warn "$mp ist KEINE separate Partition (empfohlen für CIS Compliance)"
        fi
    done
}

harden_package_management() {
    section "1.2 – Paketverwaltung"

    # gpgcheck global
    if grep -q "^gpgcheck=1" /etc/dnf/dnf.conf 2>/dev/null; then
        ok "gpgcheck ist global aktiviert"
    else
        backup_file /etc/dnf/dnf.conf
        if ! $DRY_RUN; then
            sed -i 's/^gpgcheck=.*/gpgcheck=1/' /etc/dnf/dnf.conf 2>/dev/null || \
                echo "gpgcheck=1" >> /etc/dnf/dnf.conf
        fi
        fixed "gpgcheck global aktiviert"
    fi

    # Repo-gpgcheck
    local bad_repos
    bad_repos=$(grep -rl "^gpgcheck=0" /etc/yum.repos.d/ 2>/dev/null || true)
    if [[ -n "$bad_repos" ]]; then
        warn "Repos ohne gpgcheck gefunden: $bad_repos"
    else
        ok "Alle Repos haben gpgcheck aktiviert"
    fi
}

harden_selinux() {
    section "1.3 – SELinux / Mandatory Access Control"

    # SELinux Bootloader-Parameter
    local grub_selinux
    grub_selinux=$(grubby --info=ALL 2>/dev/null | grep -E "selinux=0|enforcing=0" || true)
    if [[ -n "$grub_selinux" ]]; then
        if ! $DRY_RUN; then
            grubby --update-kernel ALL --remove-args "selinux=0 enforcing=0"
        fi
        fixed "SELinux-Deaktivierung aus Bootloader entfernt"
    else
        ok "SELinux ist nicht im Bootloader deaktiviert"
    fi

    # SELinux Policy
    local current_policy
    current_policy=$(grep -E "^SELINUXTYPE=" /etc/selinux/config 2>/dev/null | cut -d= -f2 || echo "unknown")
    if [[ "$current_policy" == "targeted" ]]; then
        ok "SELinux Policy: targeted"
    else
        warn "SELinux Policy ist '$current_policy' – sollte 'targeted' sein"
    fi

    # SELinux Enforcing
    local current_mode
    current_mode=$(getenforce 2>/dev/null || echo "Disabled")
    if [[ "$current_mode" == "Enforcing" ]]; then
        ok "SELinux ist Enforcing"
    else
        if ask_critical "SELinux auf Enforcing setzen (aktuell: $current_mode)" \
            "Relabeling dauert 30-60 Min beim nächsten Boot. Erst Permissive testen empfohlen. audit2why hilft bei Policy-Fehlern."; then
            backup_file /etc/selinux/config
            if ! $DRY_RUN; then
                sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
                if [[ "$current_mode" == "Permissive" ]]; then
                    setenforce 1 2>/dev/null || warn "setenforce 1 fehlgeschlagen – wird nach Reboot aktiv"
                else
                    warn "SELinux war Disabled – Reboot mit fixfiles -F onboot erforderlich"
                    fixfiles -F onboot 2>/dev/null || true
                fi
            fi
            fixed "SELinux auf Enforcing konfiguriert"
        fi
    fi

    # setroubleshoot und mcstrans entfernen
    remove_package "setroubleshoot" "1.3.1.6: setroubleshoot"
    remove_package "mcstrans" "1.3.1.7: mcstrans"
}

harden_bootloader() {
    section "1.4 – Bootloader absichern"

    # Bootloader-Passwort
    if grep -q "^GRUB2_PASSWORD=" /boot/grub2/user.cfg 2>/dev/null; then
        ok "Bootloader-Passwort ist gesetzt"
    else
        warn "Bootloader-Passwort ist NICHT gesetzt – manuell setzen: grub2-setpassword"
    fi

    # Bootloader-Config Permissions
    local grub_cfg="/boot/grub2/grub.cfg"
    if [[ -f "$grub_cfg" ]]; then
        local perms
        perms=$(stat -c "%a" "$grub_cfg")
        if [[ "$perms" == "600" ]] || [[ "$perms" == "700" ]]; then
            ok "grub.cfg Berechtigungen: $perms"
        else
            if ! $DRY_RUN; then
                chmod 600 "$grub_cfg"
                chown root:root "$grub_cfg"
            fi
            fixed "grub.cfg Berechtigungen auf 600 gesetzt"
        fi
    fi
}

harden_process_hardening() {
    section "1.5 – Prozess-Härtung"

    # core dumps
    ensure_config_line "/etc/security/limits.d/cis-hardening.conf" "* hard core 0" "1.5.1: Core Dumps limitiert"
    sysctl_set "fs.suid_dumpable" "0"

    # ASLR
    sysctl_set "kernel.randomize_va_space" "2"

    # ptrace
    sysctl_set "kernel.yama.ptrace_scope" "1"
}

harden_crypto_policy() {
    section "1.6 – System-weite Crypto-Policy"

    local current_policy
    current_policy=$(update-crypto-policies --show 2>/dev/null || echo "UNKNOWN")

    if [[ "$current_policy" == "LEGACY" ]]; then
        if ask_critical "Crypto-Policy von LEGACY auf DEFAULT ändern" \
            "Verbindungen zu Systemen mit TLS 1.0/1.1 oder schwachen Ciphern schlagen danach fehl."; then
            if ! $DRY_RUN; then
                update-crypto-policies --set DEFAULT >/dev/null 2>&1
            fi
            fixed "Crypto-Policy auf DEFAULT gesetzt"
        fi
    elif [[ "$current_policy" == "DEFAULT" || "$current_policy" == "FUTURE" || "$current_policy" == "FIPS" ]]; then
        ok "Crypto-Policy: $current_policy"
    else
        warn "Crypto-Policy: $current_policy – prüfen ob angemessen"
    fi

    # CBC für SSH deaktivieren
    local cbc_rec="1.6.4"
    [[ "$RHEL_VERSION" == "9" ]] && cbc_rec="1.6.5"
    if sshd -T 2>/dev/null | grep -qi "aes.*cbc"; then
        if ask_critical "$cbc_rec: CBC-Cipher für SSH deaktivieren" \
            "Ältere SSH-Clients unterstützen evtl. nur CBC. Prüfe vorher Verbindungen zu Legacy-Systemen."; then
            if ! $DRY_RUN; then
                update-crypto-policies --set "${current_policy}:NO-CBC" >/dev/null 2>&1 || \
                    warn "Subpolicy NO-CBC konnte nicht gesetzt werden"
            fi
            fixed "CBC-Cipher für SSH deaktiviert"
        fi
    else
        ok "$cbc_rec: Keine CBC-Cipher in SSH aktiv"
    fi
}

harden_banners() {
    section "1.7 – Login-Banner konfigurieren"

    local banner_text="Autorisierter Zugriff erforderlich. Alle Aktivitaeten werden protokolliert. Unbefugter Zugriff wird strafrechtlich verfolgt."

    for f in /etc/issue /etc/issue.net; do
        backup_file "$f"
        # Prüfe ob OS-Infos im Banner stehen
        if grep -qEi '\\r|\\s|\\m|\\v|kernel' "$f" 2>/dev/null; then
            if ! $DRY_RUN; then
                echo "$banner_text" > "$f"
                chmod 644 "$f"
                chown root:root "$f"
            fi
            fixed "$(basename $f): OS-Informationen entfernt, Banner gesetzt"
        elif [[ ! -s "$f" ]]; then
            if ! $DRY_RUN; then
                echo "$banner_text" > "$f"
                chmod 644 "$f"
                chown root:root "$f"
            fi
            fixed "$(basename $f): Banner gesetzt"
        else
            ok "$(basename $f): Banner vorhanden ohne OS-Infos"
        fi
    done

    # /etc/motd – keine OS-Infos
    if [[ -s /etc/motd ]] && grep -qEi '\\r|\\s|\\m|\\v|kernel' /etc/motd 2>/dev/null; then
        backup_file /etc/motd
        if ! $DRY_RUN; then
            > /etc/motd
        fi
        fixed "/etc/motd: OS-Informationen entfernt"
    else
        ok "/etc/motd: OK"
    fi
}

# ══════════════════════════════════════════════════════════════════════════════
# SEKTION 2: Dienste
# ══════════════════════════════════════════════════════════════════════════════

harden_services() {
    section "2.1 – Server-Dienste deaktivieren"

    # Unkritische Dienste – immer deaktivieren/entfernen
    local safe_services=(
        "telnet-server:Telnet-Server"
        "ypserv:NIS-Server"
        "xinetd:xinetd"
    )
    for entry in "${safe_services[@]}"; do
        local pkg="${entry%%:*}" desc="${entry##*:}"
        remove_package "$pkg" "2.1: $desc"
    done

    # Dienste die meist nicht benötigt werden aber interaktiv abfragen
    local critical_services=(
        "autofs:autofs:Automatisches Mounten von Wechselmedien und NFS-Shares wird deaktiviert."
        "avahi-daemon:avahi:mDNS/Bonjour Discovery wird deaktiviert. Kann Service-Discovery beeinträchtigen."
        "cups:cups:Drucken wird deaktiviert. Auf Servern meist unproblematisch."
        "vsftpd:vsftpd:FTP-Server wird entfernt. Alternative: SFTP über SSH."
        "samba:samba:Windows-Filesharing wird deaktiviert."
        "snmpd:net-snmp:SNMP-Monitoring wird deaktiviert. Alternative: Prometheus/node_exporter."
        "squid:squid:Web-Proxy wird deaktiviert."
        "rsync:rsync-daemon:rsync-Daemon wird deaktiviert. rsync als Client bleibt nutzbar."
    )

    for entry in "${critical_services[@]}"; do
        IFS=':' read -r svc pkg impact <<< "$entry"
        if systemctl is-enabled "$svc" 2>/dev/null | grep -q "enabled" || rpm -q "$pkg" >/dev/null 2>&1; then
            if ask_critical "2.1: $svc deaktivieren/entfernen" "$impact"; then
                disable_service "$svc" "2.1"
            fi
        else
            ok "2.1: $svc nicht aktiv/installiert"
        fi
    done

    # NFS/rpcbind – besonders kritisch wegen libvirt
    section "2.1 – NFS/rpcbind (libvirt-Abhängigkeit)"

    if rpm -q nfs-utils >/dev/null 2>&1 || rpm -q rpcbind >/dev/null 2>&1; then
        local libvirt_installed=false
        rpm -q libvirt >/dev/null 2>&1 && libvirt_installed=true

        if $libvirt_installed; then
            warn "libvirt ist installiert – NFS/rpcbind werden als Abhängigkeit benötigt"
            warn "Empfehlung: nfs-server deaktivieren, Pakete NICHT entfernen"
            if ask_critical "NFS-Server-Dienst deaktivieren (Pakete bleiben für libvirt)" \
                "nfs-server wird gestoppt, nfs-utils/rpcbind bleiben installiert für KVM/QEMU."; then
                disable_service "nfs-server" "2.1.9"
            fi
        else
            if ask_critical "NFS und rpcbind komplett entfernen" \
                "Kein libvirt installiert. NFS-Mounts, NFS-Shares und RPC-basierte Dienste werden nicht mehr funktionieren."; then
                disable_service "nfs-server" "2.1.9"
                disable_service "rpcbind.socket" "2.1.12"
                disable_service "rpcbind" "2.1.12"
            fi
        fi
    else
        ok "2.1: NFS/rpcbind nicht installiert"
    fi

    # TFTP – kritisch wegen PXE
    if rpm -q tftp-server >/dev/null 2>&1; then
        if ask_critical "TFTP-Server entfernen" \
            "TFTP wird für PXE/Netzwerk-Boot verwendet. Wenn PXE-Deployment genutzt wird: NICHT entfernen."; then
            remove_package "tftp-server" "2.1.16/17"
        fi
    else
        ok "2.1: TFTP-Server nicht installiert"
    fi

    # Web-Server prüfen
    for websrv in httpd nginx; do
        if rpm -q "$websrv" >/dev/null 2>&1; then
            if ask_critical "Web-Server $websrv deaktivieren" \
                "Web-Services werden gestoppt. Wenn dieser Server Web-Dienste hostet: NICHT deaktivieren."; then
                disable_service "$websrv" "2.1.18/19"
            fi
        fi
    done
}

harden_client_services() {
    section "2.2 – Client-Dienste"

    remove_package "ypbind" "2.2.1: NIS-Client"
    remove_package "telnet" "2.2.4: Telnet-Client"

    # LDAP-Client
    if rpm -q openldap-clients >/dev/null 2>&1; then
        warn "2.2.2: openldap-clients installiert – prüfen ob benötigt"
    fi
}

harden_time_sync() {
    section "2.3 – Zeitsynchronisation"

    if systemctl is-enabled chronyd 2>/dev/null | grep -q "enabled"; then
        ok "chronyd ist aktiviert"
    elif systemctl is-enabled ntpd 2>/dev/null | grep -q "enabled"; then
        ok "ntpd ist aktiviert (chronyd bevorzugt)"
    else
        if ! $DRY_RUN; then
            dnf install -y chrony >/dev/null 2>&1 || true
            systemctl enable --now chronyd 2>/dev/null || true
        fi
        fixed "chronyd installiert und aktiviert"
    fi
}

harden_cron() {
    section "2.4 – Cron & at absichern"

    # crontab Berechtigungen
    for f in /etc/crontab; do
        if [[ -f "$f" ]]; then
            local perms owner
            perms=$(stat -c "%a" "$f")
            owner=$(stat -c "%U:%G" "$f")
            if [[ "$perms" != "600" || "$owner" != "root:root" ]]; then
                if ! $DRY_RUN; then
                    chmod 600 "$f"
                    chown root:root "$f"
                fi
                fixed "$(basename $f): Berechtigungen auf 600 root:root"
            else
                ok "$(basename $f): Berechtigungen OK"
            fi
        fi
    done

    for d in /etc/cron.d /etc/cron.daily /etc/cron.hourly /etc/cron.weekly /etc/cron.monthly; do
        if [[ -d "$d" ]]; then
            local perms
            perms=$(stat -c "%a" "$d")
            if [[ "$perms" != "700" ]]; then
                if ! $DRY_RUN; then
                    chmod 700 "$d"
                    chown root:root "$d"
                fi
                fixed "$d: Berechtigungen auf 700"
            else
                ok "$d: Berechtigungen OK"
            fi
        fi
    done

    # cron.allow / at.allow
    for f in /etc/cron.allow /etc/at.allow; do
        if [[ ! -f "$f" ]]; then
            if ! $DRY_RUN; then
                touch "$f"
                chmod 600 "$f"
                chown root:root "$f"
                echo "root" > "$f"
            fi
            fixed "$(basename $f) erstellt (nur root)"
        else
            ok "$(basename $f) existiert"
        fi
    done

    # .deny-Dateien entfernen
    for f in /etc/cron.deny /etc/at.deny; do
        if [[ -f "$f" ]]; then
            if ! $DRY_RUN; then
                backup_file "$f"
                rm -f "$f"
            fi
            fixed "$(basename $f) entfernt"
        fi
    done
}

# ══════════════════════════════════════════════════════════════════════════════
# SEKTION 3: Netzwerk
# ══════════════════════════════════════════════════════════════════════════════

harden_network_devices() {
    section "3.1 – Netzwerk-Geräte"

    # WLAN – kritisch
    if nmcli radio wifi 2>/dev/null | grep -qi "enabled"; then
        if ask_critical "WLAN deaktivieren" \
            "Wireless-Interfaces werden komplett deaktiviert. Nur auf Servern empfohlen, NICHT auf Laptops."; then
            if ! $DRY_RUN; then
                nmcli radio wifi off 2>/dev/null || true
                disable_kernel_module "cfg80211"
            fi
            fixed "WLAN deaktiviert"
        fi
    else
        ok "WLAN bereits deaktiviert oder nicht vorhanden"
    fi

    # Bluetooth
    if systemctl is-enabled bluetooth 2>/dev/null | grep -q "enabled"; then
        if ask_critical "Bluetooth deaktivieren" \
            "Kabellose Peripherie funktioniert nicht mehr. Auf Servern empfohlen."; then
            disable_service "bluetooth" "3.1.3"
            disable_kernel_module "bluetooth"
        fi
    else
        ok "Bluetooth bereits deaktiviert oder nicht vorhanden"
    fi
}

harden_network_modules() {
    section "3.2 – Netzwerk-Kernel-Module"

    local net_modules=(dccp sctp tipc)
    for mod in "${net_modules[@]}"; do
        disable_kernel_module "$mod"
    done
}

harden_network_params() {
    section "3.3 – Netzwerk-Kernel-Parameter"

    # IP-Forwarding – kritisch
    local ip_fwd
    ip_fwd=$(sysctl -n net.ipv4.ip_forward 2>/dev/null)
    if [[ "$ip_fwd" == "1" ]]; then
        if ask_critical "IP-Forwarding deaktivieren (aktuell: aktiv)" \
            "Router/NAT/VPN/Container-Hosts benötigen Forwarding. Cloud-VMs oft ebenfalls. NICHT deaktivieren wenn System routet."; then
            sysctl_set "net.ipv4.ip_forward" "0"
            sysctl_set "net.ipv6.conf.all.forwarding" "0"
        fi
    else
        sysctl_set "net.ipv4.ip_forward" "0"
    fi

    # Unkritische Netzwerk-Parameter – automatisch setzen
    info "Setze sichere Netzwerk-Parameter..."

    # ICMP Redirects
    sysctl_set "net.ipv4.conf.all.send_redirects" "0"
    sysctl_set "net.ipv4.conf.default.send_redirects" "0"
    sysctl_set "net.ipv4.conf.all.accept_redirects" "0"
    sysctl_set "net.ipv4.conf.default.accept_redirects" "0"
    sysctl_set "net.ipv6.conf.all.accept_redirects" "0"
    sysctl_set "net.ipv6.conf.default.accept_redirects" "0"

    # Source Routing
    sysctl_set "net.ipv4.conf.all.accept_source_route" "0"
    sysctl_set "net.ipv4.conf.default.accept_source_route" "0"
    sysctl_set "net.ipv6.conf.all.accept_source_route" "0"
    sysctl_set "net.ipv6.conf.default.accept_source_route" "0"

    # Logging Martians
    sysctl_set "net.ipv4.conf.all.log_martians" "1"
    sysctl_set "net.ipv4.conf.default.log_martians" "1"

    # ICMP Protection
    sysctl_set "net.ipv4.icmp_echo_ignore_broadcasts" "1"
    sysctl_set "net.ipv4.icmp_ignore_bogus_error_responses" "1"

    # SYN Cookies
    sysctl_set "net.ipv4.tcp_syncookies" "1"

    # Reverse Path Filtering
    local rp_current
    rp_current=$(sysctl -n net.ipv4.conf.all.rp_filter 2>/dev/null)
    if [[ "$rp_current" == "0" ]]; then
        if ask_critical "Reverse Path Filtering aktivieren" \
            "Bricht asymmetrisches Routing. Wenn asymmetrisches Routing genutzt wird: rp_filter=2 (loose) statt 1 (strict)."; then
            sysctl_set "net.ipv4.conf.all.rp_filter" "1"
            sysctl_set "net.ipv4.conf.default.rp_filter" "1"
        fi
    else
        sysctl_set "net.ipv4.conf.all.rp_filter" "1"
        sysctl_set "net.ipv4.conf.default.rp_filter" "1"
    fi

    # IPv6 Router Advertisements
    sysctl_set "net.ipv6.conf.all.accept_ra" "0"
    sysctl_set "net.ipv6.conf.default.accept_ra" "0"
}

# ══════════════════════════════════════════════════════════════════════════════
# SEKTION 4: Firewall
# ══════════════════════════════════════════════════════════════════════════════

harden_firewall() {
    section "4 – Host-basierte Firewall"

    if ask_critical "Firewall konfigurieren" \
        "Drop-Policy ohne SSH-Regel sperrt Remote-Zugang! Script fügt SSH-Regel VOR Drop-Policy hinzu. Trotzdem: IPMI/iDRAC-Zugang als Fallback empfohlen."; then

        if [[ "$RHEL_VERSION" == "8" ]]; then
            # RHEL 8: firewalld
            if ! rpm -q firewalld >/dev/null 2>&1; then
                if ! $DRY_RUN; then
                    dnf install -y firewalld >/dev/null 2>&1
                fi
                fixed "firewalld installiert"
            fi

            if ! $DRY_RUN; then
                systemctl enable --now firewalld 2>/dev/null || true
                firewall-cmd --permanent --add-service=ssh 2>/dev/null || true
                firewall-cmd --reload 2>/dev/null || true
            fi
            fixed "firewalld aktiviert, SSH erlaubt"

            # Default Zone prüfen
            local zone
            zone=$(firewall-cmd --get-default-zone 2>/dev/null || echo "unknown")
            info "Default Firewall-Zone: $zone"
            if [[ "$zone" == "public" ]]; then
                ok "Default Zone ist 'public' (sinnvoller Default)"
            fi

        elif [[ "$RHEL_VERSION" == "9" ]]; then
            # RHEL 9: nftables (oder firewalld)
            if systemctl is-enabled firewalld 2>/dev/null | grep -q "enabled"; then
                info "firewalld ist aktiv (nutzt nftables als Backend)"
                if ! $DRY_RUN; then
                    firewall-cmd --permanent --add-service=ssh 2>/dev/null || true
                    firewall-cmd --reload 2>/dev/null || true
                fi
                fixed "SSH in firewalld erlaubt"
            else
                if ! rpm -q nftables >/dev/null 2>&1; then
                    if ! $DRY_RUN; then
                        dnf install -y nftables >/dev/null 2>&1
                    fi
                fi

                if ! $DRY_RUN; then
                    # SSH-Regel ZUERST, dann Drop-Policy
                    cat > /etc/nftables/cis-base.nft << 'NFTEOF'
table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;
        iif lo accept
        ct state established,related accept
        ct state invalid drop
        tcp dport 22 accept
        icmp type echo-request accept
    }
    chain forward {
        type filter hook forward priority 0; policy drop;
    }
    chain output {
        type filter hook output priority 0; policy accept;
    }
}
NFTEOF
                    # In nftables.conf einbinden
                    if ! grep -q "cis-base.nft" /etc/sysconfig/nftables.conf 2>/dev/null; then
                        echo 'include "/etc/nftables/cis-base.nft"' >> /etc/sysconfig/nftables.conf
                    fi
                    systemctl enable --now nftables 2>/dev/null || true
                fi
                fixed "nftables mit Default Deny und SSH-Allow konfiguriert"
            fi
        fi
    fi
}

# ══════════════════════════════════════════════════════════════════════════════
# SEKTION 5: Zugriffskontrolle
# ══════════════════════════════════════════════════════════════════════════════

harden_ssh() {
    section "5.1 – SSH-Server absichern"

    local ssh_conf="/etc/ssh/sshd_config.d/99-cis-hardening.conf"
    backup_file "$ssh_conf"

    if ! $DRY_RUN; then
        mkdir -p /etc/ssh/sshd_config.d
        cat > "$ssh_conf" << 'SSHEOF'
# CIS Level 1 SSH Hardening
PermitRootLogin no
MaxAuthTries 4
MaxSessions 10
PermitEmptyPasswords no
HostbasedAuthentication no
IgnoreRhosts yes
X11Forwarding no
AllowTcpForwarding no
ClientAliveInterval 300
ClientAliveCountMax 3
LoginGraceTime 60
Banner /etc/issue.net
MaxStartups 10:30:60
PermitUserEnvironment no
SSHEOF
    fi
    fixed "SSH-Härtung in $ssh_conf geschrieben"

    # SSH-Daemon neu laden
    if ! $DRY_RUN; then
        # Syntax-Check
        if sshd -t 2>/dev/null; then
            systemctl reload sshd 2>/dev/null || systemctl reload ssh 2>/dev/null || true
            ok "sshd Konfiguration validiert und neu geladen"
        else
            error "sshd Konfiguration fehlerhaft! Bitte manuell prüfen: sshd -t"
            # Rollback
            rm -f "$ssh_conf"
            warn "SSH-Konfiguration zurückgerollt"
        fi
    fi

    # SSH Private Key Berechtigungen
    find /etc/ssh -name 'ssh_host_*_key' -exec chmod 600 {} \; 2>/dev/null || true
    find /etc/ssh -name 'ssh_host_*_key.pub' -exec chmod 644 {} \; 2>/dev/null || true
    ok "SSH Host-Key Berechtigungen geprüft"
}

harden_sudo() {
    section "5.2 – sudo absichern"

    # use_pty
    if grep -rq "^Defaults.*use_pty" /etc/sudoers /etc/sudoers.d/ 2>/dev/null; then
        ok "sudo use_pty bereits konfiguriert"
    else
        if ! $DRY_RUN; then
            echo "Defaults use_pty" > /etc/sudoers.d/cis-use-pty
            chmod 440 /etc/sudoers.d/cis-use-pty
            # Syntax-Check
            if ! visudo -cf /etc/sudoers.d/cis-use-pty >/dev/null 2>&1; then
                rm -f /etc/sudoers.d/cis-use-pty
                error "sudo use_pty Syntax-Fehler – zurückgerollt"
            fi
        fi
        fixed "sudo use_pty konfiguriert"
    fi

    # sudo Logfile
    if grep -rq "^Defaults.*logfile=" /etc/sudoers /etc/sudoers.d/ 2>/dev/null; then
        ok "sudo Logfile bereits konfiguriert"
    else
        if ! $DRY_RUN; then
            echo 'Defaults logfile="/var/log/sudo.log"' > /etc/sudoers.d/cis-logfile
            chmod 440 /etc/sudoers.d/cis-logfile
            if ! visudo -cf /etc/sudoers.d/cis-logfile >/dev/null 2>&1; then
                rm -f /etc/sudoers.d/cis-logfile
                error "sudo logfile Syntax-Fehler – zurückgerollt"
            fi
        fi
        fixed "sudo Logfile konfiguriert"
    fi
}

harden_pam() {
    section "5.3 – PAM / Authentifizierung"

    # Passwort-Qualität
    local pwquality="/etc/security/pwquality.conf"
    if [[ -f "$pwquality" ]]; then
        backup_file "$pwquality"
        local settings=(
            "minlen = 14"
            "minclass = 4"
            "maxrepeat = 3"
            "maxclassrepeat = 3"
            "dcredit = -1"
            "ucredit = -1"
            "lcredit = -1"
            "ocredit = -1"
        )
        for setting in "${settings[@]}"; do
            local key="${setting%% =*}"
            if grep -q "^${key}" "$pwquality" 2>/dev/null; then
                if ! $DRY_RUN; then
                    sed -i "s/^${key}.*/${setting}/" "$pwquality"
                fi
            else
                if ! $DRY_RUN; then
                    echo "$setting" >> "$pwquality"
                fi
            fi
        done
        fixed "Passwort-Qualitätsrichtlinien gesetzt (minlen=14, minclass=4)"
    fi

    # Faillock
    local faillock="/etc/security/faillock.conf"
    if [[ -f "$faillock" ]]; then
        backup_file "$faillock"
        local fl_settings=(
            "deny = 5"
            "unlock_time = 900"
            "fail_interval = 900"
        )
        for setting in "${fl_settings[@]}"; do
            local key="${setting%% =*}"
            if grep -q "^${key}" "$faillock" 2>/dev/null; then
                if ! $DRY_RUN; then
                    sed -i "s/^${key}.*/${setting}/" "$faillock"
                fi
            else
                if ! $DRY_RUN; then
                    echo "$setting" >> "$faillock"
                fi
            fi
        done
        fixed "Account-Lockout konfiguriert (deny=5, unlock=900s)"
    fi
}

harden_password_policy() {
    section "5.4 – Passwort-Policy & User-Accounts"

    backup_file /etc/login.defs

    # PASS_MAX_DAYS
    local current_max
    current_max=$(grep "^PASS_MAX_DAYS" /etc/login.defs 2>/dev/null | awk '{print $2}')
    if [[ "${current_max:-99999}" -gt 365 ]]; then
        if ! $DRY_RUN; then
            sed -i 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS\t365/' /etc/login.defs
        fi
        fixed "PASS_MAX_DAYS auf 365 gesetzt"
    else
        ok "PASS_MAX_DAYS: ${current_max}"
    fi

    # PASS_MIN_DAYS
    local current_min
    current_min=$(grep "^PASS_MIN_DAYS" /etc/login.defs 2>/dev/null | awk '{print $2}')
    if [[ "${current_min:-0}" -lt 1 ]]; then
        if ! $DRY_RUN; then
            sed -i 's/^PASS_MIN_DAYS.*/PASS_MIN_DAYS\t1/' /etc/login.defs
        fi
        fixed "PASS_MIN_DAYS auf 1 gesetzt"
    else
        ok "PASS_MIN_DAYS: ${current_min}"
    fi

    # PASS_WARN_AGE
    local current_warn
    current_warn=$(grep "^PASS_WARN_AGE" /etc/login.defs 2>/dev/null | awk '{print $2}')
    if [[ "${current_warn:-0}" -lt 7 ]]; then
        if ! $DRY_RUN; then
            sed -i 's/^PASS_WARN_AGE.*/PASS_WARN_AGE\t7/' /etc/login.defs
        fi
        fixed "PASS_WARN_AGE auf 7 gesetzt"
    else
        ok "PASS_WARN_AGE: ${current_warn}"
    fi

    # UMASK
    local current_umask
    current_umask=$(grep "^UMASK" /etc/login.defs 2>/dev/null | awk '{print $2}')
    if [[ "$current_umask" != "027" ]]; then
        if ! $DRY_RUN; then
            sed -i 's/^UMASK.*/UMASK\t\t027/' /etc/login.defs
        fi
        fixed "UMASK auf 027 gesetzt"
    else
        ok "UMASK: $current_umask"
    fi

    # Inaktive Accounts sperren
    local inactive
    inactive=$(useradd -D 2>/dev/null | grep INACTIVE | cut -d= -f2)
    if [[ "${inactive}" == "-1" || -z "${inactive}" ]]; then
        if ! $DRY_RUN; then
            useradd -D -f 30 2>/dev/null || true
        fi
        fixed "Inaktive Accounts werden nach 30 Tagen gesperrt"
    else
        ok "INACTIVE: ${inactive} Tage"
    fi

    # Root-Zugang kontrollieren
    if ask_critical "Root-Account absichern (SSH-Root-Login ist bereits per SSH-Config deaktiviert)" \
        "Prüft ob automatisierte Prozesse root ohne Auth nutzen. Sperrt ggf. direkten Root-Login."; then
        # Shell für root prüfen
        local root_shell
        root_shell=$(grep "^root:" /etc/passwd | cut -d: -f7)
        info "Root-Shell: $root_shell"
        # Wir sperren root nicht komplett – nur Info
        ok "Root-SSH ist über sshd_config deaktiviert"
    fi

    # System-Accounts prüfen
    info "Prüfe System-Accounts..."
    local bad_shells=0
    while IFS=: read -r user _ uid _ _ _ shell; do
        if [[ "$uid" -lt 1000 && "$user" != "root" && "$shell" != "/sbin/nologin" && "$shell" != "/usr/sbin/nologin" && "$shell" != "/bin/false" ]]; then
            warn "System-Account $user (UID $uid) hat Login-Shell: $shell"
            ((bad_shells++)) || true
        fi
    done < /etc/passwd
    if [[ "$bad_shells" -eq 0 ]]; then
        ok "Alle System-Accounts haben nologin-Shell"
    fi

    # Default User Shell Timeout
    local timeout_conf="/etc/profile.d/cis-timeout.sh"
    if [[ ! -f "$timeout_conf" ]]; then
        if ! $DRY_RUN; then
            cat > "$timeout_conf" << 'TMEOF'
# CIS: Shell Timeout nach 900 Sekunden Inaktivität
readonly TMOUT=900
export TMOUT
TMEOF
            chmod 644 "$timeout_conf"
        fi
        fixed "Shell-Timeout auf 900s gesetzt"
    else
        ok "Shell-Timeout bereits konfiguriert"
    fi
}

# ══════════════════════════════════════════════════════════════════════════════
# SEKTION 6: Logging & Auditing
# ══════════════════════════════════════════════════════════════════════════════

harden_integrity() {
    section "6.1 – Integritätsprüfung (AIDE)"

    if rpm -q aide >/dev/null 2>&1; then
        ok "AIDE ist installiert"
    else
        if ! $DRY_RUN; then
            dnf install -y aide >/dev/null 2>&1 || { error "AIDE konnte nicht installiert werden"; return; }
        fi
        fixed "AIDE installiert"
    fi

    if [[ ! -f /var/lib/aide/aide.db.gz ]]; then
        info "AIDE-Datenbank wird initialisiert (kann einige Minuten dauern)..."
        if ! $DRY_RUN; then
            aide --init >/dev/null 2>&1 || { warn "AIDE init fehlgeschlagen"; return; }
            mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz 2>/dev/null || true
        fi
        fixed "AIDE-Datenbank initialisiert"
    else
        ok "AIDE-Datenbank existiert"
    fi

    # AIDE Cron
    if ! crontab -l 2>/dev/null | grep -q "aide.*--check"; then
        if ! $DRY_RUN; then
            echo "0 5 * * * /usr/sbin/aide --check --config /etc/aide.conf" | \
                crontab -l 2>/dev/null | cat - | crontab - 2>/dev/null || \
                echo "0 5 * * * root /usr/sbin/aide --check" >> /etc/crontab
        fi
        fixed "AIDE täglicher Check als Cronjob konfiguriert"
    else
        ok "AIDE Cronjob existiert"
    fi
}

harden_journald() {
    section "6.2 – journald konfigurieren"

    backup_file /etc/systemd/journald.conf

    # Persistente Logs
    if [[ ! -d /var/log/journal ]]; then
        if ! $DRY_RUN; then
            mkdir -p /var/log/journal
            systemd-tmpfiles --create --prefix /var/log/journal 2>/dev/null || true
        fi
        fixed "Persistente Journal-Logs aktiviert"
    else
        ok "Journal-Verzeichnis existiert"
    fi

    # Komprimierung und Speicherlimit
    local jconf="/etc/systemd/journald.conf"
    declare -A jparams=(
        ["Compress"]="yes"
        ["Storage"]="persistent"
        ["ForwardToSyslog"]="no"
    )

    for key in "${!jparams[@]}"; do
        local val="${jparams[$key]}"
        if grep -q "^${key}=${val}" "$jconf" 2>/dev/null; then
            ok "journald $key=$val"
        else
            if ! $DRY_RUN; then
                sed -i "s/^#*${key}=.*/${key}=${val}/" "$jconf" 2>/dev/null || \
                    echo "${key}=${val}" >> "$jconf"
            fi
            fixed "journald $key=$val gesetzt"
        fi
    done

    if ! $DRY_RUN; then
        systemctl restart systemd-journald 2>/dev/null || true
    fi
}

harden_auditd() {
    section "6.3 – auditd konfigurieren"

    # auditd installiert/aktiviert
    if ! rpm -q audit >/dev/null 2>&1; then
        if ! $DRY_RUN; then
            dnf install -y audit >/dev/null 2>&1 || { error "auditd konnte nicht installiert werden"; return; }
        fi
        fixed "auditd installiert"
    fi

    if ! systemctl is-enabled auditd 2>/dev/null | grep -q "enabled"; then
        if ! $DRY_RUN; then
            systemctl enable --now auditd 2>/dev/null || true
        fi
        fixed "auditd aktiviert"
    else
        ok "auditd ist aktiviert"
    fi

    # Audit-Regeln – CIS Level 1
    local rules_file="/etc/audit/rules.d/99-cis-level1.rules"
    if [[ ! -f "$rules_file" ]]; then
        if ! $DRY_RUN; then
            cat > "$rules_file" << 'AUDITEOF'
## CIS Level 1 Audit Rules

# Datei-Integritaet
-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/security/opasswd -p wa -k identity

# Login-Events
-w /var/log/lastlog -p wa -k logins
-w /var/run/faillock -p wa -k logins
-w /var/log/tallylog -p wa -k logins

# sudo Konfiguration
-w /etc/sudoers -p wa -k sudo_changes
-w /etc/sudoers.d -p wa -k sudo_changes

# Netzwerk-Konfiguration
-w /etc/hosts -p wa -k network_changes
-w /etc/sysconfig/network -p wa -k network_changes
-w /etc/sysconfig/network-scripts -p wa -k network_changes

# SELinux
-w /etc/selinux -p wa -k selinux_changes

# Zeitaenderungen
-a always,exit -F arch=b64 -S adjtimex -S settimeofday -k time_change
-a always,exit -F arch=b32 -S adjtimex -S settimeofday -S stime -k time_change
-a always,exit -F arch=b64 -S clock_settime -k time_change
-a always,exit -F arch=b32 -S clock_settime -k time_change
-w /etc/localtime -p wa -k time_change

# User/Group-Aenderungen
-w /etc/login.defs -p wa -k login_changes
-w /etc/securetty -p wa -k login_changes

# Session-Tracking
-w /var/run/utmp -p wa -k session
-w /var/log/wtmp -p wa -k session
-w /var/log/btmp -p wa -k session

# Kernel-Module
-w /sbin/insmod -p x -k kernel_modules
-w /sbin/rmmod -p x -k kernel_modules
-w /sbin/modprobe -p x -k kernel_modules
-a always,exit -F arch=b64 -S init_module -S delete_module -k kernel_modules

# MAC Policy
-w /etc/selinux/ -p wa -k MAC_policy

# Finalize – diese Regel muss am Ende stehen
-e 2
AUDITEOF
            # Regeln laden
            augenrules --load 2>/dev/null || auditctl -R "$rules_file" 2>/dev/null || true
        fi
        fixed "CIS Level 1 Audit-Regeln konfiguriert"
    else
        ok "CIS Audit-Regeln existieren bereits"
    fi

    # Audit-Konfiguration
    local audit_conf="/etc/audit/auditd.conf"
    if [[ -f "$audit_conf" ]]; then
        backup_file "$audit_conf"
        # max_log_file_action
        if ! grep -q "^max_log_file_action = keep_logs" "$audit_conf"; then
            if ! $DRY_RUN; then
                sed -i 's/^max_log_file_action.*/max_log_file_action = keep_logs/' "$audit_conf"
            fi
            fixed "auditd max_log_file_action = keep_logs"
        else
            ok "auditd max_log_file_action OK"
        fi

        # space_left_action
        if ! grep -q "^space_left_action = email" "$audit_conf"; then
            if ! $DRY_RUN; then
                sed -i 's/^space_left_action.*/space_left_action = email/' "$audit_conf"
            fi
            fixed "auditd space_left_action = email"
        fi
    fi
}

harden_logfile_permissions() {
    section "6.2/6.3 – Logfile-Berechtigungen"

    # /var/log Berechtigungen
    find /var/log -type f -perm /g+wx,o+rwx -exec chmod g-wx,o-rwx {} + 2>/dev/null || true
    ok "Logfile-Berechtigungen geprüft und korrigiert"
}

# ══════════════════════════════════════════════════════════════════════════════
#SEKTION 7: System Maintenance
# ══════════════════════════════════════════════════════════════════════════════

harden_system_files() {
    section "7.1 – Systemdatei-Berechtigungen"

    # Kritische Dateien
    declare -A file_perms=(
        ["/etc/passwd"]="644"
        ["/etc/shadow"]="000"
        ["/etc/group"]="644"
        ["/etc/gshadow"]="000"
        ["/etc/passwd-"]="644"
        ["/etc/shadow-"]="000"
        ["/etc/group-"]="644"
        ["/etc/gshadow-"]="000"
    )

    for file in "${!file_perms[@]}"; do
        local expected="${file_perms[$file]}"
        if [[ -f "$file" ]]; then
            local current
            current=$(stat -c "%a" "$file")
            if [[ "$current" != "$expected" ]]; then
                if ! $DRY_RUN; then
                    chmod "$expected" "$file"
                    chown root:root "$file"
                fi
                fixed "$file: $current -> $expected"
            else
                ok "$file: $current"
            fi
        fi
    done

    # SUID/SGID Binaries prüfen (nur Warnung)
    info "Suche SUID/SGID-Binaries (nur Info)..."
    local suid_count
    suid_count=$(find / -xdev \( -perm -4000 -o -perm -2000 \) -type f 2>/dev/null | wc -l)
    info "SUID/SGID-Binaries gefunden: $suid_count (manuell prüfen empfohlen)"
}

harden_user_groups() {
    section "7.2 – User & Group Einstellungen"

    # Prüfe auf Accounts ohne Passwort
    local no_pw
    no_pw=$(awk -F: '($2 == "" ) { print $1 }' /etc/shadow 2>/dev/null || true)
    if [[ -n "$no_pw" ]]; then
        warn "Accounts OHNE Passwort gefunden: $no_pw"
        warn "Bitte manuell sperren: passwd -l <user>"
    else
        ok "Keine Accounts ohne Passwort"
    fi

    # root UID 0 einzig
    local uid0_users
    uid0_users=$(awk -F: '($3 == 0 && $1 != "root") { print $1 }' /etc/passwd)
    if [[ -n "$uid0_users" ]]; then
        warn "Nicht-root Accounts mit UID 0: $uid0_users"
    else
        ok "Nur root hat UID 0"
    fi

    # root GID prüfen
    local root_gid
    root_gid=$(id -g root 2>/dev/null)
    if [[ "$root_gid" == "0" ]]; then
        ok "root GID ist 0"
    else
        warn "root GID ist $root_gid (sollte 0 sein)"
    fi

    # Prüfe PATH in /etc/profile
    if echo "$PATH" | grep -q "::"; then
        warn "PATH enthält leere Einträge (::)"
    elif echo "$PATH" | grep -q ":$"; then
        warn "PATH endet mit Doppelpunkt"
    elif echo "$PATH" | grep -q "\."; then
        warn "PATH enthält relativen Pfad (.)"
    else
        ok "Root-PATH enthält keine unsicheren Einträge"
    fi
}

# ══════════════════════════════════════════════════════════════════════════════
# ZUSAMMENFASSUNG
# ══════════════════════════════════════════════════════════════════════════════

print_summary() {
    echo "" | tee -a "$LOGFILE"
    echo -e "${BOLD}${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}" | tee -a "$LOGFILE"
    echo -e "${BOLD}${CYAN}║                    ZUSAMMENFASSUNG                           ║${NC}" | tee -a "$LOGFILE"
    echo -e "${BOLD}${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}" | tee -a "$LOGFILE"
    echo "" | tee -a "$LOGFILE"
    echo -e "  ${GREEN}Bereits OK:${NC}         $ALREADY_OK" | tee -a "$LOGFILE"
    echo -e "  ${GREEN}Geändert/Fixed:${NC}     $CHANGES_MADE" | tee -a "$LOGFILE"
    echo -e "  ${YELLOW}Übersprungen:${NC}       $SKIPPED" | tee -a "$LOGFILE"
    echo -e "  ${RED}Fehler:${NC}             $ERRORS" | tee -a "$LOGFILE"
    echo "" | tee -a "$LOGFILE"
    echo -e "  Logfile:     ${BOLD}$LOGFILE${NC}" | tee -a "$LOGFILE"
    echo -e "  Backup-Dir:  ${BOLD}$BACKUP_DIR${NC}" | tee -a "$LOGFILE"
    echo "" | tee -a "$LOGFILE"

    if [[ $CHANGES_MADE -gt 0 ]]; then
        echo -e "  ${YELLOW}WICHTIG:${NC}" | tee -a "$LOGFILE"
        echo -e "  - SSH-Verbindung in separater Session testen bevor Logout" | tee -a "$LOGFILE"
        echo -e "  - Reboot empfohlen für Kernel-Modul- und sysctl-Änderungen" | tee -a "$LOGFILE"
        echo -e "  - Rollback: Backup-Verzeichnis $BACKUP_DIR" | tee -a "$LOGFILE"
    fi

    if $DRY_RUN; then
        echo "" | tee -a "$LOGFILE"
        echo -e "  ${YELLOW}=== DRY-RUN: Es wurden KEINE Änderungen vorgenommen ===${NC}" | tee -a "$LOGFILE"
    fi
}

# ══════════════════════════════════════════════════════════════════════════════
# MAIN
# ══════════════════════════════════════════════════════════════════════════════

main() {
    preflight "${1:-}"

    # Sektion 1: Initial Setup
    harden_filesystem_modules
    harden_filesystem_partitions
    harden_package_management
    harden_selinux
    harden_bootloader
    harden_process_hardening
    harden_crypto_policy
    harden_banners

    # Sektion 2: Services
    harden_services
    harden_client_services
    harden_time_sync
    harden_cron

    # Sektion 3: Network
    harden_network_devices
    harden_network_modules
    harden_network_params

    # Sektion 4: Firewall
    harden_firewall

    # Sektion 5: Access Control
    harden_ssh
    harden_sudo
    harden_pam
    harden_password_policy

    # Sektion 6: Logging & Auditing
    harden_integrity
    harden_journald
    harden_auditd
    harden_logfile_permissions

    # Sektion 7: System Maintenance
    harden_system_files
    harden_user_groups

    print_summary
}

main "$@"