Linux 等保三级合规检测脚本

GitHub - llody55/Linux-Security-Compliance-Check: 这是一个用于 Linux 服务器的自动化安全合规检测脚本,遵循中国《GB/T 22239-2019 信息安全技术 网络安全等级保护基本要求》(等保三级)标准。脚本采用纯检测模式,不会修改系统,生成详细的 HTML 报告,覆盖身份鉴别、访问控制、安全审计、入侵防范和网络安全等关键领域。 · GitHub

这是一个用于 Linux 服务器的自动化安全合规检测脚本,遵循中国《GB/T 22239-2019 信息安全技术 网络安全等级保护基本要求》(等保三级)标准。脚本采用纯检测模式,不会修改系统,生成详细的 HTML 报告,覆盖身份鉴别、访问控制、安全审计、入侵防范和网络安全等关键领域。

功能特性

  • 全面检测 :覆盖 18 项合规检查,包括密码策略、SSH 配置、文件权限、审计日志、高危端口等。
  • 跨发行版支持 :兼容 Ubuntu 22.04、CentOS 7+、Kylin 10、openEuler 22.04、BigCloud 22.10 等 Linux 发行版。
  • HTML 报告 :生成美观的 HTML 报告,包含通过/警告/失败状态、合规率和修复建议。
  • 纯检测模式 :不修改系统配置,适用于生产环境。
  • 加权合规率 :根据检查项重要性计算加权合规率。

前置条件

  • 权限要求 :脚本需以 root 或 sudo 权限运行,以访问系统文件和配置。
  • 依赖工具 :
  • bash(建议版本 4.0+)
  • coreutils(提供 statgrepawk 等)
  • net-tools 或 iproute2(提供 netstat 或 ss
  • auditd(用于审计服务检查)
  • 可选:bc(精确合规率计算)、rpm 或 debsums(文件完整性检查)、aide 或 tripwire(高级完整性检查)
  • 支持系统 :Ubuntu、CentOS、Kylin、openEuler、BigCloud 等标准配置的 Linux 系统。

安装步骤

  1. 克隆仓库:
    git clone https://github.com/llody55/Linux-Security-Compliance-Check.git
    cd Linux-Security-Compliance-Check
  2. 确保脚本可执行:
    chmod +x security_check.sh
  3. 安装可选依赖(若缺失):
    • Ubuntu/Debian 系统:
      sudo apt update
      sudo apt install bc net-tools auditd aide
    • CentOS/RHEL 系统:
      sudo yum install bc net-tools auditd aide

使用方法

以 root 或 sudo 权限运行脚本:

sudo ./security_check.sh
  • 脚本将执行合规检测并生成 HTML 报告,保存至 /opt/security_check/<IP>_<YYYYMMDD>.html
  • 日志保存至 /var/log/security_check.log

示例输出

生成的 HTML 报告包含:

  • 概览 :系统信息(操作系统、IP、架构)、总体合规率。
  • 详细检查 :按身份鉴别、访问控制、安全审计、入侵防范、网络安全分类,显示通过/警告/失败状态。
  • 修复建议 :针对不合规项提供可执行的修复命令。
  • 进度条 :直观展示各分类的合规率。

示例报告见 report_example.html

示例报告

参考report_example.html 实际输出文件

支持的检查项

  • 身份鉴别 :
  • 密码策略(最大有效期 ≤ 90 天,最小长度 ≥ 12 等)
  • 密码复杂度(至少 3 类字符,含数字、大写字母等)
  • 账户锁定策略(失败次数 ≤ 5,锁定时间 ≥ 600 秒)
  • 三权账户(sysadmin、auditadmin、securityadmin)
  • 口令加密(SHA512)
  • 默认账户检查
  • 不活跃账户检测(从未登录或最近登录 > 90 天)
  • 访问控制 :
  • 文件权限(/etc/passwd、/etc/shadow 等)
  • SSH 配置(禁用 Root 登录、会话超时 ≤ 300 秒等)
  • 远程访问限制(/etc/hosts.allow、/etc/hosts.deny)
  • 文件系统挂载选项(noexec、nosuid、nodev)
  • 安全审计 :
  • 审计服务状态(auditd)
  • 日志保留(≥ 180 天)
  • 命令历史时间戳
  • 审计规则(监控 /etc/passwd、/etc/shadow 等)
  • 入侵防范 :
  • 文件完整性(使用 rpm、debsums 或 mtime,附最后修改时间)
  • 系统补丁状态
  • 网络安全 :
  • 高危端口(FTP:21、Telnet:23、RDP:3389 等)
  • 防火墙状态(iptables、nftables、ufw、firewalld)

贡献指南

欢迎为项目贡献代码!请按以下步骤操作:

  1. Fork 本仓库。
  2. 创建新分支(git checkout -b feature/your-feature)。
  3. 提交更改(git commit -m "添加新功能")。
  4. 推送分支(git push origin feature/your-feature)。
  5. 提交 Pull Request。

请确保代码遵循 Bash 最佳实践,并为新功能添加测试。

常见问题

  • 问:为何脚本需要 root 权限?
    • 答:需要访问敏感文件(如 /etc/shadow)并执行特定命令(如 rpm -Vf)。
  • 问:如果缺少 bc 命令怎么办?
    • 答:脚本会回退使用 awk 进行计算,但建议安装 bc 以提高精度。
  • 问:可以在非支持的发行版上运行吗?
    • 答:脚本设计具有兼容性,但在非支持系统上可能需调整。请提交 Issue 获取支持。
#!/bin/bash
# 等保三级合规检测脚本
# 作者:llody
# 邮箱:745719408@qq.com
# 兼容:Ubuntu 22.04, CentOS 7+, Kylin 10, openEuler 22.04, BigCloud 22.10
# 功能:纯检测模式,生成HTML报告
# 版本:v3.2
# 使用:sudo ./security_check.sh
# 输出:/opt/security_check/<IP>_<YYYYMMDD>.html
# 日志:/var/log/security_check.log

set -euo pipefail
export PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
export LANG=en_US.UTF-8
declare -A CHECK_RESULTS
declare -A CHECK_DETAILS
declare -A CHECK_WEIGHTS=(
    ["密码策略"]=0.3
    ["SSH配置"]=0.2
    ["用户权限控制"]=0.2
    ["审计服务状态"]=0.15
    ["日志配置"]=0.15
    ["密码复杂度策略"]=0.1
    ["账户登录失败锁定策略"]=0.1
    ["远程管理地址限制检测"]=0.1
    ["三权账户检测"]=0.1
    ["审计日志留存检测"]=0.1
    ["文件完整性检查"]=0.1
    ["账户文件检查"]=0.1
    ["网络端口检查"]=0.1
    ["口令加密算法"]=0.1
    ["默认账户检查"]=0.1
    ["文件系统挂载选项"]=0.1
    ["审计规则检查"]=0.1
    ["系统补丁检查"]=0.1
)

# 日志记录
LOG_FILE="/var/log/security_check.log"
exec 1>>"$LOG_FILE" 2>&1
echo "===== 合规检测开始: $(date +'%Y-%m-%d %H:%M:%S') ====="

if [ $EUID -ne 0 ]; then
    echo "此脚本需要 root 权限运行,请使用 sudo 或切换到 root 用户" >&2
    exit 1
fi

detect_os() {
    if [ -f /etc/os-release ]; then
        . /etc/os-release
        OS_NAME=$ID
        OS_VERSION=$VERSION_ID
        PRETTY_NAME=$PRETTY_NAME
    elif [ -f /etc/centos-release ]; then
        OS_NAME=centos
        OS_VERSION=$(sed -E 's/.* ([0-9])\..*/\1/' /etc/centos-release)
    else
        OS_NAME=unknown
        OS_VERSION=unknown
        PRETTY_NAME=unknown
    fi
}

detect_architecture() {
    ARCH=$(uname -m)
    [ -z "$ARCH" ] && ARCH="未检测到系统架构信息"
}

detect_ip() {
    IP_ADDR=$(/sbin/ip route get 1 | /usr/bin/awk '{print $7}' | /usr/bin/head -1)
    [ -z "$IP_ADDR" ] && IP_ADDR="未检测到IP地址"
}

detect_ip
detect_architecture
REPORT_DIR="/opt/security_check"
REPORT_FILE="${REPORT_DIR}/${IP_ADDR}_$(date +%Y%m%d).html"

if [ ! -d "$REPORT_DIR" ]; then
    mkdir -p "$REPORT_DIR"
    if [ $? -ne 0 ]; then
        echo "无法创建目录 $REPORT_DIR,请检查权限" >&2
        exit 1
    fi
fi

init_report() {
    cat > "$REPORT_FILE" <<EOF
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>服务器合规检测报告</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body { font-family: 'Microsoft YaHei', sans-serif; margin: 2em; }
h1 { color: #2c3e50; border-bottom: 3px solid #3498db; }
h2 { color: #34495e; }
nav { margin-bottom: 1em; }
nav a { margin-right: 1em; color: #3498db; text-decoration: none; }
nav a:hover { text-decoration: underline; }
.check-item { margin: 1em 0; padding: 1em; border-radius: 5px; }
.pass { background: #e8f5e9; border-left: 5px solid #4CAF50; }
.warning { background: #fff3e0; border-left: 5px solid #FFC107; }
.fail { background: #ffebee; border-left: 5px solid #F44336; }
.status-tag { padding: 2px 8px; border-radius: 3px; font-weight: bold; }
.pass-tag { background: #4CAF50; color: white; }
.warning-tag { background: #FFC107; color: black; }
.fail-tag { background: #F44336; color: white; }
.details { margin-left: 20px; color: #666; }
table { width: 100%; border-collapse: collapse; margin-top: 1em; }
th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
canvas { max-width: 400px; margin: 1em 0; }
.progress { background-color: #f3f3f3; border-radius: 5px; height: 20px; margin-bottom: 10px; }
.progress-bar { background-color: #4CAF50; height: 100%; border-radius: 5px; text-align: center; color: white; line-height: 20px; }
</style>
</head>
<body>
<h1>${IP_ADDR}-合规检测报告</h1>
<p>生成时间:$(date +'%Y-%m-%d %H:%M:%S')</p>
<p>操作系统:${PRETTY_NAME}</p>
<p>系统架构:${ARCH}</p>
<p>IP地址:${IP_ADDR}</p>
<nav>
    <a href="#identity">身份鉴别</a> |
    <a href="#access">访问控制</a> |
    <a href="#audit">安全审计</a> |
    <a href="#intrusion">入侵防范</a> |
    <a href="#network">网络安全</a>
</nav>
<h2 id="identity">身份鉴别</h2>
<table>
<tr><th>检查项</th><th>状态</th><th>详情</th></tr>
EOF
}

check_pass() {
    CHECK_RESULTS["$1"]="PASS"
    CHECK_DETAILS["$1"]="$2"
    echo "<tr><td>$1</td><td><span class='status-tag pass-tag'>通过</span></td><td class='details'>$2</td></tr>" >> "$REPORT_FILE"
}

check_warning() {
    CHECK_RESULTS["$1"]="WARNING"
    CHECK_DETAILS["$1"]="$2"
    echo "<tr><td>$1</td><td><span class='status-tag warning-tag'>警告</span></td><td class='details'>$2</td></tr>" >> "$REPORT_FILE"
}

check_fail() {
    CHECK_RESULTS["$1"]="FAIL"
    CHECK_DETAILS["$1"]="$2"
    echo "<tr><td>$1</td><td><span class='status-tag fail-tag'>失败</span></td><td class='details'>$2</td></tr>" >> "$REPORT_FILE"
}

# 密码策略检测(等保三级:身份鉴别,密码策略严格)
check_password_policy() {
    local issue_count=0
    local pass_max_days pass_min_days pass_warn_age pass_min_len

    if [ -r /etc/login.defs ]; then
        pass_max_days=$(grep -E '^PASS_MAX_DAYS' /etc/login.defs | awk '{print $2}' || echo "未配置")
        pass_min_days=$(grep -E '^PASS_MIN_DAYS' /etc/login.defs | awk '{print $2}' || echo "未配置")
        pass_warn_age=$(grep -E '^PASS_WARN_AGE' /etc/login.defs | awk '{print $2}' || echo "未配置")
        pass_min_len=$(grep -E '^PASS_MIN_LEN' /etc/login.defs | awk '{print $2}' || echo "未配置")

        # 小项:密码最大有效期≤30天(三级建议更严格)
        if [[ "$pass_max_days" =~ ^[0-9]+$ ]] && [ "$pass_max_days" -le 90 ]; then
            check_pass "密码最大有效期" "密码最大有效期${pass_max_days}天(符合要求)"
        else
            check_fail "密码最大有效期" "密码最大有效期${pass_max_days}天(建议≤90天)"
            ((issue_count++))
        fi

        # 小项:密码最小修改间隔≥7天
        if [[ "$pass_min_days" =~ ^[0-9]+$ ]] && [ "$pass_min_days" -ge 7 ]; then
            check_pass "密码最小修改间隔" "密码最小修改间隔${pass_min_days}天(符合要求)"
        else
            check_fail "密码最小修改间隔" "密码最小修改间隔${pass_min_days}天(建议≥7天)"
            ((issue_count++))
        fi

        # 小项:密码过期提醒≤7天
        if [[ "$pass_warn_age" =~ ^[0-9]+$ ]] && [ "$pass_warn_age" -le 7 ]; then
            check_pass "密码过期提醒" "密码过期提醒${pass_warn_age}天(符合要求)"
        else
            check_fail "密码过期提醒" "密码过期提醒${pass_warn_age}天(建议≤7天)"
            ((issue_count++))
        fi

        # 小项:密码长度≥12(三级建议更严格)
        if [[ "$pass_min_len" =~ ^[0-9]+$ ]] && [ "$pass_min_len" -ge 12 ]; then
            check_pass "密码最小长度" "密码长度${pass_min_len}(符合要求)"
        else
            check_fail "密码最小长度" "密码长度${pass_min_len}(建议≥12)"
            ((issue_count++))
        fi
    else
        check_fail "密码策略文件" "无法读取 /etc/login.defs(可能无权限或文件不存在)"
        ((issue_count++))
    fi

    # 大项总结
    if [ $issue_count -eq 0 ]; then
        check_pass "密码策略" "密码策略符合等保三级要求"
    else
        check_warning "密码策略" "密码策略存在${issue_count}个问题"
    fi
}

# SSH 安全检测(等保三级:访问控制,SSH 配置安全)
check_ssh_security() {
    local sshd_file="/etc/ssh/sshd_config"
    local issue_count=0

    if [ -r "$sshd_file" ]; then
        # 小项:检查协议版本
        if grep -qE "^Protocol\s+1" "$sshd_file"; then
            check_fail "SSH协议版本" "检测到不安全的SSHv1协议"
            ((issue_count++))
        else
            check_pass "SSH协议版本" "SSH协议版本(符合要求)"
        fi

        # 小项:Root 登录
        if grep -qE "^PermitRootLogin\s+no" "$sshd_file"; then
            check_pass "SSH Root登录" "不允许Root用户登录"
        else
            check_warning "SSH Root登录" "允许Root用户登录(建议设置为no)"
            ((issue_count++))
        fi

        # 小项:会话超时
        local client_alive_interval=$(grep -E "^ClientAliveInterval" "$sshd_file" | awk '{print $2}' || echo "未配置")
        local client_alive_count=$(grep -E "^ClientAliveCountMax" "$sshd_file" | awk '{print $2}' || echo "未配置")
        if [[ "$client_alive_interval" =~ ^[0-9]+$ ]] && [ "$client_alive_interval" -le 300 ] && \
           [[ "$client_alive_count" =~ ^[0-9]+$ ]] && [ "$client_alive_count" -ge 3 ]; then
            check_pass "SSH会话超时" "会话超时配置合理(Interval: ${client_alive_interval}s, Count: ${client_alive_count})"
        else
            check_warning "SSH会话超时" "未配置合理超时(Interval: ${client_alive_interval}, Count: ${client_alive_count}, 建议 Interval≤300, Count≥3)"
            ((issue_count++))
        fi

        # 小项:空密码登录
        if grep -qE "^PermitEmptyPasswords\s+yes" "$sshd_file"; then
            check_fail "SSH空密码登录" "允许空密码登录"
            ((issue_count++))
        else
            check_pass "SSH空密码登录" "不允许空密码登录"
        fi

        # 小项:最大认证尝试次数
        local max_auth_tries=$(grep -E "^MaxAuthTries" "$sshd_file" | awk '{print $2}' || echo "未配置")
        if [[ "$max_auth_tries" =~ ^[0-9]+$ ]] && [ "$max_auth_tries" -le 4 ]; then
            check_pass "SSH最大认证尝试" "最大认证尝试次数${max_auth_tries}(符合要求)"
        else
            check_warning "SSH最大认证尝试" "最大认证尝试次数${max_auth_tries}(建议≤4,PAM优先级可能更高)"  # 改为 warning
        fi
    else
        check_fail "SSH配置文件" "无法读取 $sshd_file(可能无权限或文件不存在)"
        ((issue_count++))
    fi

    # 检查/etc/profile中的TMOUT设置(Shell层面会话超时)
    if [ -r "$profile_file" ]; then
        local tmout_value=$(grep -E "^[[:space:]]*export[[:space:]]+TMOUT=" "$profile_file" | tail -1 | awk -F= '{print $2}' || echo "未配置")
        if [[ "$tmout_value" =~ ^[0-9]+$ ]] && [ "$tmout_value" -le 300 ]; then
            check_pass "Shell会话超时" "TMOUT=${tmout_value}(符合等保要求≤300秒)"
        else
            check_warning "Shell会话超时" "TMOUT=${tmout_value}(建议在/etc/profile中设置export TMOUT=300)"
            ((issue_count++))
        fi
    else
        check_warning "Shell会话超时" "无法读取 $profile_file,无法检查TMOUT设置"
        ((issue_count++))
    fi

    # 大项总结
    if [ $issue_count -eq 0 ]; then
        check_pass "SSH配置" "SSH配置符合等保三级要求"
    else
        check_warning "SSH配置" "SSH配置存在${issue_count}个问题"
    fi
}

# 用户权限检测
check_user_permissions() {
    local issue_count=0
    local sensitive_files=(
        "/etc/passwd:644:root:root"
        "/etc/shadow:640:root:shadow"
        "/etc/group:644:root:root"
        "/etc/sudoers:440:root:root"
    )

    for entry in "${sensitive_files[@]}"; do
        local file=$(echo "$entry" | cut -d':' -f1)
        local max_perm=$(echo "$entry" | cut -d':' -f2)
        local expected_owner=$(echo "$entry" | cut -d':' -f3)
        local expected_group=$(echo "$entry" | cut -d':' -f4)

        if [ ! -f "$file" ]; then
            check_fail "文件存在性-$file" "文件不存在"
            ((issue_count++))
            continue
        fi

        local perm=$(stat -c "%a" "$file" 2>/dev/null || echo "unknown")
        local owner=$(stat -c "%U" "$file" 2>/dev/null || echo "unknown")
        local group=$(stat -c "%G" "$file" 2>/dev/null || echo "unknown")

        if [ "$perm" = "unknown" ]; then
            check_fail "文件权限-$file" "无法获取文件权限(可能无访问权限)"
            ((issue_count++))
            continue
        fi

        if [ "$perm" -le "$max_perm" ]; then
            check_pass "文件权限-$file" "权限(符合要求)(当前:${perm})"
        else
            check_fail "文件权限-$file" "权限过高(当前:${perm},建议:${max_perm})"
            ((issue_count++))
        fi

        # 特殊处理 /etc/shadow 文件的组所有权检查
        if [ "$file" = "/etc/shadow" ]; then
            # 对于 shadow 文件,允许 root:root 或 root:shadow
            if [ "$owner" = "$expected_owner" ] && ( [ "$group" = "root" ] || [ "$group" = "shadow" ] ); then
                check_pass "文件所有者-$file" "所有者(符合要求)(当前:${owner}:${group})"
            else
                check_warning "文件所有者-$file" "所有者或组不正确(当前:${owner}:${group},建议:${expected_owner}:root 或 shadow)"
                ((issue_count++))
            fi
        else
            # 其他文件按原逻辑检查
            if [ "$owner" = "$expected_owner" ] && [ "$group" = "$expected_group" ]; then
                check_pass "文件所有者-$file" "所有者(符合要求)(当前:${owner}:${group})"
            else
                check_warning "文件所有者-$file" "所有者或组不正确(当前:${owner}:${group},建议:${expected_owner}:${expected_group})"
                ((issue_count++))
            fi
        fi
    done

    if [ -f "/etc/sudoers" ]; then
        visudo -c -f /etc/sudoers >/dev/null 2>&1
        if [ $? -eq 0 ]; then
            check_pass "sudoers语法" "sudoers文件语法正确"
        else
            check_fail "sudoers语法" "sudoers文件语法错误"
            ((issue_count++))
        fi
    fi

    local default_configs=("root" "%wheel" "%sudo" "%admin" "llody")
    normalize_line() {
        echo "$1" | sed -E 's/^[[:space:]]+|[[:space:]]+$//g; s/[[:space:]]+/ /g; s/#.*$//; s/[[:space:]]*$//'
    }

    check_sudo_config() {
        local file_path="$1"
        local raw_line="$2"
        local normalized_line=$(normalize_line "$raw_line")
        [ -z "$normalized_line" ] && return
        local entity=$(echo "$normalized_line" | awk '{print $1}')
        for config in "${default_configs[@]}"; do
            [ "$entity" = "$config" ] && return
        done
        if [[ "$normalized_line" =~ ALL=\([[:alnum:]:,]*\)[[:space:]]+.*ALL ]]; then
            check_warning "sudo权限-$file_path" "检测到宽泛sudo权限:${normalized_line}"
            ((issue_count++))
        fi
    }

    if [ -f "/etc/sudoers" ] && [ -r "/etc/sudoers" ]; then
        while IFS= read -r line; do
            check_sudo_config "/etc/sudoers" "$line"
        done < <(grep -E '^[[:space:]]*%?[[:alnum:]_]+[[:space:]]+ALL=\([[:alnum:]:,]*\)[[:space:]]+.*ALL' /etc/sudoers 2>/dev/null)
    else
        check_fail "sudoers访问" "无法访问/etc/sudoers文件(可能不存在或无权限)"
        ((issue_count++))
    fi

    if [ -d "/etc/sudoers.d" ] && [ -r "/etc/sudoers.d" ]; then
        for file in /etc/sudoers.d/*; do
            [ -f "$file" ] && [ -r "$file" ] || continue
            visudo -c -f "$file" >/dev/null 2>&1
            if [ $? -eq 0 ]; then
                check_pass "sudoers.d语法-$file" "sudoers.d文件语法正确"
            else
                check_fail "sudoers.d语法-$file" "sudoers.d文件语法错误"
                ((issue_count++))
            fi
            while IFS= read -r line; do
                check_sudo_config "$file" "$line"
            done < <(grep -E '^[[:space:]]*%?[[:alnum:]_]+[[:space:]]+ALL=\([[:alnum:]:,]*\)[[:space:]]+.*ALL' "$file" 2>/dev/null)
        done
    else
        check_warning "sudoers.d访问" "无法访问/etc/sudoers.d目录(可能不存在或无权限)"
        ((issue_count++))
    fi

    if [ $issue_count -eq 0 ]; then
        check_pass "用户权限控制" "用户权限及sudo配置符合规范"
    else
        check_warning "用户权限控制" "用户权限控制存在${issue_count}个问题"
    fi
}

# 审计服务检测
check_audit_service() {
    local issue_count=0
    if systemctl is-active auditd &>/dev/null; then
        check_pass "审计服务" "审计服务正在运行(auditd)"
    else
        check_fail "审计服务" "审计服务未运行"
        ((issue_count++))
    fi
    if [ $issue_count -eq 0 ]; then
        check_pass "审计服务状态" "审计服务状态(符合要求)"
    else
        check_warning "审计服务状态" "审计服务存在${issue_count}个问题"
    fi
}

# 日志配置检测
check_log_config() {
    local issue_count=0
    if grep -qE "^rotate\s+[0-9]+" /etc/logrotate.conf; then
        local rotate_count=$(grep -E "^rotate\s+[0-9]+" /etc/logrotate.conf | awk '{print $2}')
        if [ "$rotate_count" -ge 6 ]; then
            check_pass "日志保留策略" "日志保留周期${rotate_count}(符合要求)"
        else
            check_warning "日志保留策略" "日志保留周期${rotate_count}(建议≥6)"
            ((issue_count++))
        fi
    else
        check_warning "日志保留策略" "未配置日志保留周期(建议≥6)"
        ((issue_count++))
    fi

    local histtimeformat_found=0
    if grep -q "HISTTIMEFORMAT" /etc/profile; then
        histtimeformat_found=1
    fi
    if [ -d /etc/profile.d/ ] && grep -r "HISTTIMEFORMAT" /etc/profile.d/ >/dev/null 2>&1; then
        histtimeformat_found=1
    fi
    if grep -q "HISTTIMEFORMAT" ~/.bashrc; then
        histtimeformat_found=1
    fi
    if env | grep -q "HISTTIMEFORMAT"; then
        histtimeformat_found=1
    fi

    if [ $histtimeformat_found -eq 1 ]; then
        check_pass "操作审计" "已配置带时间戳的历史命令记录"
    else
        check_warning "操作审计" "未配置带时间戳的历史命令记录"
        ((issue_count++))
    fi

    if [ $issue_count -eq 0 ]; then
        check_pass "日志配置" "日志配置符合基本要求"
    else
        check_warning "日志配置" "日志配置存在${issue_count}个问题"
    fi
}

# 密码复杂度检测
check_password_complexity() {
    local pam_file issue_count=0
    case $OS_NAME in
        ubuntu|debian) pam_file="/etc/pam.d/common-password" ;;
        kylin) pam_file="/etc/security/pwquality.conf" ;;
        centos|rhel|fedora|almalinux|rocky|hce|openEuler|bclinux) pam_file="/etc/pam.d/system-auth" ;;
        *) pam_file="/etc/pam.d/common-password" ;;
    esac

    if [ ! -f "$pam_file" ]; then
        check_fail "密码复杂度配置" "未找到密码复杂度配置文件:$pam_file"
        ((issue_count++))
        return
    fi

    if [ "$OS_NAME" = "kylin" ]; then
        local minlen dcredit ucredit lcredit ocredit minclass
        minlen=$(grep -E '^minlen\s*=\s*[0-9]+' "$pam_file" | awk -F'=' '{print $2}' | tr -d ' ')
        dcredit=$(grep -E '^dcredit\s*=\s*-?[0-9]+' "$pam_file" | awk -F'=' '{print $2}' | tr -d ' ')
        ucredit=$(grep -E '^ucredit\s*=\s*-?[0-9]+' "$pam_file" | awk -F'=' '{print $2}' | tr -d ' ')
        lcredit=$(grep -E '^lcredit\s*=\s*-?[0-9]+' "$pam_file" | awk -F'=' '{print $2}' | tr -d ' ')
        ocredit=$(grep -E '^ocredit\s*=\s*-?[0-9]+' "$pam_file" | awk -F'=' '{print $2}' | tr -d ' ')
        minclass=$(grep -E '^minclass\s*=\s*[0-9]+' "$pam_file" | awk -F'=' '{print $2}' | tr -d ' ')

        if [[ "$minlen" =~ ^[0-9]+$ ]] && [ "$minlen" -ge 10 ]; then
            check_pass "密码最小长度" "密码最小长度${minlen}(符合要求)"
        else
            check_warning "密码最小长度" "密码最小长度${minlen:-未配置}(应≥10)"
            ((issue_count++))
        fi
        if [[ "$dcredit" =~ ^-?[0-9]+$ ]] && [ "$dcredit" -le -1 ]; then
            check_pass "密码数字要求" "密码要求至少1个数字(符合要求)"
        else
            check_warning "密码数字要求" "密码未要求包含数字(当前:${dcredit:-未配置})"
            ((issue_count++))
        fi
        if [[ "$ucredit" =~ ^-?[0-9]+$ ]] && [ "$ucredit" -le -1 ]; then
            check_pass "密码大写字母要求" "密码要求至少1个大写字母(符合要求)"
        else
            check_warning "密码大写字母要求" "密码未要求包含大写字母(当前:${ucredit:-未配置})"
            ((issue_count++))
        fi
        if [[ "$lcredit" =~ ^-?[0-9]+$ ]] && [ "$lcredit" -le -1 ]; then
            check_pass "密码小写字母要求" "密码要求至少1个小写字母(符合要求)"
        else
            check_warning "密码小写字母要求" "密码未要求包含小写字母(当前:${lcredit:-未配置})"
            ((issue_count++))
        fi
        if [[ "$ocredit" =~ ^-?[0-9]+$ ]] && [ "$ocredit" -le -1 ]; then
            check_pass "密码特殊字符要求" "密码要求至少1个特殊字符(符合要求)"
        else
            check_warning "密码特殊字符要求" "密码未要求包含特殊字符(当前:${ocredit:-未配置})"
            ((issue_count++))
        fi
        if [[ "$minclass" =~ ^[0-9]+$ ]] && [ "$minclass" -ge 3 ]; then
            check_pass "密码字符类别要求" "密码要求至少${minclass}类字符(符合要求)"
        else
            check_warning "密码字符类别要求" "密码字符类别${minclass:-未配置}(应≥3)"
            ((issue_count++))
        fi
    else
        local pam_module=$(grep -E '^password\s+requisite\s+pam_(cracklib|pwquality).so' "$pam_file")
        if [ -z "$pam_module" ]; then
            check_fail "密码复杂度策略" "未配置密码复杂度要求"
            ((issue_count++))
        else
            if echo "$pam_module" | grep -qE 'minlen=([1-9][0-9]+|[1-9][0-9]*)'; then  # 修正正则,匹配≥10
                local minlen_val=$(echo "$pam_module" | sed -n 's/.*minlen=\([0-9]*\).*/\1/p')
                if [ "$minlen_val" -ge 10 ]; then  # 提升至≥10
                    check_pass "密码最小长度" "密码最小长度${minlen_val}(符合要求)"
                else
                    check_warning "密码最小长度" "密码最小长度${minlen_val:-未配置}(建议≥10)"
                    ((issue_count++))
                fi
            else
                check_warning "密码最小长度" "未配置密码最小长度(建议≥12)"
                ((issue_count++))
            fi
            if echo "$pam_module" | grep -q 'dcredit=-1'; then
                check_pass "密码数字要求" "密码包含数字要求(符合要求)"
            else
                check_warning "密码数字要求" "密码未要求包含数字"
                ((issue_count++))
            fi
            if echo "$pam_module" | grep -q 'ucredit=-1'; then
                check_pass "密码大写字母要求" "密码包含大写字母要求(符合要求)"
            else
                check_warning "密码大写字母要求" "密码未要求包含大写字母"
                ((issue_count++))
            fi
            if echo "$pam_module" | grep -q 'lcredit=-1'; then
                check_pass "密码小写字母要求" "密码包含小写字母要求(符合要求)"
            else
                check_warning "密码小写字母要求" "密码未要求包含小写字母"
                ((issue_count++))
            fi
            if echo "$pam_module" | grep -q 'ocredit=-1'; then
                check_pass "密码特殊字符要求" "密码包含特殊字符要求(符合要求)"
            else
                check_warning "密码特殊字符要求" "密码未要求包含特殊字符"
                ((issue_count++))
            fi
        fi
    fi

    if [ $issue_count -eq 0 ]; then
        check_pass "密码复杂度策略" "密码复杂度配置(符合要求)"
    else
        check_warning "密码复杂度策略" "密码复杂度策略存在${issue_count}个问题"
    fi
}

# 账户锁定策略检测
check_account_lockout_policy() {
    local pam_file issue_count=0
    case $OS_NAME in
        ubuntu|debian) pam_file="/etc/pam.d/common-auth" ;;
        centos|rhel|fedora|almalinux|rocky|kylin|hce|openEuler|bclinux) pam_file="/etc/pam.d/system-auth" ;;
        *) pam_file="/etc/pam.d/common-auth" ;;
    esac

    if [ ! -f "$pam_file" ]; then
        check_fail "账户登录失败锁定策略" "未找到账户锁定配置文件:$pam_file"
        ((issue_count++))
        return
    fi

    local pam_module=$(grep -E '^auth\s+required\s+pam_(faillock|tally2).so' "$pam_file")
    if [ -z "$pam_module" ]; then
        check_fail "账户登录失败锁定策略" "未配置账户锁定策略"
        ((issue_count++))
    else
        if echo "$pam_module" | grep -q 'deny=[1-9]'; then
            check_pass "账户登录失败次数限制" "已配置失败次数限制"
        else
            check_warning "账户登录失败次数限制" "未配置失败次数限制(建议deny=5)"
            ((issue_count++))
        fi
        if echo "$pam_module" | grep -q 'unlock_time=[1-9][0-9]*'; then
            check_pass "账户锁定时间" "已配置账户锁定时间"
        else
            check_warning "账户锁定时间" "未配置账户锁定时间(建议unlock_time=600)"
            ((issue_count++))
        fi
    fi

    if [ $issue_count -eq 0 ]; then
        check_pass "账户登录失败锁定策略" "账户锁定策略配置(符合要求)"
    else
        check_warning "账户登录失败锁定策略" "账户锁定策略存在${issue_count}个问题"
    fi
}

# 远程管理地址限制检测
check_remote_access_restriction() {
    local hosts_allow="/etc/hosts.allow"
    local hosts_deny="/etc/hosts.deny"
    local issue_count=0

    if [ "$OS_NAME" = "kylin" ] || [ "$OS_NAME" = "hce" ] || [ "$OS_NAME" = "openEuler" ] || [ "$OS_NAME" = "bclinux" ]; then
        check_pass "远程管理地址限制检测" "国产操作系统无需配置hosts.allow/deny"
        return
    fi

    local ip_regex='\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b'
    local cidr_regex='\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\/\d{1,2}\b'
    local allow_regex="^sshd:\s*($ip_regex|$cidr_regex)"

    if [ -f "$hosts_allow" ]; then
        if grep -qE "$allow_regex" "$hosts_allow"; then
            check_pass "远程管理地址限制-允许" "已配置允许访问的合法IP地址或网段"
        else
            check_warning "远程管理地址限制-允许" "/etc/hosts.allow 未配置允许访问的合法 IP 地址或网段"
            ((issue_count++))
        fi
    else
        check_fail "远程管理地址限制-允许" "未找到 /etc/hosts.allow 文件"
        ((issue_count++))
    fi

    if [ -f "$hosts_deny" ]; then
        if grep -qE '^sshd:\s*[Aa][Ll][Ll]' "$hosts_deny"; then
            check_pass "远程管理地址限制-拒绝" "已配置拒绝所有其他主机的访问"
        else
            check_warning "远程管理地址限制-拒绝" "/etc/hosts.deny 未配置拒绝所有其他主机的访问"
            ((issue_count++))
        fi
    else
        check_fail "远程管理地址限制-拒绝" "未找到 /etc/hosts.deny 文件"
        ((issue_count++))
    fi

    if [ $issue_count -eq 0 ]; then
        check_pass "远程管理地址限制检测" "远程管理地址限制配置(符合要求)"
    else
        check_warning "远程管理地址限制检测" "远程管理地址限制存在${issue_count}个问题"
    fi
}

# 三权分力账户检测
check_three_roles_accounts() {
    local roles=("sysadmin" "auditadmin" "securityadmin")
    local issue_count=0

    for role in "${roles[@]}"; do
        if ! id "$role" &>/dev/null; then
            check_warning "账户-$role" "未找到账户:$role(建议创建职责分离账户)"  # 改为 warning
            ((issue_count++))
        else
            check_pass "账户-$role" "账户 $role 存在"
            # 统一检查 sudo/wheel 组权限
            if groups "$role" | grep -qE '(sudo|wheel)'; then
                check_pass "账户权限-$role" "账户 $role 已授予最小权限(sudo/wheel 组)"
            else
                check_warning "账户权限-$role" "账户 $role 未授予最小权限(未加入sudo/wheel组)"
                ((issue_count++))
            fi
        fi
    done

    if [ $issue_count -eq 0 ]; then
        check_pass "三权账户检测" "三权账户配置(符合要求)"
    else
        check_warning "三权账户检测" "三权账户存在${issue_count}个问题"
    fi
}

# 审计日志留存检测
check_audit_log_retention() {
    local auditd_conf="/etc/audit/auditd.conf"
    local logrotate_conf="/etc/logrotate.d/audit"
    local issue_count=0

    if [ -f "$auditd_conf" ]; then
        local num_logs_value=$(grep -E '^num_logs\s*=\s*[0-9]+' "$auditd_conf" | awk -F'=' '{print $2}' | tr -d ' ')
        if [[ "$num_logs_value" =~ ^[0-9]+$ ]] && [ "$num_logs_value" -ge 26 ]; then
            check_pass "审计日志保留数量" "已配置保留至少${num_logs_value}个日志文件"
        else
            check_warning "审计日志保留数量" "未配置保留至少26个日志文件(当前:${num_logs_value:-未配置},建议≥180天)"
            ((issue_count++))
        fi
    else
        check_fail "审计日志配置文件" "未找到auditd配置文件:$auditd_conf"
        ((issue_count++))
    fi

    if [ -f "$logrotate_conf" ]; then
        local rotate_value=$(grep -E '^\s*rotate\s+[0-9]+' "$logrotate_conf" | awk '{print $2}')
        if [[ "$rotate_value" =~ ^[0-9]+$ ]] && [ "$rotate_value" -ge 12 ]; then
            check_pass "审计日志轮转保留" "logrotate已配置保留至少${rotate_value}个日志文件"
        else
            check_warning "审计日志轮转保留" "logrotate未配置保留至少12个日志文件(当前:${rotate_value:-未配置})"
            ((issue_count++))
        fi
        if grep -qE '^\s*daily\b' "$logrotate_conf"; then
            check_pass "审计日志轮转频率" "logrotate已配置每天轮转日志"
        else
            check_warning "审计日志轮转频率" "logrotate未配置每天轮转日志"
            ((issue_count++))
        fi
    else
        check_fail "审计日志轮转配置文件" "未找到logrotate配置文件:$logrotate_conf"
        ((issue_count++))
    fi

    if [ $issue_count -eq 0 ]; then
        check_pass "审计日志留存检测" "审计日志留存配置(符合要求)"
    else
        check_warning "审计日志留存检测" "审计日志留存存在${issue_count}个问题"
    fi
}

# 文件完整性检测
check_file_integrity() {
    local critical_files=(
        "/bin/bash"
        "/usr/bin/sudo"
        "/etc/passwd"
        "/etc/shadow"
        "/etc/group"
        "/etc/sudoers"
        "/etc/ssh/sshd_config"
    )
    local issue_count=0

    if command -v rpm &>/dev/null; then
        for file in "${critical_files[@]}"; do
            if [ -f "$file" ]; then
                if rpm -Vf "$file" &>/dev/null; then
                    check_pass "文件完整性-$file" "文件未被修改"
                else
                    local mtime=$(stat -c '%Y' "$file" 2>/dev/null)
                    local modified_time=$(date -d @$mtime +'%Y-%m-%d %H:%M:%S' 2>/dev/null)
                    check_warning "文件完整性-$file" "系统关键文件可能被修改(最后修改时间: ${modified_time:-未知})"
                    ((issue_count++))
                fi
            else
                check_warning "文件完整性-$file" "文件不存在"
                ((issue_count++))
            fi
        done
    elif command -v debsums &>/dev/null; then
        for file in "${critical_files[@]}"; do
            if [ -f "$file" ]; then
                if debsums -e "$file" &>/dev/null; then
                    check_pass "文件完整性-$file" "文件未被修改"
                else
                    local mtime=$(stat -c '%Y' "$file" 2>/dev/null)
                    local modified_time=$(date -d @$mtime +'%Y-%m-%d %H:%M:%S' 2>/dev/null)
                    check_warning "文件完整性-$file" "系统关键文件可能被修改(最后修改时间: ${modified_time:-未知})"
                    ((issue_count++))
                fi
            else
                check_warning "文件完整性-$file" "文件不存在"
                ((issue_count++))
            fi
        done
    else
        for file in "${critical_files[@]}"; do
            if [ -f "$file" ]; then
                local mtime=$(stat -c %Y "$file")
                local now=$(date +%s)
                local modified_time=$(date -d @$mtime +'%Y-%m-%d %H:%M:%S' 2>/dev/null)
                if [ $((now - mtime)) -lt 86400 ]; then
                    check_warning "文件完整性-$file" "文件最近被修改(24小时内)(最后修改时间: ${modified_time:-未知})"
                    ((issue_count++))
                else
                    check_pass "文件完整性-$file" "文件未在最近24小时内修改"
                fi
            else
                check_warning "文件完整性-$file" "文件不存在"
                ((issue_count++))
            fi
        done
    fi

    if command -v aide &>/dev/null || command -v tripwire &>/dev/null; then
        check_pass "文件完整性工具" "已安装文件完整性检查工具(AIDE/Tripwire)"
    else
        check_warning "文件完整性工具" "未安装文件完整性检查工具(建议安装AIDE或Tripwire)"
        ((issue_count++))
    fi

    if [ $issue_count -eq 0 ]; then
        check_pass "文件完整性检查" "关键文件完整性检查通过"
    else
        check_warning "文件完整性检查" "文件完整性检查存在${issue_count}个问题"
    fi
}

# 账户文件检测
check_account_files() {
    local issue_count=0
    if [ -f "/etc/shadow" ] && [ -r "/etc/shadow" ]; then
        if grep -qE '^[[:alnum:]_-]+::' /etc/shadow 2>/dev/null; then
            check_fail "账户安全-空密码" "发现空密码账户"
            ((issue_count++))
        else
            check_pass "账户安全-空密码" "未发现空密码账户"
        fi
        if grep -qE '^[[:alnum:]_-]+:\$1\$' /etc/shadow 2>/dev/null; then
            check_warning "账户安全-密码哈希" "发现使用弱MD5哈希的账户"
            ((issue_count++))
        else
            check_pass "账户安全-密码哈希" "未发现弱MD5哈希的账户"
        fi
        local locked_accounts=$(grep -cE '^[[:alnum:]_-]+:!!' /etc/shadow 2>/dev/null)
        if [ "$locked_accounts" -gt 0 ]; then
            check_pass "账户安全-锁定账户" "发现${locked_accounts}个锁定账户(符合要求)"
        else
            check_warning "账户安全-锁定账户" "未发现锁定账户,可能存在未锁定的默认或不必要账户"
            ((issue_count++))
        fi
    else
        check_fail "账户安全-shadow文件" "未找到或无法读取/etc/shadow文件"
        ((issue_count++))
    fi

    if [ -f "/etc/passwd" ] && [ -r "/etc/passwd" ]; then
        local super_users=$(grep -cE '^[[:alnum:]_-]+:[^:]*:0:' /etc/passwd 2>/dev/null)
        if [ "$super_users" -le 1 ]; then
            check_pass "账户安全-超级用户" "仅root账户具有UID 0(符合要求)"
        else
            check_fail "账户安全-超级用户" "发现多个UID为0的账户"
            ((issue_count++))
        fi
        if grep -qE '^[[:alnum:]_-]+::' /etc/passwd 2>/dev/null; then
            check_fail "账户安全-无密码" "发现无密码账户"
            ((issue_count++))
        else
            check_pass "账户安全-无密码" "未发现无密码账户"
        fi
    else
        check_fail "账户安全-passwd文件" "未找到或无法读取/etc/passwd文件"
        ((issue_count++))
    fi

    # 不活跃账户扫描(使用 lastlog 检查从未登录或最近登录>90天的账户,排除 uid<1000 系统账户)
    local inactive_accounts=""
    local now=$(date +%s)
    while read -r line; do
        local user=$(echo "$line" | awk '{print $1}')
        local last_login=$(echo "$line" | awk '{print $NF}')
        local uid=$(id -u "$user" 2>/dev/null)
        if [ -n "$uid" ] && [ "$uid" -ge 1000 ]; then  # 仅检查普通用户
            if [[ "$last_login" == "Never" ]]; then
                inactive_accounts+="$user (从未登录) "
            elif [[ "$last_login" =~ [0-9]+ ]]; then
                local last_time=$(date -d "$last_login" +%s 2>/dev/null)
                if [ $((now - last_time)) -gt 7776000 ]; then  # 90天 = 90*86400
                    inactive_accounts+="$user (最近登录: $last_login) "
                fi
            fi
        fi
    done < <(lastlog -t 99999 2>/dev/null | tail -n +2)  # lastlog 输出排除标题行

    if [ -n "$inactive_accounts" ]; then
        check_warning "账户安全-不活跃账户" "发现不活跃账户: $inactive_accounts(建议锁定或删除)"
        ((issue_count++))
    else
        check_pass "账户安全-不活跃账户" "未发现不活跃账户"
    fi

    if [ $issue_count -eq 0 ]; then
        check_pass "账户文件检查" "账户文件检查通过"
    else
        check_warning "账户文件检查" "账户文件检查存在${issue_count}个问题"
    fi
}

# 高危端口检测(等保三级:网络安全,边界防护)
check_dangerous_ports() {
    local absolute_dangerous_ports=("21" "23" "135" "137" "138" "139" "445" "873" "1433" "1521" "2049" "3389" "5900" "11211")  # 绝对高危:FTP/Telnet/RPC/NetBIOS/SMB/rsync/MSSQL/Oracle/NFS/RDP/VNC/Memcached
    local potential_dangerous_ports=("22" "80" "443" "3306" "5432" "6379" "7001" "7002" "8000" "8008" "8080" "8081" "8443" "9200" "27017")  # 潜在高危:SSH/HTTP/HTTPS/MySQL/PostgreSQL/Redis/WebLogic/HTTP variants/Elasticsearch/MongoDB
    local issue_count=0

    # 检测命令选择
    local net_cmd
    if command -v ss >/dev/null 2>&1; then
        net_cmd="ss -tuln"
    elif command -v netstat >/dev/null 2>&1; then
        net_cmd="netstat -tuln"
    elif command -v lsof >/dev/null 2>&1; then
        net_cmd="lsof -i -P -n | grep LISTEN"
    else
        check_warning "端口检测工具" "无法检测端口(未找到ss/netstat/lsof命令)"
        ((issue_count++))
        if [ $issue_count -eq 0 ]; then
            check_pass "网络端口检查" "网络端口检查通过"
        else
            check_warning "网络端口检查" "网络端口检查存在${issue_count}个问题"
        fi
        return
    fi

    # 检测开放端口
    local open_absolute=() open_potential=()
    for port in "${absolute_dangerous_ports[@]}"; do
        if $net_cmd | grep -qE "(:$port\b|\.$port\b)"; then
            open_absolute+=("$port")
        fi
    done
    for port in "${potential_dangerous_ports[@]}"; do
        if $net_cmd | grep -qE "(:$port\b|\.$port\b)"; then
            open_potential+=("$port")
        fi
    done

    # 小项报告
    if [ ${#open_absolute[@]} -gt 0 ]; then
        check_fail "绝对高危端口" "发现开放的绝对高危端口: ${open_absolute[*]}(建议立即关闭)"
        ((issue_count++))
        echo "<div class='details'><p>绝对高危端口详情:</p><ul>" >> "$REPORT_FILE"
        for port in "${open_absolute[@]}"; do
            local service=$(grep -w "$port/tcp" /etc/services | awk '{print $1}' | head -1 || echo "未知")
            echo "<li>端口 $port: $service(建议禁用相关服务或关闭端口)</li>" >> "$REPORT_FILE"
        done
        echo "</ul></div>" >> "$REPORT_FILE"
    else
        check_pass "绝对高危端口" "未发现开放的绝对高危端口"
    fi

    if [ ${#open_potential[@]} -gt 0 ]; then
        check_warning "潜在高危端口" "发现开放的潜在高危端口: ${open_potential[*]}(评估是否必要)"
        ((issue_count++))
        echo "<div class='details'><p>潜在高危端口详情:</p><ul>" >> "$REPORT_FILE"
        for port in "${open_potential[@]}"; do
            local service=$(grep -w "$port/tcp" /etc/services | awk '{print $1}' | head -1 || echo "未知")
            echo "<li>端口 $port: $service(必要时限制IP访问)</li>" >> "$REPORT_FILE"
        done
        echo "</ul></div>" >> "$REPORT_FILE"
    else
        check_pass "潜在高危端口" "未发现开放的潜在高危端口"
    fi

    # 防火墙检查(兼容多 OS)
    local fw_active=0
    if command -v iptables >/dev/null 2>&1 && iptables -L -n >/dev/null 2>&1; then
        fw_active=1
    elif command -v nft >/dev/null 2>&1 && nft list ruleset >/dev/null 2>&1; then
        fw_active=1
    elif command -v ufw >/dev/null 2>&1 && ufw status | grep -q "active"; then
        fw_active=1
    elif command -v firewall-cmd >/dev/null 2>&1 && firewall-cmd --state >/dev/null 2>&1; then
        fw_active=1
    fi
    if [ $fw_active -eq 0 ]; then
        check_warning "防火墙状态" "防火墙未启用或无法检测(建议启用 iptables/nftables/ufw/firewalld)"
        ((issue_count++))
    else
        check_pass "防火墙状态" "防火墙已启用"
    fi

    # 大项总结
    if [ $issue_count -eq 0 ]; then
        check_pass "网络端口检查" "网络端口检查符合等保三级要求"
    else
        check_warning "网络端口检查" "网络端口检查存在${issue_count}个问题"
    fi
}

# 口令加密算法检测
check_password_encryption() {
    local issue_count=0
    if [ -f /etc/login.defs ] && [ -r /etc/login.defs ]; then
        if grep -q '^ENCRYPT_METHOD\s+SHA512' /etc/login.defs; then
            check_pass "口令加密算法" "使用SHA512加密算法(符合要求)"
        else
            local current_method=$(grep '^ENCRYPT_METHOD' /etc/login.defs | awk '{print $2}')
            check_warning "口令加密算法" "未使用SHA512加密算法(当前:${current_method:-未配置})"
            ((issue_count++))
        fi
    else
        check_fail "口令加密算法" "无法读取 /etc/login.defs(可能无权限或文件不存在)"
        ((issue_count++))
    fi

    if [ $issue_count -eq 0 ]; then
        check_pass "口令加密算法检查" "口令加密算法(符合要求)"
    else
        check_warning "口令加密算法检查" "口令加密算法存在${issue_count}个问题"
    fi
}

# 默认账户检测
check_default_accounts() {
    local default_accounts=("games" "ftp" "nobody" "news" "uucp")
    local issue_count=0

    for account in "${default_accounts[@]}"; do
        if id "$account" &>/dev/null; then
            if grep -q "^${account}:.*:!!" /etc/shadow 2>/dev/null; then
                check_pass "默认账户-$account" "默认账户 $account 已锁定"
            else
                check_warning "默认账户-$account" "默认账户 $account 未锁定或启用"
                ((issue_count++))
            fi
        else
            check_pass "默认账户-$account" "默认账户 $account 不存在"
        fi
    done

    if [ $issue_count -eq 0 ]; then
        check_pass "默认账户检查" "默认账户检查通过"
    else
        check_warning "默认账户检查" "默认账户检查存在${issue_count}个问题"
    fi
}

# 增强版文件系统挂载选项检测
check_filesystem_mounts() {
    local issue_count=0
    local critical_mounts=("/tmp:noexec,nosuid,nodev" "/var/tmp:noexec,nosuid,nodev" "/dev/shm:nosuid,nodev" "/home:nodev,nosuid")

    for mount in "${critical_mounts[@]}"; do
        local path=$(echo "$mount" | cut -d':' -f1)
        local options=$(echo "$mount" | cut -d':' -f2 | tr ',' ' ')
        
        # 检查当前实际挂载选项(而不仅仅是fstab配置)
        if mount | grep -qE "\s+$path\s+"; then
            local current_options=$(mount | grep -E "\s+$path\s+" | awk '{print $6}' | tr ',' ' ')
            
            for opt in $options; do
                if echo "$current_options" | grep -qw "$opt"; then
                    check_pass "挂载选项-$path-$opt" "$path 已配置 $opt 选项"
                else
                    check_warning "挂载选项-$path-$opt" "$path 未配置 $opt 选项(等保要求)"
                    ((issue_count++))
                fi
            done
        else
            check_warning "挂载选项-$path" "$path 未挂载或无法检测"
            ((issue_count++))
        fi
    done

    if [ $issue_count -eq 0 ]; then
        check_pass "文件系统挂载选项" "文件系统挂载选项符合等保要求"
    else
        check_warning "文件系统挂载选项" "文件系统挂载选项存在${issue_count}个问题(不符合等保要求)"
    fi
}

# 新增:审计规则检测
check_audit_rules() {
    local audit_rules_file="/etc/audit/audit.rules"
    local issue_count=0
    local critical_audits=(
        "-w /etc/passwd -p wa -k passwd_changes"
        "-w /etc/shadow -p wa -k shadow_changes"
        "-w /etc/group -p wa -k group_changes"
        "-a always,exit -F arch=b64 -S execve -k exec"
    )

    if [ -f "$audit_rules_file" ] && [ -r "$audit_rules_file" ]; then
        for rule in "${critical_audits[@]}"; do
            if grep -qF "$rule" "$audit_rules_file"; then
                check_pass "审计规则-$(echo "$rule" | awk '{print $2}')" "已配置审计规则:$rule"
            else
                check_warning "审计规则-$(echo "$rule" | awk '{print $2}')" "未配置审计规则:$rule"
                ((issue_count++))
            fi
        done
    else
        check_fail "审计规则文件" "未找到或无法读取 $audit_rules_file"
        ((issue_count++))
    fi

    if [ $issue_count -eq 0 ]; then
        check_pass "审计规则检查" "审计规则配置(符合要求)"
    else
        check_warning "审计规则检查" "审计规则检查存在${issue_count}个问题"
    fi
}

# 新增:系统补丁检查
check_system_patches() {
    local issue_count=0
    if [ "$OS_NAME" = "centos" ] || [ "$OS_NAME" = "rhel" ] || [ "$OS_NAME" = "bclinux" ]; then
        if yum check-update &>/dev/null; then
            check_pass "系统补丁" "系统补丁已是最新"
        else
            check_warning "系统补丁" "存在可更新的系统补丁"
            ((issue_count++))
        fi
    elif [ "$OS_NAME" = "ubuntu" ] || [ "$OS_NAME" = "debian" ]; then
        if apt list --upgradable 2>/dev/null | grep -q "upgradable"; then
            check_warning "系统补丁" "存在可更新的系统补丁"
            ((issue_count++))
        else
            check_pass "系统补丁" "系统补丁已是最新"
        fi
    else
        check_warning "系统补丁" "无法检测系统补丁(不支持的系统:$OS_NAME)"
        ((issue_count++))
    fi

    if [ $issue_count -eq 0 ]; then
        check_pass "系统补丁检查" "系统补丁检查通过"
    else
        check_warning "系统补丁检查" "系统补丁检查存在${issue_count}个问题"
    fi
}

# 生成统计信息和修复建议(等保三级:报告汇总)
generate_statistics() {
    # 关闭身份鉴别表格,生成后续分类表格
    echo "</table>" >> "$REPORT_FILE"
    echo "<h2 id=\"access\">访问控制</h2><table><tr><th>检查项</th><th>状态</th><th>详情</th></tr>" >> "$REPORT_FILE"
    check_user_permissions
    check_remote_access_restriction
    check_filesystem_mounts
    echo "</table>" >> "$REPORT_FILE"
    echo "<h2 id=\"audit\">安全审计</h2><table><tr><th>检查项</th><th>状态</th><th>详情</th></tr>" >> "$REPORT_FILE"
    check_audit_service
    check_log_config
    check_audit_log_retention
    check_audit_rules
    echo "</table>" >> "$REPORT_FILE"
    echo "<h2 id=\"intrusion\">入侵防范</h2><table><tr><th>检查项</th><th>状态</th><th>详情</th></tr>" >> "$REPORT_FILE"
    check_file_integrity
    check_system_patches
    echo "</table>" >> "$REPORT_FILE"
    echo "<h2 id=\"network\">网络安全</h2><table><tr><th>检查项</th><th>状态</th><th>详情</th></tr>" >> "$REPORT_FILE"
    check_dangerous_ports
    echo "</table>" >> "$REPORT_FILE"

    # 统计合规率
    local total=${#CHECK_RESULTS[@]}
    local pass_count=0 warn_count=0 fail_count=0
    local identity_pass=0 identity_total=0 access_pass=0 access_total=0
    local audit_pass=0 audit_total=0 intrusion_pass=0 intrusion_total=0 network_pass=0 network_total=0

    for check in "${!CHECK_RESULTS[@]}"; do
        case ${CHECK_RESULTS[$check]} in
            PASS) ((pass_count++));;
            WARNING) ((warn_count++));;
            FAIL) ((fail_count++));;
        esac
        if [[ $check =~ ^(密码|账户|三权|口令) ]]; then
            ((identity_total++))
            [ "${CHECK_RESULTS[$check]}" = "PASS" ] && ((identity_pass++))
        elif [[ $check =~ ^(用户权限|远程管理|挂载) ]]; then
            ((access_total++))
            [ "${CHECK_RESULTS[$check]}" = "PASS" ] && ((access_pass++))
        elif [[ $check =~ ^(审计|日志) ]]; then
            ((audit_total++))
            [ "${CHECK_RESULTS[$check]}" = "PASS" ] && ((audit_pass++))
        elif [[ $check =~ ^(文件完整性|系统补丁) ]]; then
            ((intrusion_total++))
            [ "${CHECK_RESULTS[$check]}" = "PASS" ] && ((intrusion_pass++))
        elif [[ $check =~ ^(网络端口|绝对高危端口|潜在高危端口|防火墙状态) ]]; then
            ((network_total++))
            [ "${CHECK_RESULTS[$check]}" = "PASS" ] && ((network_pass++))
        fi
    done

    # 检查 bc 命令可用性
    local bc_available=1
    if ! command -v bc >/dev/null 2>&1; then
        bc_available=0
        echo "<li>计算工具: 缺少 bc 命令,使用整数计算,可能影响精度,建议安装 bc: <code>$([ "$OS_NAME" = "centos" ] && echo 'yum install bc -y' || echo 'apt install bc -y')</code></li>" >> "$REPORT_FILE"
    fi

    # 计算合规率(bc 或 awk 后备)
    local compliance_rate
    if [ $bc_available -eq 1 ]; then
        compliance_rate=$(bc <<< "scale=2; $pass_count*100/$total")
    else
        compliance_rate=$(awk "BEGIN {printf \"%.2f\", $pass_count*100/$total}")
    fi

    local weighted_compliance=0
    for check in "${!CHECK_RESULTS[@]}"; do
        local weight=${CHECK_WEIGHTS[$check]:-0.05}
        if [ "${CHECK_RESULTS[$check]}" = "PASS" ]; then
            if [ $bc_available -eq 1 ]; then
                weighted_compliance=$(bc <<< "$weighted_compliance + $weight")
            else
                weighted_compliance=$(awk "BEGIN {printf \"%.2f\", $weighted_compliance + $weight}")
            fi
        fi
    done
    if [ $bc_available -eq 1 ]; then
        weighted_compliance=$(bc <<< "scale=2; $weighted_compliance * 100")
    else
        weighted_compliance=$(awk "BEGIN {printf \"%.2f\", $weighted_compliance * 100}")
    fi

    local identity_rate=$([ $identity_total -gt 0 ] && ( [ $bc_available -eq 1 ] && bc <<< "scale=2; $identity_pass*100/$identity_total" || awk "BEGIN {printf \"%.2f\", $identity_pass*100/$identity_total}") || echo 0)
    local access_rate=$([ $access_total -gt 0 ] && ( [ $bc_available -eq 1 ] && bc <<< "scale=2; $access_pass*100/$access_total" || awk "BEGIN {printf \"%.2f\", $access_pass*100/$access_total}") || echo 0)
    local audit_rate=$([ $audit_total -gt 0 ] && ( [ $bc_available -eq 1 ] && bc <<< "scale=2; $audit_pass*100/$audit_total" || awk "BEGIN {printf \"%.2f\", $audit_pass*100/$audit_total}") || echo 0)
    local intrusion_rate=$([ $intrusion_total -gt 0 ] && ( [ $bc_available -eq 1 ] && bc <<< "scale=2; $intrusion_pass*100/$intrusion_total" || awk "BEGIN {printf \"%.2f\", $intrusion_pass*100/$intrusion_total}") || echo 0)
    local network_rate=$([ $network_total -gt 0 ] && ( [ $bc_available -eq 1 ] && bc <<< "scale=2; $network_pass*100/$network_total" || awk "BEGIN {printf \"%.2f\", $network_pass*100/$network_total}") || echo 0)

    # 生成统计信息(使用 CSS 进度条)
    cat >> "$REPORT_FILE" <<EOF
<h2>统计信息</h2>
<div class="stats">
<p>总检查项:$total</p>
<p>通过项:<span style="color:#4CAF50">$pass_count</span></p>
<p>警告项:<span style="color:#FFC107">$warn_count</span></p>
<p>失败项:<span style="color:#F44336">$fail_count</span></p>
<p>合规率:<strong>${compliance_rate}%</strong></p>
<div class="progress"><div class="progress-bar" style="width: ${compliance_rate}%">${compliance_rate}% 通过</div></div>
<p>加权合规率:<strong>${weighted_compliance}%</strong></p>
<div class="progress"><div class="progress-bar" style="width: 100%">${weighted_compliance}% 加权</div></div>
<h3>分类合规率</h3>
<ul>
    <li>身份鉴别:${identity_rate}% ($identity_pass/$identity_total)
        <div class="progress"><div class="progress-bar" style="width: ${identity_rate}%">${identity_rate}%</div></div>
    </li>
    <li>访问控制:${access_rate}% ($access_pass/$access_total)
        <div class="progress"><div class="progress-bar" style="width: ${access_rate}%">${access_rate}%</div></div>
    </li>
    <li>安全审计:${audit_rate}% ($audit_pass/$audit_total)
        <div class="progress"><div class="progress-bar" style="width: ${audit_rate}%">${audit_rate}%</div></div>
    </li>
    <li>入侵防范:${intrusion_rate}% ($intrusion_pass/$intrusion_total)
        <div class="progress"><div class="progress-bar" style="width: ${intrusion_rate}%">${intrusion_rate}%</div></div>
    </li>
    <li>网络安全:${network_rate}% ($network_pass/$network_total)
        <div class="progress"><div class="progress-bar" style="width: ${network_rate}%">${network_rate}%</div></div>
    </li>
</ul>
</div>
<h2>修复建议摘要</h2>
<ul>
EOF

    # 定义全局上下文变量(需在检查函数中设置)
    : ${OPEN_PORTS:=""}  # 由 check_dangerous_ports 设置,格式 "21 23 445"
    : ${MISSING_RULES:=""}  # 由 check_audit_rules 设置,格式 "-w /etc/passwd -p wa -k passwd_changes"
    : ${EMPTY_PASSWD_ACCOUNTS:=""}  # 由 check_account_security 设置,格式 "user1 user2"
    : ${DEFAULT_ACCOUNTS:="games ftp nobody"}  # 默认账户列表
    : ${CRITICAL_MOUNTS:="/tmp:noexec,nosuid /var/tmp:noexec,nosuid /dev/shm:nosuid"}  # 挂载点和选项
    : ${SENSITIVE_FILES:="/etc/passwd:644:root:root /etc/shadow:600:root:shadow /etc/group:644:root:root /etc/sudoers:440:root:root"}  # 敏感文件

    # 动态生成修复建议
    for check in "${!CHECK_RESULTS[@]}"; do
        if [[ ${CHECK_RESULTS[$check]} == "FAIL" || ${CHECK_RESULTS[$check]} == "WARNING" ]]; then
            case $check in
                "密码最大有效期")
                    echo "<li>$check: 设置最大有效期为30天: <code>sed -i '/^PASS_MAX_DAYS/s/.*/PASS_MAX_DAYS 30/' /etc/login.defs</code></li>" >> "$REPORT_FILE"
                    ;;
                "密码最小修改间隔")
                    echo "<li>$check: 设置最小修改间隔为7天: <code>sed -i '/^PASS_MIN_DAYS/s/.*/PASS_MIN_DAYS 7/' /etc/login.defs</code></li>" >> "$REPORT_FILE"
                    ;;
                "密码过期提醒")
                    echo "<li>$check: 设置过期提醒为7天: <code>sed -i '/^PASS_WARN_AGE/s/.*/PASS_WARN_AGE 7/' /etc/login.defs</code></li>" >> "$REPORT_FILE"
                    ;;
                "密码最小长度")
                    echo "<li>$check: 设置最小长度为12: <code>sed -i '/^PASS_MIN_LEN/s/.*/PASS_MIN_LEN 12/' /etc/login.defs</code></li>" >> "$REPORT_FILE"
                    ;;
                "密码策略文件")
                    echo "<li>$check: 检查文件权限和存在性: <code>touch /etc/login.defs; chmod 644 /etc/login.defs; chown root:root /etc/login.defs</code></li>" >> "$REPORT_FILE"
                    ;;
                "SSH协议版本")
                    echo "<li>$check: 移除 Protocol 1 并重启: <code>sed -i '/^Protocol 1/d' /etc/ssh/sshd_config; systemctl restart sshd</code></li>" >> "$REPORT_FILE"
                    ;;
                "SSH Root登录")
                    echo "<li>$check: 禁用 Root 登录: <code>sed -i '/^#*PermitRootLogin/s/.*/PermitRootLogin no/' /etc/ssh/sshd_config; systemctl restart sshd</code></li>" >> "$REPORT_FILE"
                    ;;
                "SSH会话超时")
                    echo "<li>$check: 设置超时为300秒和3次: <code>sed -i '/^#*ClientAliveInterval/s/.*/ClientAliveInterval 300/' /etc/ssh/sshd_config; sed -i '/^#*ClientAliveCountMax/s/.*/ClientAliveCountMax 3/' /etc/ssh/sshd_config; systemctl restart sshd</code></li>" >> "$REPORT_FILE"
                    ;;
                "SSH空密码登录")
                    echo "<li>$check: 禁用空密码登录: <code>sed -i '/^#*PermitEmptyPasswords/s/.*/PermitEmptyPasswords no/' /etc/ssh/sshd_config; systemctl restart sshd</code></li>" >> "$REPORT_FILE"
                    ;;
                "SSH最大认证尝试")
                    echo "<li>$check: 设置最大认证尝试为4: <code>sed -i '/^#*MaxAuthTries/s/.*/MaxAuthTries 4/' /etc/ssh/sshd_config; systemctl restart sshd</code></li>" >> "$REPORT_FILE"
                    ;;
                "SSH配置文件")
                    echo "<li>$check: 检查文件权限: <code>touch /etc/ssh/sshd_config; chmod 600 /etc/ssh/sshd_config; chown root:root /etc/ssh/sshd_config</code></li>" >> "$REPORT_FILE"
                    ;;
                "文件权限-"*)
                    local file=$(echo "$check" | cut -d'-' -f2-)
                    local max_perm=$(echo "$SENSITIVE_FILES" | grep "$file" | cut -d':' -f2)
                    echo "<li>$check: 设置权限为 $max_perm: <code>chmod $max_perm $file</code></li>" >> "$REPORT_FILE"
                    ;;
                "文件所有者-"*)
                    local file=$(echo "$check" | cut -d'-' -f2-)
                    local owner_group=$(echo "$SENSITIVE_FILES" | grep "$file" | cut -d':' -f3-4)
                    echo "<li>$check: 设置所有者和组为 $owner_group: <code>chown $owner_group $file</code></li>" >> "$REPORT_FILE"
                    ;;
                "sudoers语法"|"sudoers访问")
                    echo "<li>$check: 检查 /etc/sudoers 语法和权限: <code>visudo; chmod 440 /etc/sudoers; chown root:root /etc/sudoers</code></li>" >> "$REPORT_FILE"
                    ;;
                "sudoers.d访问")
                    echo "<li>$check: 设置 /etc/sudoers.d 目录权限: <code>mkdir -p /etc/sudoers.d; chmod 750 /etc/sudoers.d; chown root:root /etc/sudoers.d</code></li>" >> "$REPORT_FILE"
                    ;;
                "审计服务")
                    echo "<li>$check: 启用 auditd 服务: <code>systemctl start auditd; systemctl enable auditd</code></li>" >> "$REPORT_FILE"
                    ;;
                "日志保留策略")
                    echo "<li>$check: 设置 rotate 为26: <code>sed -i '/^rotate/s/.*/rotate 26/' /etc/logrotate.conf</code></li>" >> "$REPORT_FILE"
                    ;;
                "操作审计")
                    echo "<li>$check: 添加 HISTTIMEFORMAT: <code>echo 'export HISTTIMEFORMAT=\"%F %T \"' >> /etc/profile; source /etc/profile</code></li>" >> "$REPORT_FILE"
                    ;;
                "密码复杂度配置"|"密码数字要求"|"密码大写字母要求"|"密码小写字母要求"|"密码特殊字符要求"|"密码字符类别")
                    echo "<li>$check: 配置 pam_pwquality.so: <code>echo 'password requisite pam_pwquality.so minlen=12 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 minclass=4' >> $pam_file</code></li>" >> "$REPORT_FILE"
                    ;;
                "账户登录失败锁定策略"|"账户登录失败次数限制"|"账户锁定时间")
                    echo "<li>$check: 配置 pam_faillock.so: <code>echo 'auth required pam_faillock.so preauth silent deny=5 unlock_time=600' >> $pam_file; echo 'auth [default=die] pam_faillock.so authfail deny=5 unlock_time=600' >> $pam_file</code></li>" >> "$REPORT_FILE"
                    ;;
                "远程管理地址限制-允许")
                    echo "<li>$check: 限制 sshd 访问: <code>echo 'sshd: 192.168.0.0/24' >> /etc/hosts.allow</code></li>" >> "$REPORT_FILE"
                    ;;
                "远程管理地址限制-拒绝")
                    echo "<li>$check: 拒绝所有 sshd 访问: <code>echo 'sshd: ALL' >> /etc/hosts.deny</code></li>" >> "$REPORT_FILE"
                    ;;
                "账户-"*)
                    local role=$(echo "$check" | cut -d'-' -f2)
                    echo "<li>$check: 创建 $role 账户并加入 sudo 组: <code>useradd $role; usermod -aG sudo $role</code></li>" >> "$REPORT_FILE"
                    ;;
                "账户权限-"*)
                    local role=$(echo "$check" | cut -d'-' -f2)
                    echo "<li>$check: 将 $role 加入 sudo 组: <code>usermod -aG sudo $role</code></li>" >> "$REPORT_FILE"
                    ;;
                "审计日志保留数量")
                    echo "<li>$check: 设置 num_logs 为26: <code>sed -i '/^num_logs/s/.*/num_logs = 26/' /etc/audit/auditd.conf</code></li>" >> "$REPORT_FILE"
                    ;;
                "审计日志轮转保留")
                    echo "<li>$check: 设置 rotate 为26: <code>sed -i '/rotate/s/.*/rotate 26/' /etc/logrotate.d/audit</code></li>" >> "$REPORT_FILE"
                    ;;
                "审计日志轮转频率")
                    echo "<li>$check: 设置 daily 轮转: <code>sed -i '/weekly/s/.*/daily/' /etc/logrotate.d/audit</code></li>" >> "$REPORT_FILE"
                    ;;
                "审计日志轮转配置文件")
                    echo "<li>$check: 创建审计日志轮转配置: <code>echo '/var/log/audit/audit.log { daily rotate 26 }' > /etc/logrotate.d/audit</code></li>" >> "$REPORT_FILE"
                    ;;
                "审计日志配置文件")
                    echo "<li>$check: 检查文件权限: <code>touch /etc/audit/auditd.conf; chmod 640 /etc/audit/auditd.conf; chown root:root /etc/audit/auditd.conf</code></li>" >> "$REPORT_FILE"
                    ;;
                "文件完整性-"*)
                    local file=$(echo "$check" | cut -d'-' -f2-)
                    echo "<li>$check: 验证文件完整性: <code>$([ "$OS_NAME" = "centos" ] && echo "rpm -Vf $file" || echo "debsums -e $file")</code></li>" >> "$REPORT_FILE"
                    ;;
                "文件完整性工具")
                    echo "<li>$check: 安装 AIDE: <code>$([ "$OS_NAME" = "centos" ] && echo 'yum install aide -y' || echo 'apt install aide -y')</code></li>" >> "$REPORT_FILE"
                    ;;
                "账户安全-空密码")
                    if [ -n "$EMPTY_PASSWD_ACCOUNTS" ]; then
                        echo "<li>$check: 锁定空密码账户 ($EMPTY_PASSWD_ACCOUNTS): <code>echo '$EMPTY_PASSWD_ACCOUNTS' | xargs -n1 passwd -l</code></li>" >> "$REPORT_FILE"
                    else
                        echo "<li>$check: 锁定所有空密码账户: <code>awk -F: '(\$2 == \"\") {print \$1}' /etc/shadow | xargs -n1 passwd -l</code></li>" >> "$REPORT_FILE"
                    fi
                    ;;
                "账户安全-密码哈希")
                    echo "<li>$check: 升级密码哈希到 SHA512: <code>sed -i '/^ENCRYPT_METHOD/s/.*/ENCRYPT_METHOD SHA512/' /etc/login.defs; passwd [用户名]</code></li>" >> "$REPORT_FILE"
                    ;;
                "账户安全-锁定账户")
                    echo "<li>$check: 锁定非必要账户: <code>passwd -l [用户名]</code></li>" >> "$REPORT_FILE"
                    ;;
                "账户安全-超级用户")
                    echo "<li>$check: 移除非 root UID 0 账户: <code>awk -F: '(\$3 == 0 && \$1 != \"root\") {print \$1}' /etc/passwd | xargs -n1 userdel</code></li>" >> "$REPORT_FILE"
                    ;;
                "账户安全-passwd文件")
                    echo "<li>$check: 检查 /etc/passwd 权限: <code>chmod 644 /etc/passwd; chown root:root /etc/passwd</code></li>" >> "$REPORT_FILE"
                    ;;
                "账户安全-shadow文件")
                    echo "<li>$check: 检查 /etc/shadow 权限: <code>chmod 600 /etc/shadow; chown root:shadow /etc/shadow</code></li>" >> "$REPORT_FILE"
                    ;;
                "绝对高危端口")
                    if [ -n "$OPEN_PORTS" ]; then
                        echo "<li>$check: 关闭高危端口 ($OPEN_PORTS): <code>for port in $OPEN_PORTS; do firewall-cmd --permanent --remove-port=\$port/tcp; done; firewall-cmd --reload</code></li>" >> "$REPORT_FILE"
                    else
                        echo "<li>$check: 关闭高危端口: <code>firewall-cmd --permanent --remove-port={port}/tcp; firewall-cmd --reload</code></li>" >> "$REPORT_FILE"
                    fi
                    ;;
                "潜在高危端口")
                    if [ -n "$OPEN_PORTS" ]; then
                        echo "<li>$check: 限制潜在高危端口 ($OPEN_PORTS): <code>for port in $OPEN_PORTS; do firewall-cmd --permanent --add-rich-rule='rule family=\"ipv4\" source address=\"192.168.0.0/24\" port port=\"\$port\" protocol=\"tcp\" accept'; done; firewall-cmd --reload</code></li>" >> "$REPORT_FILE"
                    else
                        echo "<li>$check: 限制潜在高危端口: <code>firewall-cmd --permanent --add-rich-rule='rule family=\"ipv4\" source address=\"192.168.0.0/24\" port port=\"{port}\" protocol=\"tcp\" accept'; firewall-cmd --reload</code></li>" >> "$REPORT_FILE"
                    fi
                    ;;
                "防火墙状态")
                    echo "<li>$check: 启用防火墙: <code>$([ "$OS_NAME" = "centos" ] && echo 'systemctl start firewalld; systemctl enable firewalld' || echo 'ufw enable')</code></li>" >> "$REPORT_FILE"
                    ;;
                "端口检测工具")
                    echo "<li>$check: 安装 ss 或 netstat: <code>$([ "$OS_NAME" = "centos" ] && echo 'yum install net-tools -y' || echo 'apt install net-tools -y')</code></li>" >> "$REPORT_FILE"
                    ;;
                "口令加密算法")
                    echo "<li>$check: 设置 ENCRYPT_METHOD SHA512: <code>sed -i '/^ENCRYPT_METHOD/s/.*/ENCRYPT_METHOD SHA512/' /etc/login.defs</code></li>" >> "$REPORT_FILE"
                    ;;
                "默认账户-"*)
                    local account=$(echo "$check" | cut -d'-' -f2)
                    echo "<li>$check: 禁用或删除账户 $account: <code>passwd -l $account || userdel $account</code></li>" >> "$REPORT_FILE"
                    ;;
                "挂载选项-"*)
                    local path_opt=$(echo "$check" | cut -d'-' -f2-)
                    local path=$(echo "$path_opt" | cut -d'-' -f1)
                    local opt=$(echo "$path_opt" | cut -d'-' -f2)
                    if [ -n "$opt" ]; then
                        echo "<li>$check: 为 $path 添加 $opt 选项: <code>sed -i '/$path/s/defaults/defaults,$opt/' /etc/fstab; mount -o remount $path</code></li>" >> "$REPORT_FILE"
                    else
                        echo "<li>$check: 配置 $path 挂载: <code>echo '$path $path tmpfs defaults,noexec,nosuid 0 0' >> /etc/fstab; mount -o remount $path</code></li>" >> "$REPORT_FILE"
                    fi
                    ;;
                "审计规则-"*)
                    local rule_name=$(echo "$check" | cut -d'-' -f2-)
                    if [ -n "$MISSING_RULES" ]; then
                        echo "<li>$check: 添加缺失的审计规则 ($rule_name): <code>echo '$MISSING_RULES' >> /etc/audit/audit.rules; systemctl restart auditd</code></li>" >> "$REPORT_FILE"
                    else
                        echo "<li>$check: 添加审计规则: <code>echo '-w $rule_name -p wa -k ${rule_name}_changes' >> /etc/audit/audit.rules; systemctl restart auditd</code></li>" >> "$REPORT_FILE"
                    fi
                    ;;
                "审计规则文件")
                    echo "<li>$check: 检查审计规则文件权限: <code>touch /etc/audit/audit.rules; chmod 640 /etc/audit/audit.rules; chown root:root /etc/audit/audit.rules</code></li>" >> "$REPORT_FILE"
                    ;;
                "系统补丁")
                    echo "<li>$check: 更新系统补丁: <code>$([ "$OS_NAME" = "centos" ] && echo 'yum update -y' || echo 'apt update && apt upgrade -y')</code></li>" >> "$REPORT_FILE"
                    ;;
                *)
                    echo "<li>$check: 暂无具体建议,请手动检查配置</li>" >> "$REPORT_FILE"
                    ;;
            esac
        fi
    done

    echo "</ul></body></html>" >> "$REPORT_FILE"
}

main() {
    detect_os
    init_report
    check_account_files
    check_password_policy
    check_password_complexity
    check_account_lockout_policy
    check_three_roles_accounts
    check_password_encryption
    check_default_accounts
    check_ssh_security
    generate_statistics
    echo "检测报告已生成:$REPORT_FILE"
    echo "===== 合规检测结束: $(date +'%Y-%m-%d %H:%M:%S') =====" >> "$LOG_FILE"
}

main

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值