#!/bin/bash

# ═══════════════════════════════════════════════════════════════
#  Linux系统安全配置脚本 - Docker兼容版 v2.0
#  功能：系统检测、防火墙配置、端口管理、fail2ban配置
#  针对代理服务器优化，减少对正常流量的影响
#  兼容Docker，使用iptables-legacy
# ═══════════════════════════════════════════════════════════════
#
#  用法: bash inition.sh [选项]
#    --dry-run       模拟运行，不实际修改系统
#    --uninstall     卸载/还原所有配置
#    --non-interactive  非交互模式，使用默认配置全部执行
#    --help          显示帮助信息
#
# ═══════════════════════════════════════════════════════════════

# 不使用 set -e，避免与 || true 模式冲突导致意外退出
# set -u: 引用未定义变量时报错；pipefail: 管道中任意命令失败都记录退出码
set -uo pipefail

# ═══════════════════════════════════════════════════════════════
# [A1] 可配置变量 - 根据需要修改以下参数
# ═══════════════════════════════════════════════════════════════

# --- 网络端口配置 ---
SSH_PORT="22"                          # 默认SSH端口（稍后动态检测覆盖）
OPEN_TCP_PORTS=(12748 80 443 5222 7777 8080 8007)  # 需要开放的TCP端口
MANAGEMENT_PORTS=(8006)                # 管理端口（应用DDoS防护）
FTP_PORTS=(20 21)                      # FTP端口
FTP_PASSIVE_RANGE="30000:31000"        # FTP被动模式端口范围
HIGH_PORT_START=10000                  # 高端口范围起始
HIGH_PORT_END=65535                    # 高端口范围结束

# --- DDoS 防护参数 ---
DDOS_WINDOW=300                        # 检测窗口（秒）
DDOS_THRESHOLD=100                     # 窗口内最大新连接数

# --- fail2ban 配置 ---
F2B_MAXRETRY=5                         # 最大重试次数
F2B_FINDTIME=600                       # 查找窗口（秒）
F2B_BANTIME=-1                         # 封禁时长（-1=永久）
F2B_WHITELIST="127.0.0.1/8 ::1"       # 默认白名单
F2B_WHITELIST_FILE="/etc/fail2ban/whitelist.local"  # 额外白名单文件路径
F2B_BANACTION="iptables-multiport"     # [S4] 封禁方式：iptables-multiport(推荐,只封目标端口)|iptables-allports(封全部)|iptables-ipset(高性能)
F2B_JAILS_EXTRA=()                     # [F8] 额外启用的 jail（可加: nginx-http-auth postfix recidive）；默认留空，仅在安装对应服务后再填入

# --- 恶意网段封禁 ---
BAD_SUBNET_THRESHOLD=10                # 超过此次数失败登录的 /24 网段将被封禁

# --- 基础工具列表 ---
BASIC_TOOLS_APT=(btop net-tools curl wget mtr-tiny traceroute)  # [Q7] 改为数组，避免分词歧义
BASIC_TOOLS_YUM=(btop net-tools curl wget mtr traceroute)

# --- 自定义二进制工具 [name]=url ---
CUSTOM_TOOL_NAMES=("bandwhich" "tcping")
CUSTOM_TOOL_URLS=(
    "https://dl.lierye.shop/BAK/bandwhich"
    "https://dl.lierye.shop/Tools/tcping"
)
CUSTOM_TOOLS_DIR="/usr/local/bin"

# --- 外部脚本 ---
EXTERNAL_SCRIPT_URL="https://dl.lierye.shop/Tools/Linux_tools.sh"
EXTERNAL_SCRIPT_SHA256=""              # 留空跳过校验；填入哈希值则启用完整性校验
EXTERNAL_SCRIPT_MAX_RETRIES=3

# --- 日志配置 ---
LOG_FILE="/var/log/inition.log"

# --- sysctl 优化 ---
ENABLE_BBR=true                        # 是否启用 TCP BBR 拥塞控制
ENABLE_ZRAM=true                       # [F6] 内存 ≤ 2GB 时优先使用 zram 替代 swapfile（压缩内存，延迟更低）

# --- 时区配置 ---
DEFAULT_TIMEZONE="Asia/Shanghai"       # 内网IP或检测失败时的默认时区

# ═══════════════════════════════════════════════════════════════
# 运行模式（由命令行参数设置）
# ═══════════════════════════════════════════════════════════════
DRY_RUN=false
UNINSTALL_MODE=false
INTERACTIVE_MODE=true

# 模块开关（默认全开，交互菜单可调整）
MOD_UPDATE=true
MOD_BASIC_TOOLS=true
MOD_CUSTOM_TOOLS=true
MOD_RSYSLOG=true
MOD_FIREWALL=true
MOD_IPV6_FIREWALL=true
MOD_FAIL2BAN=true
MOD_EXTERNAL_SCRIPT=true
MOD_SYSCTL=true
MOD_SWAP=true
MOD_TIMEZONE=true
MOD_BLOCK_SUBNETS=false                # 默认不执行，需用户确认

# ═══════════════════════════════════════════════════════════════
# 颜色定义
# ═══════════════════════════════════════════════════════════════
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
MAGENTA='\033[0;35m'
BOLD='\033[1m'
DIM='\033[2m'
NC='\033[0m'

# ═══════════════════════════════════════════════════════════════
# 进度跟踪 [E1]
# ═══════════════════════════════════════════════════════════════
TOTAL_STEPS=0
CURRENT_STEP=0

# ═══════════════════════════════════════════════════════════════
# 回滚数据 [C1]
# ═══════════════════════════════════════════════════════════════
IPTABLES_BACKUP_FILE=""
IP6TABLES_BACKUP_FILE=""

# ═══════════════════════════════════════════════════════════════
# OS / 包管理器（由 detect_os 设置）
# ═══════════════════════════════════════════════════════════════
OS=""
PACKAGE_MANAGER=""

# ═══════════════════════════════════════════════════════════════
# 帮助信息与参数解析 [A3] [E4]
# ═══════════════════════════════════════════════════════════════
show_help() {
    echo "用法: bash inition.sh [选项]"
    echo ""
    echo "选项:"
    echo "  --dry-run            模拟运行，只输出将要执行的操作，不实际修改系统"
    echo "  --uninstall          卸载/还原所有由本脚本配置的内容"
    echo "  --non-interactive    非交互模式，使用默认配置全部执行"
    echo "  --help               显示此帮助信息"
    echo ""
    echo "可配置变量说明："
    echo "  编辑脚本头部的 '可配置变量' 区域即可自定义端口、阈值等参数"
    exit 0
}

parse_args() {
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --dry-run)
                DRY_RUN=true
                shift
                ;;
            --uninstall)
                UNINSTALL_MODE=true
                shift
                ;;
            --non-interactive)
                INTERACTIVE_MODE=false
                shift
                ;;
            --help|-h)
                show_help
                ;;
            *)
                echo "未知参数: $1"
                show_help
                ;;
        esac
    done
}

# ═══════════════════════════════════════════════════════════════
# 日志函数 [C2] 同时输出到终端和日志文件
# ═══════════════════════════════════════════════════════════════
log_to_file() {
    local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
    echo "$msg" >> "$LOG_FILE" 2>/dev/null
}

log_info() {
    echo -e "${GREEN}[INFO]${NC} $1"
    log_to_file "[INFO] $1"
}

log_warn() {
    echo -e "${YELLOW}[WARN]${NC} $1"
    log_to_file "[WARN] $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
    log_to_file "[ERROR] $1"
}

log_step() {
    echo -e "${BLUE}[STEP]${NC} $1"
    log_to_file "[STEP] $1"
}

log_dry() {
    echo -e "${MAGENTA}[DRY-RUN]${NC} $1"
    log_to_file "[DRY-RUN] $1"
}

# ═══════════════════════════════════════════════════════════════
# 进度显示 [E1]
# ═══════════════════════════════════════════════════════════════
init_progress() {
    TOTAL_STEPS=$1
    CURRENT_STEP=0
}

advance_step() {
    local desc="$1"
    ((CURRENT_STEP++))
    # [Q5] 防止除零
    local pct=0
    [[ "$TOTAL_STEPS" -gt 0 ]] && pct=$((CURRENT_STEP * 100 / TOTAL_STEPS))
    # 生成进度条
    local bar_len=25
    local filled=$((pct * bar_len / 100))
    local empty=$((bar_len - filled))
    local bar=""
    local i
    for ((i=0; i<filled; i++)); do bar+="█"; done
    for ((i=0; i<empty; i++)); do bar+="░"; done
    echo ""
    echo -e "${CYAN}━━━━ [ ${bar} ] ${pct}%  (${CURRENT_STEP}/${TOTAL_STEPS}) ${BOLD}${desc}${NC} ${CYAN}━━━━${NC}"
    log_to_file "[PROGRESS ${CURRENT_STEP}/${TOTAL_STEPS}] ${desc}"
}

# ═══════════════════════════════════════════════════════════════
# Dry-run 执行包装器 [A3]
# 仅用于会修改系统状态的命令；只读/检测命令不需要包装
# ═══════════════════════════════════════════════════════════════
execute() {
    if [[ "$DRY_RUN" == true ]]; then
        log_dry "  $*"
        return 0
    fi
    "$@"
}

# ═══════════════════════════════════════════════════════════════
# 回滚机制 [C1]
# ═══════════════════════════════════════════════════════════════
backup_iptables() {
    log_info "备份当前 iptables 规则..."
    # [S3] 备份目录改到 /var/backups/inition/，避免 /tmp 权限问题
    local backup_dir="/var/backups/inition"
    mkdir -p "$backup_dir"
    chmod 700 "$backup_dir"

    IPTABLES_BACKUP_FILE="${backup_dir}/iptables_backup_$(date +%Y%m%d_%H%M%S).rules"
    iptables-save > "$IPTABLES_BACKUP_FILE" 2>/dev/null || true
    chmod 600 "$IPTABLES_BACKUP_FILE" 2>/dev/null || true
    log_info "IPv4 规则已备份到: $IPTABLES_BACKUP_FILE"

    if command -v ip6tables &>/dev/null; then
        IP6TABLES_BACKUP_FILE="${backup_dir}/ip6tables_backup_$(date +%Y%m%d_%H%M%S).rules"
        ip6tables-save > "$IP6TABLES_BACKUP_FILE" 2>/dev/null || true
        chmod 600 "$IP6TABLES_BACKUP_FILE" 2>/dev/null || true
        log_info "IPv6 规则已备份到: $IP6TABLES_BACKUP_FILE"
    fi
}

rollback_iptables() {
    log_warn "正在回滚 iptables 规则到备份状态..."
    if [[ -n "$IPTABLES_BACKUP_FILE" && -f "$IPTABLES_BACKUP_FILE" ]]; then
        iptables-restore < "$IPTABLES_BACKUP_FILE" 2>/dev/null || true
        log_info "IPv4 规则已回滚"
    fi
    if [[ -n "$IP6TABLES_BACKUP_FILE" && -f "$IP6TABLES_BACKUP_FILE" ]]; then
        ip6tables-restore < "$IP6TABLES_BACKUP_FILE" 2>/dev/null || true
        log_info "IPv6 规则已回滚"
    fi
}

cleanup_backup() {
    rm -f "$IPTABLES_BACKUP_FILE" 2>/dev/null || true
    rm -f "$IP6TABLES_BACKUP_FILE" 2>/dev/null || true
}

# ═══════════════════════════════════════════════════════════════
# 网络连通性预检 [C4]
# ═══════════════════════════════════════════════════════════════
check_network() {
    log_step "检测网络连通性..."

    local test_urls=("https://dl.lierye.shop" "https://www.baidu.com" "https://www.google.com")

    for url in "${test_urls[@]}"; do
        if curl -fsSL --connect-timeout 5 --max-time 10 -o /dev/null "$url" 2>/dev/null; then
            log_info "网络连通性正常 (${url})"
            return 0
        fi
    done

    # 备选方案：用 ping 测试
    if ping -c 1 -W 3 8.8.8.8 &>/dev/null; then
        log_warn "HTTPS 访问受限但网络可达，部分下载功能可能失败"
        return 0
    fi

    log_error "网络连通性检测失败，无法访问外部资源"
    log_error "请检查网络连接或 DNS 配置后重试"
    return 1
}

# ═══════════════════════════════════════════════════════════════
# [A2] 通用二进制文件下载安装函数（消除重复代码）
# ═══════════════════════════════════════════════════════════════
download_and_install_binary() {
    local name="$1"
    local url="$2"
    local dest="${3:-$CUSTOM_TOOLS_DIR}"

    log_info "下载并安装 ${name}..."

    # 检查是否已安装 [C3]
    if command -v "$name" &>/dev/null; then
        log_info "✓ ${name} 已安装，跳过"
        return 0
    fi

    # [Q8] 使用 mktemp 生成唯一临时文件，避免并发冲突
    local tmp_file
    tmp_file=$(mktemp "/tmp/inition_bin_XXXXXX") || { log_warn "mktemp 失败，跳过 ${name}"; return 1; }

    # 选择下载工具
    if command -v wget &>/dev/null; then
        execute wget -q -O "$tmp_file" "$url"
    elif command -v curl &>/dev/null; then
        execute curl -fsSL -o "$tmp_file" "$url"
    else
        log_warn "没有可用的下载工具（wget/curl），跳过 ${name}"
        return 1
    fi

    if [[ "$DRY_RUN" == true ]]; then
        return 0
    fi

    if [[ -f "$tmp_file" ]]; then
        chmod +x "$tmp_file"
        mv "$tmp_file" "${dest}/${name}"
        if command -v "$name" &>/dev/null; then
            log_info "✓ ${name} 安装成功"
        else
            log_warn "${name} 已放置到 ${dest}/ 但可能无法执行"
        fi
    else
        log_warn "${name} 下载失败"
        return 1
    fi
}

# ═══════════════════════════════════════════════════════════════
# 基础检查与检测函数
# ═══════════════════════════════════════════════════════════════
check_root() {
    if [[ $EUID -ne 0 ]]; then
        log_error "此脚本需要root权限运行"
        exit 1
    fi
}

detect_os() {
    log_step "检测操作系统类型..."

    # [Q1/F5] 优先使用标准 /etc/os-release
    if [[ -f /etc/os-release ]]; then
        # shellcheck source=/dev/null
        source /etc/os-release
        local os_id="${ID:-unknown}"
        local os_id_like="${ID_LIKE:-}"

        case "${os_id,,}" in
            ubuntu)
                OS="ubuntu"; PACKAGE_MANAGER="apt" ;;
            debian)
                OS="debian"; PACKAGE_MANAGER="apt" ;;
            centos)
                OS="centos"; PACKAGE_MANAGER="yum" ;;
            rhel)
                OS="rhel"; PACKAGE_MANAGER="yum" ;;
            almalinux|alma)
                OS="almalinux"; PACKAGE_MANAGER="dnf" ;;
            rocky)
                OS="rocky"; PACKAGE_MANAGER="dnf" ;;
            fedora)
                OS="fedora"; PACKAGE_MANAGER="dnf" ;;
            opensuse*|sles)
                OS="opensuse"; PACKAGE_MANAGER="zypper"
                log_warn "openSUSE/SLES 支持为实验性，部分功能可能受限"
                ;;
            *)
                # 通过 ID_LIKE 兼容衍生发行版
                if [[ "$os_id_like" =~ rhel|centos|fedora ]]; then
                    OS="$os_id"; PACKAGE_MANAGER="dnf"
                    log_warn "未知RHEL系发行版 ($os_id)，使用 dnf 模式"
                elif [[ "$os_id_like" =~ debian|ubuntu ]]; then
                    OS="$os_id"; PACKAGE_MANAGER="apt"
                    log_warn "未知Debian系发行版 ($os_id)，使用 apt 模式"
                else
                    log_error "不支持的操作系统: $os_id (ID_LIKE: ${os_id_like:-无})"
                    exit 1
                fi
                ;;
        esac
    else
        # 降级兼容：极老系统无 os-release 文件
        if [[ -f /etc/redhat-release ]]; then
            if grep -q "CentOS" /etc/redhat-release; then
                OS="centos"; PACKAGE_MANAGER="yum"
            elif grep -q "Red Hat" /etc/redhat-release; then
                OS="rhel"; PACKAGE_MANAGER="yum"
            else
                log_error "无法识别的 RedHat 系发行版"; exit 1
            fi
        elif [[ -f /etc/debian_version ]]; then
            OS="debian"; PACKAGE_MANAGER="apt"
        else
            log_error "无法读取 /etc/os-release，不支持的操作系统"; exit 1
        fi
    fi

    log_info "检测到操作系统: $OS (包管理器: $PACKAGE_MANAGER)"
}

detect_ssh_port() {
    log_step "检测实际SSH服务端口..."

    # 方法1: 从SSH配置文件读取端口
    local config_port=""
    if [[ -f /etc/ssh/sshd_config ]]; then
        config_port=$(grep "^Port\s" /etc/ssh/sshd_config | awk '{print $2}' | head -1)
    fi

    # 方法2: 从ss/netstat检测SSH监听端口（兼容IPv4和IPv6格式）
    local listening_ports=""
    if command -v ss &>/dev/null; then
        listening_ports=$(ss -tlnp 2>/dev/null | grep sshd | awk '{print $4}' | grep -oE '[0-9]+$' | sort -u | tr '\n' ' ')
    elif command -v netstat &>/dev/null; then
        listening_ports=$(netstat -tlnp 2>/dev/null | grep sshd | awk '{print $4}' | rev | cut -d: -f1 | rev | sort -u | tr '\n' ' ')
    fi

    # 方法3: 检查systemd服务配置
    local systemd_port=""
    if systemctl is-active --quiet ssh 2>/dev/null || systemctl is-active --quiet sshd 2>/dev/null; then
        systemd_port=$(systemctl show ssh.service sshd.service 2>/dev/null | grep -i port | head -1 | cut -d= -f2 2>/dev/null || true)
    fi

    log_info "SSH端口检测结果："
    [[ -n "$config_port" ]] && log_info "  配置文件中的端口: $config_port"
    [[ -n "$listening_ports" ]] && log_info "  实际监听的端口: $listening_ports"
    [[ -n "$systemd_port" ]] && log_info "  系统服务端口: $systemd_port"

    # 确定最终使用的SSH端口
    local final_ssh_port=""

    if [[ -n "$listening_ports" ]]; then
        final_ssh_port=$(echo $listening_ports | awk '{print $1}')
    elif [[ -n "$config_port" ]]; then
        final_ssh_port="$config_port"
    else
        final_ssh_port="22"
        log_warn "无法检测到SSH端口，使用默认端口22"
    fi

    # 验证端口号是否合理
    if [[ "$final_ssh_port" =~ ^[0-9]+$ ]] && [[ "$final_ssh_port" -ge 1 ]] && [[ "$final_ssh_port" -le 65535 ]]; then
        SSH_PORT="$final_ssh_port"
        log_info "确定SSH端口为: $SSH_PORT"
        if [[ "$SSH_PORT" != "22" ]]; then
            log_info "检测到自定义SSH端口: $SSH_PORT"
        fi
    else
        log_warn "检测到的端口号无效: $final_ssh_port，使用默认端口22"
        SSH_PORT="22"
    fi

    return 0
}

# ═══════════════════════════════════════════════════════════════
# 系统更新
# ═══════════════════════════════════════════════════════════════
update_system() {
    log_step "更新系统包管理器..."

    case $PACKAGE_MANAGER in
        "yum")
            log_info "正在执行 yum update..."
            execute yum update -y
            ;;
        "dnf")  # [F5] AlmaLinux / Rocky / Fedora
            log_info "正在执行 dnf update..."
            execute dnf update -y
            ;;
        "apt")
            # [Q4] 修复: apt update 不需要 -y；upgrade 才需要
            log_info "正在执行 apt update + upgrade..."
            execute apt-get update
            execute apt-get upgrade -y
            ;;
        "zypper")  # [F5] openSUSE
            log_info "正在执行 zypper update..."
            execute zypper --non-interactive update
            ;;
    esac

    log_info "系统包管理器更新完成"
}

# ═══════════════════════════════════════════════════════════════
# 安装基础工具 [C3 幂等]
# ═══════════════════════════════════════════════════════════════
install_basic_tools() {
    log_step "安装基础工具..."

    # [Q7] 使用数组定义工具列表，避免字符串分词歧义
    local extra_tools_yum=(iputils tcpdump nmap neofetch python3 python3-pip)
    local extra_tools_apt=(iputils-ping tcpdump nmap neofetch python3 python3-pip)
    local extra_tools_dnf=(iputils tcpdump nmap neofetch python3 python3-pip)

    case $PACKAGE_MANAGER in
        "yum")
            # CentOS/RHEL 需要 EPEL 源来安装 btop, neofetch, pip 等
            if ! yum repolist 2>/dev/null | grep -qi epel; then
                log_info "安装 EPEL 源（部分工具与 pip 依赖）..."
                execute yum install -y epel-release || log_warn "EPEL源安装异常，可能影响部分工具获取，继续执行..."
            fi
            log_info "正在安装与更新基础工具集 (YUM)..."
            execute yum install -y "${BASIC_TOOLS_YUM[@]}" "${extra_tools_yum[@]}" || log_warn "YUM 安装过程存在报错，将跳过并稍后检查"
            ;;
        "dnf")  # [F5] AlmaLinux / Rocky / Fedora
            if ! dnf repolist 2>/dev/null | grep -qi epel; then
                log_info "安装 EPEL 源..."
                execute dnf install -y epel-release || log_warn "EPEL源安装异常，继续执行..."
            fi
            log_info "正在安装与更新基础工具集 (DNF)..."
            execute dnf install -y "${BASIC_TOOLS_YUM[@]}" "${extra_tools_dnf[@]}" || log_warn "DNF 安装过程存在报错，将跳过并稍后检查"
            ;;
        "apt")
            log_info "正在安装与更新基础工具集 (APT)..."
            execute apt-get install -y "${BASIC_TOOLS_APT[@]}" "${extra_tools_apt[@]}" || log_warn "APT 安装过程存在报错，将跳过并稍后检查"
            ;;
        "zypper")  # [F5] openSUSE
            log_info "正在安装与更新基础工具集 (ZYPPER)..."
            execute zypper --non-interactive install btop net-tools curl wget mtr traceroute tcpdump nmap python3 python3-pip || log_warn "ZYPPER 安装过程存在报错"
            ;;
        *)
            log_warn "未知的包管理器，跳过基础工具自动安装"
            ;;
    esac

    # Neofetch 兜底方案（neofetch 项目已归档，直接拉取最终版）
    if ! command -v neofetch &>/dev/null; then
        log_info "系统源中未检测到 neofetch，直接拉取最终发布版..."
        curl -sLo /usr/local/bin/neofetch https://raw.githubusercontent.com/dylanaraps/neofetch/master/neofetch 2>/dev/null
        chmod +x /usr/local/bin/neofetch 2>/dev/null
    fi

    # 验证安装结果与反馈调试参数
    log_info "验证工具可用性..."
    
    # 验证列表新增 python3 和 pip3
    local tools=("btop" "netstat" "curl" "wget" "mtr" "traceroute" "neofetch" "ping" "tcpdump" "nmap" "python3" "pip3")
    local pkg_apt=("btop" "net-tools" "curl" "wget" "mtr" "traceroute" "neofetch" "iputils-ping" "tcpdump" "nmap" "python3" "python3-pip")
    local pkg_yum=("btop" "net-tools" "curl" "wget" "mtr" "traceroute" "neofetch" "iputils" "tcpdump" "nmap" "python3" "python3-pip")

    for i in "${!tools[@]}"; do
        local cmd="${tools[$i]}"
        
        if command -v "$cmd" &>/dev/null; then
            log_info "✓ 工具已就绪: $cmd"
        else
            # 提取对应的包名生成调试命令
            local debug_pkg=""
            local debug_cmd=""
            if [[ "$PACKAGE_MANAGER" == "yum" ]]; then
                debug_pkg="${pkg_yum[$i]}"
                debug_cmd="yum install -y $debug_pkg"
            elif [[ "$PACKAGE_MANAGER" == "apt" ]]; then
                debug_pkg="${pkg_apt[$i]}"
                debug_cmd="apt install -y $debug_pkg"
            fi
            
            log_warn "✗ 工具缺失: $cmd"
            log_warn "  => 调试参数: 请稍后手动执行 '$debug_cmd' 以检查具体报错原因"
        fi
    done

    # 针对 Python 输出版本信息，方便你确认更新状态
    if command -v python3 &>/dev/null; then
        local py_ver
        py_ver=$(python3 --version 2>&1)
        log_info "当前 Python 状态: $py_ver (源内最新)"
    fi
    if command -v pip3 &>/dev/null; then
        local pip_ver
        pip_ver=$(pip3 --version | awk '{print $2}')
        log_info "当前 pip 状态: pip $pip_ver"
    fi

    log_info "基础工具安装流程结束"
}

# ═══════════════════════════════════════════════════════════════
# 安装自定义工具 [A2 通用函数] [C3 幂等]
# ═══════════════════════════════════════════════════════════════
install_custom_tools() {
    log_step "安装自定义工具..."

    for i in "${!CUSTOM_TOOL_NAMES[@]}"; do
        download_and_install_binary "${CUSTOM_TOOL_NAMES[$i]}" "${CUSTOM_TOOL_URLS[$i]}"
    done

    log_info "自定义工具安装完成"
}

# ═══════════════════════════════════════════════════════════════
# 安装并配置rsyslog（确保登录日志正常记录）
# ═══════════════════════════════════════════════════════════════
install_and_configure_rsyslog() {
    log_step "安装并配置rsyslog服务..."

    case $PACKAGE_MANAGER in
        "yum")
            log_info "CentOS/RHEL系统通常已预装rsyslog"
            if ! command -v rsyslogd &>/dev/null; then
                log_info "安装rsyslog..."
                execute yum install -y rsyslog
            fi
            ;;
        "apt")
            if ! command -v rsyslogd &>/dev/null; then
                log_info "安装rsyslog..."
                execute apt install -y rsyslog
            else
                log_info "rsyslog 已安装"
            fi
            ;;
    esac

    # 启动并启用rsyslog服务
    log_info "启用并启动rsyslog服务..."
    execute systemctl enable rsyslog
    execute systemctl start rsyslog

    # 验证rsyslog服务状态
    if [[ "$DRY_RUN" != true ]]; then
        if systemctl is-active --quiet rsyslog; then
            log_info "rsyslog服务已成功启动"
        else
            log_warn "rsyslog服务可能未正常启动，请检查"
        fi
    fi

    # 确保日志目录存在
    if [[ "$DRY_RUN" != true ]]; then
        case $OS in
            "ubuntu"|"debian")
                if [[ ! -f /var/log/auth.log ]]; then
                    log_info "创建auth.log文件..."
                    touch /var/log/auth.log
                    chmod 640 /var/log/auth.log
                fi
                ;;
            "centos"|"rhel")
                if [[ ! -f /var/log/secure ]]; then
                    log_info "创建secure日志文件..."
                    touch /var/log/secure
                    chmod 600 /var/log/secure
                fi
                ;;
        esac
    fi

    execute systemctl restart rsyslog
    log_info "rsyslog配置完成，SSH登录日志将正常记录"
}

# ═══════════════════════════════════════════════════════════════
# 清理已有的iptables配置 [C1 备份]
# ═══════════════════════════════════════════════════════════════
clean_existing_iptables() {
    log_step "清理已有的iptables配置..."

    # [C1] 先备份当前规则
    backup_iptables

    # 停止iptables服务（如果正在运行）
    if systemctl is-active --quiet iptables 2>/dev/null; then
        log_info "停止现有iptables服务..."
        execute systemctl stop iptables 2>/dev/null || true
    fi

    # 检查是否有fail2ban封禁的IP（保留fail2ban的封禁规则）
    local has_fail2ban_rules=false
    if iptables -L INPUT -n 2>/dev/null | grep -qE "f2b-|fail2ban-"; then
        has_fail2ban_rules=true
        log_info "检测到fail2ban封禁规则，将保留这些规则"
    fi

    log_info "清空所有iptables规则（保留fail2ban封禁规则）..."

    # 清空规则，但保留fail2ban链
    execute iptables -F 2>/dev/null || true
    # 删除自定义链，但保留fail2ban链
    if [[ "$DRY_RUN" != true ]]; then
        for chain in $(iptables -S 2>/dev/null | awk '/^-N / {print $2}'); do
            if [[ ! "$chain" =~ ^f2b- ]] && [[ ! "$chain" =~ ^fail2ban- ]]; then
                iptables -X "$chain" 2>/dev/null || true
            fi
        done
    fi

    # 清理 raw 表端口0阻断与日志规则
    log_info "清理 raw 表端口0规则..."
    for proto in tcp udp; do
        for dir in --dport --sport; do
            while iptables -t raw -C PREROUTING -p "$proto" "$dir" 0 \
                -j LOG --log-prefix "PORT0_DROP: " --log-level 4 2>/dev/null; do
                iptables -t raw -D PREROUTING -p "$proto" "$dir" 0 \
                    -j LOG --log-prefix "PORT0_DROP: " --log-level 4 2>/dev/null || break
            done
            while iptables -t raw -C PREROUTING -p "$proto" "$dir" 0 -j DROP 2>/dev/null; do
                iptables -t raw -D PREROUTING -p "$proto" "$dir" 0 -j DROP 2>/dev/null || break
            done
            while iptables -t raw -C OUTPUT -p "$proto" "$dir" 0 -j DROP 2>/dev/null; do
                iptables -t raw -D OUTPUT -p "$proto" "$dir" 0 -j DROP 2>/dev/null || break
            done
            if command -v ip6tables &>/dev/null; then
                while ip6tables -t raw -C PREROUTING -p "$proto" "$dir" 0 \
                    -j LOG --log-prefix "PORT0_DROP: " --log-level 4 2>/dev/null; do
                    ip6tables -t raw -D PREROUTING -p "$proto" "$dir" 0 \
                        -j LOG --log-prefix "PORT0_DROP: " --log-level 4 2>/dev/null || break
                done
                while ip6tables -t raw -C PREROUTING -p "$proto" "$dir" 0 -j DROP 2>/dev/null; do
                    ip6tables -t raw -D PREROUTING -p "$proto" "$dir" 0 -j DROP 2>/dev/null || break
                done
            fi
        done
    done

    execute iptables -t nat -F 2>/dev/null || true
    execute iptables -t nat -X 2>/dev/null || true
    execute iptables -t mangle -F 2>/dev/null || true
    execute iptables -t mangle -X 2>/dev/null || true
    execute iptables -t filter -F 2>/dev/null || true

    if [[ "$DRY_RUN" != true ]]; then
        for chain in $(iptables -t filter -S 2>/dev/null | awk '/^-N / {print $2}'); do
            if [[ ! "$chain" =~ ^f2b- ]] && [[ ! "$chain" =~ ^fail2ban- ]]; then
                iptables -t filter -X "$chain" 2>/dev/null || true
            fi
        done
    fi

    # 重置所有链的默认策略为ACCEPT（避免锁定）
    execute iptables -P INPUT ACCEPT 2>/dev/null || true
    execute iptables -P FORWARD ACCEPT 2>/dev/null || true
    execute iptables -P OUTPUT ACCEPT 2>/dev/null || true

    # 删除已保存的iptables规则文件
    if [[ "$DRY_RUN" != true ]]; then
        case $OS in
            "centos"|"rhel")
                if [[ -f /etc/sysconfig/iptables ]]; then
                    log_info "删除旧的iptables配置文件..."
                    rm -f /etc/sysconfig/iptables
                    rm -f /etc/sysconfig/iptables.save
                fi
                ;;
            "ubuntu"|"debian")
                if [[ -f /etc/iptables/rules.v4 ]]; then
                    log_info "删除旧的iptables配置文件..."
                    rm -f /etc/iptables/rules.v4
                    rm -f /etc/iptables/rules.v6
                fi
                ;;
        esac
    fi

    log_info "iptables配置清理完成"
}

# ═══════════════════════════════════════════════════════════════
# 清理已有的fail2ban配置
# ═══════════════════════════════════════════════════════════════
clean_existing_fail2ban() {
    log_step "清理已有的fail2ban配置..."

    if command -v fail2ban-client &>/dev/null; then
        log_info "检测到已安装的fail2ban，开始清理..."

        # 检查是否有被封禁的IP
        local has_banned_ips=false
        if systemctl is-active --quiet fail2ban 2>/dev/null; then
            for jail in $(fail2ban-client status 2>/dev/null | grep "Jail list:" | sed 's/.*Jail list:\s*//' | tr ',' ' '); do
                if fail2ban-client status "$jail" 2>/dev/null | grep -q "Currently banned: [1-9]"; then
                    has_banned_ips=true
                    local banned_count=$(fail2ban-client status "$jail" 2>/dev/null | grep "Currently banned:" | awk '{print $3}')
                    log_info "检测到 $jail jail 有 $banned_count 个被封禁的IP，将保留这些封禁"
                    break
                fi
            done
        fi

        if [[ "$has_banned_ips" == true ]]; then
            log_info "停止fail2ban服务（保留封禁规则）..."
            execute systemctl stop fail2ban 2>/dev/null || true
        else
            if systemctl is-active --quiet fail2ban 2>/dev/null; then
                log_info "停止fail2ban服务..."
                execute systemctl stop fail2ban 2>/dev/null || true
            fi
            execute fail2ban-client unban --all 2>/dev/null || true
        fi

        # 备份原有配置
        if [[ -f /etc/fail2ban/jail.local ]] && [[ "$DRY_RUN" != true ]]; then
            log_info "备份原有fail2ban配置..."
            cp /etc/fail2ban/jail.local /etc/fail2ban/jail.local.backup.$(date +%Y%m%d_%H%M%S) 2>/dev/null || true
        fi

        # 清理配置文件
        if [[ "$DRY_RUN" != true ]]; then
            log_info "清理fail2ban配置文件..."
            rm -f /etc/fail2ban/jail.local
            rm -f /etc/fail2ban/jail.d/*.conf 2>/dev/null || true
            rm -f /etc/fail2ban/filter.d/nginx-*.conf 2>/dev/null || true
            rm -f /var/lib/fail2ban/fail2ban.sqlite3 2>/dev/null || true
            rm -f /var/log/fail2ban.log* 2>/dev/null || true
        fi

        log_info "fail2ban配置清理完成"
    else
        log_info "未检测到fail2ban，无需清理"
    fi
}

# ═══════════════════════════════════════════════════════════════
# 关闭系统自带防火墙
# ═══════════════════════════════════════════════════════════════
disable_system_firewalls() {
    log_step "关闭系统自带防火墙..."

    case $OS in
        "centos"|"rhel")
            if systemctl is-active --quiet firewalld 2>/dev/null; then
                log_info "正在停止firewalld..."
                execute systemctl stop firewalld
                execute systemctl disable firewalld
                log_info "已停止并禁用firewalld"
            else
                log_info "firewalld未运行"
            fi
            if command -v ufw &>/dev/null; then
                log_info "正在禁用ufw..."
                execute ufw --force disable
            fi
            ;;
        "ubuntu"|"debian")
            if command -v ufw &>/dev/null; then
                log_info "正在停止并禁用ufw..."
                execute ufw --force disable
                execute systemctl stop ufw 2>/dev/null || true
                execute systemctl disable ufw 2>/dev/null || true
                log_info "已停止并禁用ufw"
            else
                log_info "ufw未安装"
            fi
            if systemctl is-active --quiet firewalld 2>/dev/null; then
                log_info "正在停止firewalld..."
                execute systemctl stop firewalld
                execute systemctl disable firewalld
            fi
            ;;
    esac
}

# ═══════════════════════════════════════════════════════════════
# 安装iptables-legacy（Docker兼容版）
# ═══════════════════════════════════════════════════════════════
install_iptables_legacy() {
    log_step "安装并配置iptables-legacy（Docker兼容版）..."

    case $PACKAGE_MANAGER in
        "yum")
            log_info "安装iptables和相关服务..."
            execute yum install -y iptables iptables-services
            execute systemctl disable iptables 2>/dev/null || true
            execute systemctl stop iptables 2>/dev/null || true
            ;;
        "apt")
            log_info "安装iptables和iptables-persistent..."
            if [[ "$DRY_RUN" != true ]]; then
                echo iptables-persistent iptables-persistent/autosave_v4 boolean true | debconf-set-selections
                echo iptables-persistent iptables-persistent/autosave_v6 boolean true | debconf-set-selections
            fi
            execute apt install -y iptables iptables-persistent
            execute systemctl disable netfilter-persistent 2>/dev/null || true
            execute systemctl stop netfilter-persistent 2>/dev/null || true
            ;;
    esac

    # 切换到iptables-legacy（Docker兼容）
    log_info "切换到iptables-legacy以确保Docker兼容性..."
    if command -v update-alternatives &>/dev/null; then
        execute update-alternatives --set iptables /usr/sbin/iptables-legacy 2>/dev/null || log_warn "无法切换到iptables-legacy"
        execute update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy 2>/dev/null || log_warn "无法切换到ip6tables-legacy"
        log_info "已切换到iptables-legacy"
    else
        log_info "系统可能已默认使用iptables-legacy"
    fi

    local iptables_version=$(iptables --version 2>/dev/null || echo "未知版本")
    log_info "当前iptables版本: $iptables_version"
}

# ═══════════════════════════════════════════════════════════════
# 配置基础iptables规则 [C3 幂等] [A1 使用配置变量]
# ═══════════════════════════════════════════════════════════════
configure_basic_iptables() {
    log_step "配置基础iptables规则（代理服务器优化版）..."

    # 端口0阻断与日志（最高优先级，在所有其他规则之前配置）
    setup_port0_log

    # 检查是否已有china_ip_firewall.sh的规则
    local has_china_firewall=false
    if command -v ipset &>/dev/null && ipset list china_ip &>/dev/null 2>&1; then
        has_china_firewall=true
        log_info "检测到china_ip_firewall.sh规则，将保持兼容性配置"
    fi

    # 检查fail2ban封禁规则
    local has_fail2ban_rules=false
    if iptables -L INPUT -n 2>/dev/null | grep -qE "f2b-|fail2ban-"; then
        has_fail2ban_rules=true
        log_info "检测到fail2ban封禁规则，将保留"
    fi

    # china_ip_firewall 共存处理
    if [[ "$has_china_firewall" == true ]]; then
        log_info "保留现有规则（china_ip_firewall.sh + fail2ban）"
        if [[ "$DRY_RUN" != true ]]; then
            while read -r custom_chain; do
                [[ -z "$custom_chain" ]] && continue
                [[ "$custom_chain" =~ ^f2b- ]] && continue
                [[ "$custom_chain" =~ ^fail2ban- ]] && continue
                [[ "$custom_chain" == "ddos_protect" ]] && continue
                iptables -F "$custom_chain" 2>/dev/null || true
                iptables -X "$custom_chain" 2>/dev/null || true
            done < <(iptables -S 2>/dev/null | awk '/^-N / {print $2}')
        fi
    fi

    # 设置默认策略
    log_info "设置默认策略..."
    if [[ "$has_china_firewall" != true ]]; then
        execute iptables -P INPUT DROP
    else
        local current_policy=$(iptables -L INPUT -n | head -1 | grep -o "policy [A-Z]*" | awk '{print $2}')
        if [[ "$current_policy" != "ACCEPT" ]]; then
            execute iptables -P INPUT DROP
        else
            log_info "保持INPUT链策略为ACCEPT（china_ip_firewall.sh已设置）"
        fi
    fi
    execute iptables -P FORWARD ACCEPT
    execute iptables -P OUTPUT ACCEPT

    # [C3] 幂等添加：先检查是否存在，再添加
    # 允许本地回环
    log_info "配置本地回环规则..."
    if ! iptables -C INPUT -i lo -j ACCEPT 2>/dev/null; then
        execute iptables -A INPUT -i lo -j ACCEPT
    fi
    if ! iptables -C OUTPUT -o lo -j ACCEPT 2>/dev/null; then
        execute iptables -A OUTPUT -o lo -j ACCEPT
    fi

    # 允许已建立的连接和相关连接
    log_info "配置连接状态规则..."
    if ! iptables -C INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 2>/dev/null && \
       ! iptables -C INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT 2>/dev/null; then
        execute iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
    else
        log_info "已存在ESTABLISHED规则，跳过"
    fi

    # FORWARD链默认ACCEPT
    log_info "FORWARD链默认策略为ACCEPT，Docker、PVE、代理转发正常工作"

    # [S5] 创建双重防护链：syn_protect(hashlimit精准限速) + ddos_protect(recent计数)
    log_info "配置双重 DDoS 防护规则（hashlimit + recent）..."
    if [[ "$DRY_RUN" != true ]]; then
        # syn_protect 链：基于每源IP的SYN包速率限制（第一层）
        iptables -N syn_protect 2>/dev/null || iptables -F syn_protect 2>/dev/null
        # ddos_protect 链：基于时间窗口的连接计数（第二层）
        iptables -N ddos_protect 2>/dev/null || iptables -F ddos_protect 2>/dev/null
    fi

    # syn_protect 规则：每源IP每秒超过20个SYN包则DROP（burst允许100）
    execute iptables -A syn_protect -p tcp --syn \
        -m hashlimit \
        --hashlimit-above 20/s \
        --hashlimit-burst 100 \
        --hashlimit-mode srcip \
        --hashlimit-name syn_flood \
        -j DROP 2>/dev/null || log_warn "hashlimit 模块不可用，跳过SYN精准限速（仍有recent保护）"
    execute iptables -A syn_protect -j RETURN

    # ddos_protect 规则：时间窗口内新连接计数超阈值则DROP
    execute iptables -A ddos_protect -m recent --set --name ddos_check
    execute iptables -A ddos_protect -m recent --update --seconds "$DDOS_WINDOW" --hitcount "$DDOS_THRESHOLD" --name ddos_check -j DROP
    execute iptables -A ddos_protect -j RETURN

    # SSH端口（先过 syn_protect，再过 ddos_protect）
    log_info "配置SSH端口访问规则（端口 $SSH_PORT）..."
    if ! iptables -C INPUT -p tcp --dport "$SSH_PORT" -m state --state NEW -j syn_protect 2>/dev/null; then
        execute iptables -A INPUT -p tcp --dport "$SSH_PORT" -m state --state NEW -j syn_protect
    fi
    if ! iptables -C INPUT -p tcp --dport "$SSH_PORT" -m state --state NEW -j ddos_protect 2>/dev/null; then
        execute iptables -A INPUT -p tcp --dport "$SSH_PORT" -m state --state NEW -j ddos_protect
    fi
    if ! iptables -C INPUT -p tcp --dport "$SSH_PORT" -j ACCEPT 2>/dev/null; then
        execute iptables -A INPUT -p tcp --dport "$SSH_PORT" -j ACCEPT
    fi

    # 管理端口（应用防护）[A1]
    for port in "${MANAGEMENT_PORTS[@]}"; do
        if ! iptables -C INPUT -p tcp --dport "$port" -m state --state NEW -j syn_protect 2>/dev/null; then
            execute iptables -A INPUT -p tcp --dport "$port" -m state --state NEW -j syn_protect
        fi
        if ! iptables -C INPUT -p tcp --dport "$port" -m state --state NEW -j ddos_protect 2>/dev/null; then
            execute iptables -A INPUT -p tcp --dport "$port" -m state --state NEW -j ddos_protect
        fi
    done

    # 开放代理相关端口 [A1]
    log_info "开放代理相关端口..."
    for port in "${OPEN_TCP_PORTS[@]}"; do
        if ! iptables -C INPUT -p tcp --dport "$port" -j ACCEPT 2>/dev/null; then
            execute iptables -A INPUT -p tcp --dport "$port" -j ACCEPT
        fi
    done

    # FTP端口 [A1]
    log_info "开放FTP端口..."
    for port in "${FTP_PORTS[@]}"; do
        if ! iptables -C INPUT -p tcp --dport "$port" -j ACCEPT 2>/dev/null; then
            execute iptables -A INPUT -p tcp --dport "$port" -j ACCEPT
        fi
    done
    if ! iptables -C INPUT -p tcp --dport "$FTP_PASSIVE_RANGE" -j ACCEPT 2>/dev/null; then
        execute iptables -A INPUT -p tcp --dport "$FTP_PASSIVE_RANGE" -j ACCEPT
    fi

    # 管理端口 [A1]
    log_info "配置管理端口..."
    for port in "${MANAGEMENT_PORTS[@]}"; do
        if ! iptables -C INPUT -p tcp --dport "$port" -j ACCEPT 2>/dev/null; then
            execute iptables -A INPUT -p tcp --dport "$port" -j ACCEPT
        fi
    done

    # ICMP规则
    log_info "配置ICMP规则..."
    if [[ "$DRY_RUN" != true ]]; then
        iptables -D INPUT -p icmp --icmp-type echo-request -j DROP 2>/dev/null || true
        iptables -D INPUT -p icmp --icmp-type echo-request -m set ! --match-set china_ip src -j DROP 2>/dev/null || true
    fi
    # china_ip_firewall.sh 接管时，不能重加无条件 ACCEPT，否则会短路其中国IP限制。
    if [[ "$has_china_firewall" == true ]]; then
        log_info "检测到china_ip_firewall.sh，跳过无条件 ICMP ping 放行"
    elif ! iptables -C INPUT -p icmp --icmp-type echo-request -j ACCEPT 2>/dev/null; then
        execute iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
        log_info "已添加规则：允许ICMP ping"
    fi

    # 高端口范围 [A1]
    log_info "开放高端口范围（${HIGH_PORT_START}-${HIGH_PORT_END}）..."
    if ! iptables -C INPUT -p tcp --dport "${HIGH_PORT_START}:${HIGH_PORT_END}" -j ACCEPT 2>/dev/null; then
        execute iptables -A INPUT -p tcp --dport "${HIGH_PORT_START}:${HIGH_PORT_END}" -j ACCEPT
    fi
    if ! iptables -C INPUT -p udp --dport "${HIGH_PORT_START}:${HIGH_PORT_END}" -j ACCEPT 2>/dev/null; then
        execute iptables -A INPUT -p udp --dport "${HIGH_PORT_START}:${HIGH_PORT_END}" -j ACCEPT
    fi

    log_info "基础iptables规则配置完成（代理服务器优化版）"
    log_info "已开放端口: ${OPEN_TCP_PORTS[*]}, FTP(${FTP_PORTS[*]}), 被动(${FTP_PASSIVE_RANGE})"
    log_info "已开放高端口范围: ${HIGH_PORT_START}-${HIGH_PORT_END}（TCP/UDP）"
    log_info "DDoS防护: ${DDOS_WINDOW}秒内超过${DDOS_THRESHOLD}个新连接触发限制"
}

# ═══════════════════════════════════════════════════════════════
# 用户交互：添加自定义端口
# ═══════════════════════════════════════════════════════════════

add_custom_ports() {
    log_step "配置自定义端口..."

    if [[ "$INTERACTIVE_MODE" != true ]]; then
        log_info "非交互模式，跳过自定义端口配置"
        return 0
    fi

    echo -e "${YELLOW}请输入需要开放的额外TCP端口，多个端口用空格分隔（直接回车跳过）:${NC}"
    read -r custom_ports

    if [[ -n "$custom_ports" ]]; then
        for port in $custom_ports; do
            if [[ "$port" =~ ^[0-9]+$ ]] && [[ "$port" -ge 1 ]] && [[ "$port" -le 65535 ]]; then
                if [[ "$port" -ge "$HIGH_PORT_START" ]]; then
                    log_info "端口 $port 已在高端口范围内开放"
                else
                    execute iptables -A INPUT -p tcp --dport "$port" -j ACCEPT
                    log_info "已开放TCP端口: $port"
                fi
            else
                log_warn "无效端口号: $port，跳过"
            fi
        done
    else
        log_info "未添加额外端口"
    fi
}

# ═══════════════════════════════════════════════════════════════
# 保存iptables规则 (含 IPv6)
# ═══════════════════════════════════════════════════════════════
save_iptables() {
    log_step "保存iptables规则..."

    if [[ "$DRY_RUN" == true ]]; then
        log_dry "  保存 iptables / ip6tables 规则"
        return 0
    fi

    case $OS in
        "centos"|"rhel"|"almalinux"|"rocky"|"fedora")
            log_info "配置 iptables 规则持久化..."
            if ! rpm -q iptables-services &>/dev/null; then
                log_info "正在安装 iptables-services 确保重启后自动加载规则..."
                execute yum install -y iptables-services
            fi
            execute systemctl enable iptables
            if command -v ip6tables &>/dev/null; then
                execute systemctl enable ip6tables
            fi
            service iptables save 2>/dev/null || iptables-save > /etc/sysconfig/iptables
            if command -v ip6tables &>/dev/null; then
                ip6tables-save > /etc/sysconfig/ip6tables 2>/dev/null || true
            fi
            ;;
        "ubuntu"|"debian")
            log_info "配置 iptables 规则持久化..."
            mkdir -p /etc/iptables
            iptables-save > /etc/iptables/rules.v4
            if command -v ip6tables &>/dev/null; then
                ip6tables-save > /etc/iptables/rules.v6 2>/dev/null || true
            fi
            # 使用 netfilter-persistent/iptables-persistent 实现自动加载
            if ! dpkg -s iptables-persistent &>/dev/null; then
                log_info "正在安装 iptables-persistent 确保重启后自动加载规则..."
                DEBIAN_FRONTEND=noninteractive apt-get install -y iptables-persistent
            fi
            execute systemctl enable netfilter-persistent 2>/dev/null || true
            # 将规则保存至 netfilter-persistent 目录
            if command -v netfilter-persistent &>/dev/null; then
                execute netfilter-persistent save
            fi
            ;;
        *)
            # 兜底
            iptables-save > /etc/iptables/rules.v4 2>/dev/null || iptables-save > /etc/sysconfig/iptables 2>/dev/null || true
            if command -v ip6tables &>/dev/null; then
                ip6tables-save > /etc/iptables/rules.v6 2>/dev/null || ip6tables-save > /etc/sysconfig/ip6tables 2>/dev/null || true
            fi
            ;;
    esac

    log_info "iptables规则已保存，重启自动持久化加载已就绪"
}

# ═══════════════════════════════════════════════════════════════
# 端口0流量阻断与日志记录（raw表，最高优先级）
# 幂等：检测 LOG 前缀 PORT0_DROP: 和 rsyslog 配置文件是否已存在
# ═══════════════════════════════════════════════════════════════
setup_port0_log() {
    local PORT0_LOG="/var/log/port0_traffic.log"
    local RSYSLOG_CONF="/etc/rsyslog.d/port0.conf"
    local LOGROTATE_CONF="/etc/logrotate.d/port0_traffic"

    # --- 幂等检测 ---
    local iptables_ok=false
    local rsyslog_ok=false
    if iptables -t raw -S PREROUTING 2>/dev/null | grep -q 'PORT0_DROP:'; then
        iptables_ok=true
    fi
    if [[ -f "$RSYSLOG_CONF" ]]; then
        rsyslog_ok=true
    fi

    if [[ "$iptables_ok" == true && "$rsyslog_ok" == true ]]; then
        log_info "端口0日志功能已配置，跳过重复添加"
        return 0
    fi

    log_info "配置端口0流量阻断与日志记录（raw表）..."

    if [[ "$DRY_RUN" == true ]]; then
        log_dry "  iptables -t raw: LOG+DROP 端口0规则（tcp/udp × dport/sport）"
        log_dry "  写入 $RSYSLOG_CONF"
        log_dry "  写入 $LOGROTATE_CONF"
        return 0
    fi

    # --- iptables raw 表：LOG 在前，DROP 在后（幂等）---
    for proto in tcp udp; do
        for dir in --dport --sport; do
            # LOG 规则
            if ! iptables -t raw -C PREROUTING -p "$proto" "$dir" 0 \
                -j LOG --log-prefix "PORT0_DROP: " --log-level 4 2>/dev/null; then
                iptables -t raw -A PREROUTING -p "$proto" "$dir" 0 \
                    -j LOG --log-prefix "PORT0_DROP: " --log-level 4
            fi
            # DROP 规则
            if ! iptables -t raw -C PREROUTING -p "$proto" "$dir" 0 -j DROP 2>/dev/null; then
                iptables -t raw -A PREROUTING -p "$proto" "$dir" 0 -j DROP
            fi
            # OUTPUT 链同步阻断（阻止本机发出端口0包）
            if ! iptables -t raw -C OUTPUT -p "$proto" "$dir" 0 -j DROP 2>/dev/null; then
                iptables -t raw -A OUTPUT -p "$proto" "$dir" 0 -j DROP
            fi
            # IPv6 同步
            if command -v ip6tables &>/dev/null; then
                if ! ip6tables -t raw -C PREROUTING -p "$proto" "$dir" 0 \
                    -j LOG --log-prefix "PORT0_DROP: " --log-level 4 2>/dev/null; then
                    ip6tables -t raw -A PREROUTING -p "$proto" "$dir" 0 \
                        -j LOG --log-prefix "PORT0_DROP: " --log-level 4
                fi
                if ! ip6tables -t raw -C PREROUTING -p "$proto" "$dir" 0 -j DROP 2>/dev/null; then
                    ip6tables -t raw -A PREROUTING -p "$proto" "$dir" 0 -j DROP
                fi
            fi
        done
    done

    # --- rsyslog：过滤 PORT0_DROP 写入独立文件 ---
    if [[ ! -f "$RSYSLOG_CONF" ]]; then
        cat > "$RSYSLOG_CONF" << 'EOF'
# 捕获 iptables PORT0_DROP 端口0恶意流量日志
:msg, contains, "PORT0_DROP:" /var/log/port0_traffic.log
& stop
EOF
        systemctl reload rsyslog 2>/dev/null || service rsyslog reload 2>/dev/null || true
        log_info "rsyslog 过滤配置已写入 $RSYSLOG_CONF"
    fi

    # --- 创建日志文件 ---
    if [[ ! -f "$PORT0_LOG" ]]; then
        touch "$PORT0_LOG"
        chmod 640 "$PORT0_LOG"
    fi

    # --- logrotate：每日轮转，保留7天 ---
    if [[ ! -f "$LOGROTATE_CONF" ]]; then
        cat > "$LOGROTATE_CONF" << 'EOF'
/var/log/port0_traffic.log {
    daily
    rotate 7
    compress
    missingok
    notifempty
    postrotate
        systemctl reload rsyslog > /dev/null 2>&1 || true
    endscript
}
EOF
        log_info "logrotate 配置已写入 $LOGROTATE_CONF"
    fi

    log_info "端口0流量阻断与日志配置完成，日志路径：$PORT0_LOG"
}

# ═══════════════════════════════════════════════════════════════
# [B2] IPv6 防火墙规则
# ═══════════════════════════════════════════════════════════════
configure_ipv6_firewall() {
    log_step "配置IPv6防火墙规则..."

    if ! command -v ip6tables &>/dev/null; then
        log_warn "ip6tables 不可用，跳过IPv6防火墙配置"
        return 1
    fi

    # 清空现有 IPv6 规则
    execute ip6tables -F 2>/dev/null || true
    if [[ "$DRY_RUN" != true ]]; then
        for chain in $(ip6tables -S 2>/dev/null | awk '/^-N / {print $2}'); do
            if [[ ! "$chain" =~ ^f2b- ]] && [[ ! "$chain" =~ ^fail2ban- ]]; then
                ip6tables -X "$chain" 2>/dev/null || true
            fi
        done
    fi

    # 设置默认策略
    execute ip6tables -P INPUT DROP
    execute ip6tables -P FORWARD ACCEPT
    execute ip6tables -P OUTPUT ACCEPT

    # 允许本地回环
    execute ip6tables -A INPUT -i lo -j ACCEPT

    # 允许已建立的连接
    execute ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

    # ICMPv6（IPv6正常工作必需，包括邻居发现协议）
    execute ip6tables -A INPUT -p icmpv6 -j ACCEPT

    # SSH端口
    execute ip6tables -A INPUT -p tcp --dport "$SSH_PORT" -j ACCEPT

    # 开放TCP端口（与IPv4保持一致）[A1]
    for port in "${OPEN_TCP_PORTS[@]}"; do
        execute ip6tables -A INPUT -p tcp --dport "$port" -j ACCEPT
    done

    # 管理端口 [A1]
    for port in "${MANAGEMENT_PORTS[@]}"; do
        execute ip6tables -A INPUT -p tcp --dport "$port" -j ACCEPT
    done

    # FTP端口 [A1]
    for port in "${FTP_PORTS[@]}"; do
        execute ip6tables -A INPUT -p tcp --dport "$port" -j ACCEPT
    done
    execute ip6tables -A INPUT -p tcp --dport "$FTP_PASSIVE_RANGE" -j ACCEPT

    # 高端口范围 [A1]
    # 说明：如果此服务器后续会运行 china_ip_firewall.sh，这两行可注释掉，
    # 由 china_ip_v6 ipset 控制 IPv6 高端口访问，避免 IPv6 全球暴露。
    # 仅运行 inition.sh 的服务器保留这两行（代理监听高端口需要对外开放）。
    execute ip6tables -A INPUT -p tcp --dport "${HIGH_PORT_START}:${HIGH_PORT_END}" -j ACCEPT
    execute ip6tables -A INPUT -p udp --dport "${HIGH_PORT_START}:${HIGH_PORT_END}" -j ACCEPT

    log_info "IPv6防火墙规则配置完成（与IPv4端口策略一致）"
}

# ═══════════════════════════════════════════════════════════════
# [D1] 系统内核参数优化 (sysctl + BBR)
# ═══════════════════════════════════════════════════════════════
configure_sysctl() {
    log_step "优化系统内核参数..."

    local sysctl_conf="/etc/sysctl.d/99-inition.conf"

    if [[ "$DRY_RUN" == true ]]; then
        log_dry "  写入 ${sysctl_conf}"
        log_dry "  sysctl -p ${sysctl_conf}"
        return 0
    fi

    cat > "$sysctl_conf" << 'SYSCTL_EOF'
# ═══ inition.sh v2.0 内核参数优化 ═══
# 生成时间: 见文件修改时间

# --- TCP 连接优化 ---
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_max_orphans = 65535
net.ipv4.tcp_syncookies = 1

# --- 内存缓冲区 ---
net.ipv4.tcp_mem = 65536 131072 262144
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216

# --- SYN Flood 防护 ---
net.ipv4.tcp_synack_retries = 2

# --- IP 转发（Docker / 代理需要）---
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

# --- 文件描述符 ---
fs.file-max = 1048576
SYSCTL_EOF

    # 连接追踪参数（需要模块已加载）
    if lsmod 2>/dev/null | grep -q nf_conntrack || modprobe nf_conntrack 2>/dev/null; then
        cat >> "$sysctl_conf" << 'CONNTRACK_EOF'

# --- 连接追踪（nf_conntrack 已加载）---
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_tcp_timeout_established = 7200
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30
CONNTRACK_EOF
        log_info "已添加连接追踪优化参数"
    else
        log_warn "nf_conntrack 模块未加载，跳过连接追踪参数"
    fi

    # BBR 拥塞控制
    if [[ "$ENABLE_BBR" == true ]]; then
        if grep -q "tcp_bbr" /proc/modules 2>/dev/null || modprobe tcp_bbr 2>/dev/null; then
            cat >> "$sysctl_conf" << 'BBR_EOF'

# --- TCP BBR 拥塞控制 ---
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
BBR_EOF
            log_info "✓ 已启用 TCP BBR 拥塞控制"
        else
            log_warn "内核不支持 BBR 模块，跳过"
        fi
    fi

    # 应用参数
    sysctl -p "$sysctl_conf" 2>/dev/null || log_warn "部分 sysctl 参数可能未生效"
    log_info "内核参数优化完成"
}

# ═══════════════════════════════════════════════════════════════
# [D2] 自动配置 Swap
# ═══════════════════════════════════════════════════════════════
configure_swap() {
    log_step "检测并配置 Swap..."

    # 1. 计算目标 Swap 大小
    local total_ram
    total_ram=$(free -m | awk '/Mem/ {print $2}')
    local target_swap_size

    if [[ "$total_ram" -le 1024 ]]; then
        target_swap_size=$((total_ram * 2))    # RAM ≤ 1GB: 2x
    elif [[ "$total_ram" -le 4096 ]]; then
        target_swap_size=$total_ram            # RAM ≤ 4GB: 1x
    elif [[ "$total_ram" -le 16384 ]]; then
        target_swap_size=4096                  # RAM ≤ 16GB: 4GB
    else
        target_swap_size=8192                  # RAM > 16GB 大内存物理机: 8GB (更稳妥的缓冲)
    fi

    # 2. 检查现有 Swap 是否充足
    local current_swap
    current_swap=$(free -m | awk '/Swap/ {print $2}')
    # 如果现有 swap 达到了目标大小的 80% 以上，认为足够，跳过
    if [[ "$current_swap" -ge $((target_swap_size * 8 / 10)) ]]; then
        log_info "系统已有 ${current_swap}MB Swap，容量充足，跳过创建"
        return 0
    fi

    # [F6] 低内存优先尝试 zram（延迟更低，无磁盘I/O）
    if [[ "$ENABLE_ZRAM" == true ]] && [[ "$total_ram" -le 2048 ]]; then
        log_info "内存 ≤ 2GB，尝试配置 zram 压缩内存..."
        if [[ "$DRY_RUN" == true ]]; then
            log_dry "  modprobe zram && echo lz4 > /sys/block/zram0/comp_algorithm"
            log_dry "  echo ${total_ram}M > /sys/block/zram0/disksize && mkswap /dev/zram0"
        elif modprobe zram 2>/dev/null; then
            local zram_size=$((total_ram / 2))  # zram 大小 = RAM/2（压缩比约2x，实际效果接近1x RAM）
            # 设置压缩算法（优先 lz4，回退 lzo）
            if echo "lz4" > /sys/block/zram0/comp_algorithm 2>/dev/null; then
                log_info "zram 压缩算法: lz4"
            else
                log_info "zram 压缩算法: lzo（默认）"
            fi
            echo "${zram_size}M" > /sys/block/zram0/disksize
            mkswap /dev/zram0 && swapon -p 100 /dev/zram0 && {
                log_info "✓ zram 配置成功: ${zram_size}MB (高优先级，swappiness=100)"
                # zram 写入 fstab（开机自动挂载需要 zram-tools 包，这里仅记录）
                sysctl -w vm.swappiness=100 2>/dev/null
                log_info "提示: 重启后 zram 需手动重建，建议安装 zram-tools: apt install zram-tools"
                return 0
            } || log_warn "zram swapon 失败，回退到 swapfile"
        else
            log_info "zram 模块不可用，使用传统 swapfile"
        fi
    fi

    local swap_file="/swapfile"

    if [[ -f "$swap_file" ]]; then
        log_info "发现现有 ${swap_file} 但容量可能不足。如需调整，请手动清理。"
        # 安全起见，不主动删除已有的 swapfile，避免误删用户特定配置
        return 0
    fi

    log_info "创建 ${target_swap_size}MB Swap 文件（RAM: ${total_ram}MB）..."

    if [[ "$DRY_RUN" == true ]]; then
        log_dry "  dd if=/dev/zero of=${swap_file} bs=1M count=${target_swap_size}"
        log_dry "  mkswap && swapon ${swap_file}"
        return 0
    fi

    # 3. 检查文件系统类型以避免 fallocate 在 Btrfs 上的坑
    local root_fs
    root_fs=$(df -T / | awk 'NR==2 {print $2}')

    if [[ "$root_fs" == "btrfs" ]]; then
        log_info "检测到 Btrfs 文件系统，采用禁用 CoW 的安全模式创建..."
        truncate -s 0 "$swap_file"
        chattr +C "$swap_file" 2>/dev/null # 禁用 CoW 属性
        dd if=/dev/zero of="$swap_file" bs=1M count="$target_swap_size" status=progress
    elif command -v fallocate &>/dev/null && [[ "$root_fs" != "zfs" ]]; then
        # ZFS 也不支持普通 fallocate swap，同样 fallback
        fallocate -l "${target_swap_size}M" "$swap_file" 2>/dev/null || \
            dd if=/dev/zero of="$swap_file" bs=1M count="$target_swap_size" status=progress
    else
        dd if=/dev/zero of="$swap_file" bs=1M count="$target_swap_size" status=progress
    fi

    # 4. 配置权限并激活
    chmod 600 "$swap_file"
    mkswap "$swap_file"
    swapon "$swap_file" || {
        log_error "Swap 激活失败！可能是文件系统不支持空洞文件。"
        return 1
    }

    # 5. 持久化到 fstab
    if ! grep -q "^$swap_file" /etc/fstab; then
        echo "$swap_file none swap sw 0 0" >> /etc/fstab
    fi

    # 6. 设置 swappiness
    sysctl -w vm.swappiness=10 2>/dev/null
    
    # 确保配置目录存在
    mkdir -p /etc/sysctl.d
    local sysctl_conf="/etc/sysctl.d/99-inition.conf"
    
    if ! grep -q "^vm.swappiness" "$sysctl_conf" 2>/dev/null; then
        echo -e "\n# --- Swap Optimization ---" >> "$sysctl_conf"
        echo "vm.swappiness = 10" >> "$sysctl_conf"
    else
        # 如果已经有其他值，将其替换为 10
        sed -i 's/^vm.swappiness.*/vm.swappiness = 10/' "$sysctl_conf"
    fi
    sysctl -p "$sysctl_conf" >/dev/null 2>&1

    log_info "✓ Swap 配置完成: ${target_swap_size}MB (swappiness=10)"
}

# ═══════════════════════════════════════════════════════════════
# [D3] 时区与 NTP 配置（基于 IP 地理位置自动检测）
# ═══════════════════════════════════════════════════════════════

# 检测公网 IP 是否为内网地址
is_private_ip() {
    local ip="$1"
    # RFC 1918 / RFC 6598 / Loopback
    if [[ "$ip" =~ ^10\. ]] || \
       [[ "$ip" =~ ^172\.(1[6-9]|2[0-9]|3[01])\. ]] || \
       [[ "$ip" =~ ^192\.168\. ]] || \
       [[ "$ip" =~ ^100\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\. ]] || \
       [[ "$ip" =~ ^127\. ]]; then
        return 0  # 是内网
    fi
    return 1  # 不是内网
}

# 根据 IP 地理位置检测推荐时区
detect_timezone_by_ip() {
    local public_ip=""
    local detected_tz=""
    local detected_country=""
    local detected_city=""

    # 1. 获取公网 IP
    log_info "获取服务器公网 IP..."
    if command -v curl &>/dev/null; then
        public_ip=$(curl -fsSL --connect-timeout 5 --max-time 10 https://ip.sb 2>/dev/null || \
                    curl -fsSL --connect-timeout 5 --max-time 10 https://ifconfig.me 2>/dev/null || \
                    curl -fsSL --connect-timeout 5 --max-time 10 https://api.ipify.org 2>/dev/null || true)
    fi

    if [[ -z "$public_ip" ]]; then
        log_warn "无法获取公网 IP，使用默认时区: $DEFAULT_TIMEZONE"
        echo "$DEFAULT_TIMEZONE"
        return
    fi

    log_info "服务器公网 IP: $public_ip"

    # 2. 检查是否为内网地址
    if is_private_ip "$public_ip"; then
        log_info "检测到内网地址，默认使用中国时区: $DEFAULT_TIMEZONE"
        echo "$DEFAULT_TIMEZONE"
        return
    fi

    # 3. 通过 IP 查询地理位置和时区
    log_info "查询 IP 地理位置..."
    local geo_json=""
    geo_json=$(curl -fsSL --connect-timeout 5 --max-time 10 "http://ip-api.com/json/${public_ip}?fields=status,country,city,timezone" 2>/dev/null || true)

    if [[ -n "$geo_json" ]] && echo "$geo_json" | grep -q '"status":"success"'; then
        detected_tz=$(echo "$geo_json" | grep -oP '"timezone":"\K[^"]+' 2>/dev/null || \
                      echo "$geo_json" | sed -n 's/.*"timezone":"\([^"]*\)".*/\1/p')
        detected_country=$(echo "$geo_json" | grep -oP '"country":"\K[^"]+' 2>/dev/null || \
                           echo "$geo_json" | sed -n 's/.*"country":"\([^"]*\)".*/\1/p')
        detected_city=$(echo "$geo_json" | grep -oP '"city":"\K[^"]+' 2>/dev/null || \
                        echo "$geo_json" | sed -n 's/.*"city":"\([^"]*\)".*/\1/p')
    fi

    if [[ -n "$detected_tz" ]]; then
        log_info "IP 地理位置: ${detected_country} / ${detected_city}"
        log_info "推荐时区: ${detected_tz}"
        echo "$detected_tz"
    else
        log_warn "无法通过 IP 检测时区，使用默认: $DEFAULT_TIMEZONE"
        echo "$DEFAULT_TIMEZONE"
    fi
}


configure_timezone_ntp() {
    log_step "配置时区和 NTP 时间同步..."

    if ! command -v timedatectl &>/dev/null; then
        log_warn "timedatectl 不可用，跳过时区配置"
    else
        local current_tz
        current_tz=$(timedatectl show -p Timezone --value 2>/dev/null || \
                     timedatectl 2>/dev/null | grep "Time zone" | awk '{print $3}' || echo "unknown")
        log_info "当前时区: $current_tz"

        # 基于 IP 检测推荐时区
        local suggested_tz
        suggested_tz=$(detect_timezone_by_ip)

        local final_tz="$suggested_tz"

        if [[ "$INTERACTIVE_MODE" == true ]]; then
            echo ""
            echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
            echo -e "${BOLD}  当前时区:   ${YELLOW}${current_tz}${NC}"
            echo -e "${BOLD}  推荐时区:   ${GREEN}${suggested_tz}${NC}"
            echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
            echo ""
            echo -e "${YELLOW}请选择时区：${NC}"
            echo -e "  ${GREEN}1${NC}) 新加坡        (Asia/Singapore)"
            echo -e "  ${GREEN}2${NC}) 中国          (Asia/Shanghai)"
            echo -e "  ${GREEN}3${NC}) 日本          (Asia/Tokyo)"
            echo -e "  ${GREEN}4${NC}) 美西时间      (America/Los_Angeles)"
            echo -e "  ${GREEN}5${NC}) 美东时间      (America/New_York)"
            echo -e "  ${GREEN}6${NC}) 英国时间      (Europe/London)"
            echo -e "  ${GREEN}7${NC}) 德国          (Europe/Berlin)"
            echo -e "  ${GREEN}8${NC}) 越南          (Asia/Ho_Chi_Minh)"
            echo -e "  ${GREEN}9${NC}) 印度          (Asia/Kolkata)"
            echo -e "  ${GREEN}10${NC}) 韩国         (Asia/Seoul)"
            echo -e "  ${GREEN}11${NC}) 使用推荐时区 (${suggested_tz})"
            echo -e "  ${GREEN}12${NC}) 手动输入"
            echo -e "  ${GREEN}0${NC}) 保持当前时区不变"
            echo ""
            echo -ne "${YELLOW}请输入选项 [0-12] (直接回车 = 使用推荐): ${NC}"
            read -r tz_choice

            case $tz_choice in
                1)  final_tz="Asia/Singapore" ;;
                2)  final_tz="Asia/Shanghai" ;;
                3)  final_tz="Asia/Tokyo" ;;
                4)  final_tz="America/Los_Angeles" ;;
                5)  final_tz="America/New_York" ;;
                6)  final_tz="Europe/London" ;;
                7)  final_tz="Europe/Berlin" ;;
                8)  final_tz="Asia/Ho_Chi_Minh" ;;
                9)  final_tz="Asia/Kolkata" ;;
                10) final_tz="Asia/Seoul" ;;
                11|"")
                    final_tz="$suggested_tz"
                    ;;
                12)
                    echo -ne "${YELLOW}请输入时区 (如 Asia/Shanghai): ${NC}"
                    read -r custom_tz
                    if [[ -n "$custom_tz" ]]; then
                        # 验证时区是否有效
                        if timedatectl list-timezones 2>/dev/null | grep -qx "$custom_tz"; then
                            final_tz="$custom_tz"
                        else
                            log_warn "无效时区: $custom_tz，使用推荐时区: $suggested_tz"
                            final_tz="$suggested_tz"
                        fi
                    else
                        final_tz="$suggested_tz"
                    fi
                    ;;
                0)
                    log_info "保持当前时区: $current_tz"
                    final_tz=""
                    ;;
                *)
                    log_warn "无效选项，使用推荐时区: $suggested_tz"
                    final_tz="$suggested_tz"
                    ;;
            esac
        fi

        if [[ -n "$final_tz" ]] && [[ "$final_tz" != "$current_tz" ]]; then
            execute timedatectl set-timezone "$final_tz"
            log_info "✓ 时区已设置为: $final_tz"
        elif [[ -n "$final_tz" ]]; then
            log_info "时区已经是 $final_tz，无需修改"
        fi
    fi

    # 安装 chrony（NTP 时间同步）
    log_info "安装并配置 chrony..."
    case $PACKAGE_MANAGER in
        "yum"|"dnf")
            execute $PACKAGE_MANAGER install -y chrony
            execute systemctl enable chronyd
            execute systemctl start chronyd
            ;;
        "apt")
            execute apt install -y chrony
            execute systemctl enable chrony 2>/dev/null || execute systemctl enable chronyd 2>/dev/null || true
            execute systemctl start chrony 2>/dev/null || execute systemctl start chronyd 2>/dev/null || true
            ;;
        "zypper")
            execute zypper --non-interactive install chrony
            execute systemctl enable chronyd
            execute systemctl start chronyd
            ;;
    esac

    if [[ "$DRY_RUN" != true ]]; then
        if command -v chronyc &>/dev/null; then
            log_info "NTP 同步状态："
            chronyc tracking 2>/dev/null | head -5 || true
            log_info "✓ NTP 时间同步已配置（使用 chrony）"
        fi
    fi
}
install_fail2ban() {
    log_step "安装fail2ban（暂不配置）..."

    # [C3] 幂等检查
    if command -v fail2ban-client &>/dev/null; then
        log_info "fail2ban 已安装，跳过"
        return 0
    fi

    case $PACKAGE_MANAGER in
        "yum")
            execute yum install -y fail2ban
            ;;
        "apt")
            execute apt install -y fail2ban
            ;;
    esac

    log_info "fail2ban安装完成，等待后续配置"
}

# ═══════════════════════════════════════════════════════════════
# [B4] fail2ban 白名单增强
# 注意: 此函数通过 stdout 返回白名单字符串，日志必须输出到 stderr
# ═══════════════════════════════════════════════════════════════
get_fail2ban_whitelist() {
    local whitelist="$F2B_WHITELIST"

    # 从白名单文件读取额外 IP
    if [[ -f "$F2B_WHITELIST_FILE" ]]; then
        local file_ips
        file_ips=$(grep -v '^#' "$F2B_WHITELIST_FILE" | grep -v '^[[:space:]]*$' | tr '\n' ' ')
        if [[ -n "$file_ips" ]]; then
            whitelist="$whitelist $file_ips"
            log_info "从白名单文件加载了额外的 IP: $file_ips" >&2
        fi
    else
        log_info "白名单文件 $F2B_WHITELIST_FILE 不存在，仅使用默认白名单" >&2
        log_info "如需添加白名单 IP，请创建该文件，每行一个 IP/CIDR" >&2
    fi

    echo "$whitelist"
}

# ═══════════════════════════════════════════════════════════════
# [B1] 运行外部脚本（增加 SHA256 完整性校验）
# ═══════════════════════════════════════════════════════════════
run_external_script() {
    log_step "准备运行外部脚本..."

    if [[ "$INTERACTIVE_MODE" == true ]]; then
        echo -e "${YELLOW}即将运行外部脚本，该脚本可能包含SSH端口修改等配置${NC}"
        echo -e "${YELLOW}按任意键继续...${NC}"
        read -n 1 -s
    fi

    if ! command -v curl &>/dev/null; then
        log_error "curl 不可用，无法执行外部脚本"
        return 1
    fi

    # [Q8] 使用 mktemp 生成唯一临时文件，避免时间戳冲突
    local tmp_script
    tmp_script=$(mktemp "/tmp/inition_ext_XXXXXX.sh") || { log_error "mktemp 失败，无法创建临时文件"; return 1; }
    local retry_count=0

    log_info "下载外部脚本..."

    while [[ $retry_count -lt $EXTERNAL_SCRIPT_MAX_RETRIES ]]; do
        retry_count=$((retry_count + 1))
        log_info "下载尝试 (第 ${retry_count}/${EXTERNAL_SCRIPT_MAX_RETRIES} 次)..."

        if curl -fsSL -o "$tmp_script" "$EXTERNAL_SCRIPT_URL" 2>/dev/null; then
            break
        fi

        if [[ $retry_count -lt $EXTERNAL_SCRIPT_MAX_RETRIES ]]; then
            log_warn "下载失败，等待3秒后重试..."
            sleep 3
        fi
    done

    if [[ ! -f "$tmp_script" ]] || [[ ! -s "$tmp_script" ]]; then
        log_error "外部脚本下载失败"
        rm -f "$tmp_script" 2>/dev/null
        return 1
    fi

    # [B1] SHA256 完整性校验
    local actual_sha256
    if command -v sha256sum &>/dev/null; then
        actual_sha256=$(sha256sum "$tmp_script" | awk '{print $1}')
    elif command -v shasum &>/dev/null; then
        actual_sha256=$(shasum -a 256 "$tmp_script" | awk '{print $1}')
    fi

    if [[ -n "$actual_sha256" ]]; then
        if [[ -n "$EXTERNAL_SCRIPT_SHA256" ]]; then
            if [[ "$actual_sha256" != "$EXTERNAL_SCRIPT_SHA256" ]]; then
                log_error "外部脚本 SHA256 校验失败！"
                log_error "  预期: $EXTERNAL_SCRIPT_SHA256"
                log_error "  实际: $actual_sha256"
                log_error "脚本可能已被篡改，拒绝执行"

                if [[ "$INTERACTIVE_MODE" == true ]]; then
                    echo -e "${RED}SHA256 校验失败！是否仍然继续执行？(y/N)${NC}"
                    read -r force_run
                    if [[ ! "$force_run" =~ ^[Yy]$ ]]; then
                        rm -f "$tmp_script"
                        return 1
                    fi
                    log_warn "用户强制执行未通过校验的脚本"
                else
                    rm -f "$tmp_script"
                    return 1
                fi
            else
                log_info "✓ 外部脚本 SHA256 校验通过"
            fi
        else
            log_warn "未配置 SHA256 校验，跳过完整性验证"
            log_info "脚本 SHA256: $actual_sha256"
            log_info "如需启用校验，请将此哈希值填入脚本头部的 EXTERNAL_SCRIPT_SHA256 变量"
        fi
    fi

    # 执行脚本
    if [[ "$DRY_RUN" == true ]]; then
        log_dry "  bash ${tmp_script}"
        rm -f "$tmp_script"
        return 0
    fi

    log_info "正在执行外部脚本..."
    if bash "$tmp_script"; then
        log_info "外部脚本执行成功"
        rm -f "$tmp_script"
        return 0
    else
        local exit_code=$?
        log_warn "外部脚本执行失败 (退出码: $exit_code)"
        rm -f "$tmp_script"

        if [[ "$INTERACTIVE_MODE" == true ]]; then
            echo -e "${YELLOW}是否继续执行后续配置？(y/n)${NC}"
            read -r continue_choice
            if [[ "$continue_choice" =~ ^[Yy]$ ]]; then
                log_warn "跳过外部脚本，继续使用默认配置"
                return 1
            else
                log_error "用户选择退出"
                exit 1
            fi
        else
            log_warn "非交互模式，继续后续配置"
            return 1
        fi
    fi
}

# ═══════════════════════════════════════════════════════════════
# 重新检测SSH端口并更新iptables规则
# ═══════════════════════════════════════════════════════════════
update_ssh_port_iptables() {
    log_step "重新检测SSH端口并更新iptables规则..."

    sleep 2

    local old_ssh_port="$SSH_PORT"
    detect_ssh_port

    if [[ "$SSH_PORT" != "$old_ssh_port" ]]; then
        log_info "检测到SSH端口已更改: $old_ssh_port -> $SSH_PORT"

        # 删除旧的SSH端口规则
        log_info "删除旧的SSH端口规则..."
        execute iptables -D INPUT -p tcp --dport "$old_ssh_port" -m state --state NEW -j ddos_protect 2>/dev/null || true
        execute iptables -D INPUT -p tcp --dport "$old_ssh_port" -j ACCEPT 2>/dev/null || true

        # 添加新的SSH端口规则
        log_info "添加新的SSH端口规则..."
        execute iptables -I INPUT -p tcp --dport "$SSH_PORT" -m state --state NEW -j ddos_protect
        execute iptables -I INPUT -p tcp --dport "$SSH_PORT" -j ACCEPT

        # 更新 IPv6 规则
        if command -v ip6tables &>/dev/null; then
            execute ip6tables -D INPUT -p tcp --dport "$old_ssh_port" -j ACCEPT 2>/dev/null || true
            execute ip6tables -I INPUT 4 -p tcp --dport "$SSH_PORT" -j ACCEPT
        fi

        save_iptables
        log_info "iptables规则已更新为新的SSH端口: $SSH_PORT"
    else
        log_info "SSH端口未发生变化，保持: $SSH_PORT"
    fi
}

# ═══════════════════════════════════════════════════════════════
# 配置fail2ban [B4 白名单增强]
# ═══════════════════════════════════════════════════════════════
configure_fail2ban_after_external() {
    log_step "配置fail2ban（SSH端口: $SSH_PORT）..."

    # 根据操作系统确定SSH日志路径
    local ssh_log_path
    case $OS in
        "ubuntu"|"debian")
            ssh_log_path="/var/log/auth.log"
            ;;
        "centos"|"rhel")
            ssh_log_path="/var/log/secure"
            ;;
        *)
            ssh_log_path="/var/log/auth.log"
            ;;
    esac

    log_info "SSH日志路径: $ssh_log_path"

    if [[ "$DRY_RUN" != true ]]; then
        if [[ ! -f "$ssh_log_path" ]]; then
            log_warn "SSH日志文件 $ssh_log_path 不存在，尝试创建..."
            touch "$ssh_log_path"
            chmod 640 "$ssh_log_path"
        fi
    fi

    # [B4] 获取白名单
    local whitelist
    whitelist=$(get_fail2ban_whitelist)
    log_info "fail2ban 白名单: $whitelist"

    # 写入配置 [A1 使用配置变量]
    if [[ "$DRY_RUN" == true ]]; then
        log_dry "  写入 /etc/fail2ban/jail.local"
        log_dry "  systemctl enable/restart fail2ban"
        return 0
    fi

    # [S4] 使用 F2B_BANACTION 变量控制封禁方式
    # [F8] 先生成基础配置，再按 F2B_JAILS_EXTRA 动态追加额外 jail
    cat > /etc/fail2ban/jail.local << EOF
[DEFAULT]
# 封禁时长（-1=永久）
bantime = ${F2B_BANTIME}
# 查找时间窗口(秒)
findtime = ${F2B_FINDTIME}
# 最大重试次数
maxretry = ${F2B_MAXRETRY}
# [S4] 封禁方式由 F2B_BANACTION 变量控制
banaction = ${F2B_BANACTION}
# 白名单 [B4]
ignoreip = ${whitelist}

[sshd]
enabled = true
port = ${SSH_PORT}
filter = sshd
logpath = ${ssh_log_path}
maxretry = ${F2B_MAXRETRY}
bantime = ${F2B_BANTIME}
findtime = ${F2B_FINDTIME}

# 以下 jail 默认关闭，由 F2B_JAILS_EXTRA 变量控制
[nginx-http-auth]
enabled = false

[nginx-noscript]
enabled = false

[nginx-badbots]
enabled = false

[nginx-noproxy]
enabled = false

[postfix]
enabled = false

[recidive]
enabled = false
EOF

    # [F8] 动态启用 F2B_JAILS_EXTRA 中指定的额外 jail
    # 各 jail 所需的日志文件路径（用于预检）
    declare -A JAIL_LOGPATH_MAP=(
        [nginx-http-auth]="/var/log/nginx/error.log"
        [nginx-noscript]="/var/log/nginx/access.log"
        [nginx-badbots]="/var/log/nginx/access.log"
        [nginx-noproxy]="/var/log/nginx/access.log"
        [postfix]="/var/log/mail.log /var/log/maillog"
        [recidive]="/var/log/fail2ban.log"
    )
    if [[ ${#F2B_JAILS_EXTRA[@]} -gt 0 ]]; then
        log_info "启用额外 fail2ban jail: ${F2B_JAILS_EXTRA[*]}"
        for extra_jail in "${F2B_JAILS_EXTRA[@]}"; do
            # [F8] 预检：确认 jail 所需的日志文件至少有一个存在
            local required_log="${JAIL_LOGPATH_MAP[$extra_jail]:-}"
            local log_ok=true
            if [[ -n "$required_log" ]]; then
                log_ok=false
                for rlog in $required_log; do
                    if [[ -f "$rlog" ]]; then
                        log_ok=true
                        break
                    fi
                done
            fi
            if [[ "$log_ok" != true ]]; then
                log_warn "  ✗ 跳过 jail '${extra_jail}'：所需日志文件不存在（${required_log}）"
                log_warn "    请先安装并启动对应服务，再重新运行脚本或手动启用该 jail"
                continue
            fi
            if grep -q "^\[${extra_jail}\]" /etc/fail2ban/jail.local 2>/dev/null; then
                sed -i "/^\[${extra_jail}\]/,/^\[/ s/^enabled = false/enabled = true/" /etc/fail2ban/jail.local
                log_info "  ✓ 已启用 jail: ${extra_jail}"
            else
                cat >> /etc/fail2ban/jail.local << JAIL_EOF

[${extra_jail}]
enabled = true
maxretry = ${F2B_MAXRETRY}
bantime = ${F2B_BANTIME}
findtime = ${F2B_FINDTIME}
JAIL_EOF
                log_info "  ✓ 已追加并启用 jail: ${extra_jail}"
            fi
        done
    fi

    # 检查现有封禁状态
    local has_banned_ips=false
    local banned_ips_count=0
    if systemctl is-active --quiet fail2ban 2>/dev/null; then
        if fail2ban-client status sshd >/dev/null 2>&1; then
            local status_output
            status_output=$(fail2ban-client status sshd 2>/dev/null)
            if echo "$status_output" | grep -q "Currently banned: [1-9]"; then
                has_banned_ips=true
                banned_ips_count=$(echo "$status_output" | grep "Currently banned:" | awk '{print $3}')
                log_info "检测到已有 $banned_ips_count 个被封禁的IP"
            fi
        fi
    fi

    # 启动服务
    systemctl enable fail2ban

    if [[ "$has_banned_ips" == true ]]; then
        log_info "使用 reload 保留封禁状态..."
        fail2ban-client reload 2>/dev/null || {
            log_warn "reload 失败，尝试 restart..."
            systemctl restart fail2ban
        }
    else
        systemctl restart fail2ban
    fi

    sleep 3

    # 验证配置
    log_info "验证fail2ban配置..."
    if fail2ban-client status sshd >/dev/null 2>&1; then
        log_info "✓ fail2ban SSH监控配置成功（端口: $SSH_PORT）"
        fail2ban-client status sshd

        if [[ "$has_banned_ips" == true ]]; then
            local current_banned
            current_banned=$(fail2ban-client status sshd 2>/dev/null | grep "Currently banned:" | awk '{print $3}')
            if [[ "$current_banned" == "$banned_ips_count" ]]; then
                log_info "✓ 已保留 $current_banned 个被封禁的IP"
            else
                log_warn "封禁IP数量变化: 之前 $banned_ips_count → 现在 $current_banned"
            fi
        fi
    else
        log_warn "fail2ban SSH监控可能配置有误，请检查: journalctl -u fail2ban"
    fi

    log_info "fail2ban配置完成 | 端口: $SSH_PORT | 日志: $ssh_log_path"
    log_info "规则: ${F2B_MAXRETRY}次失败/${F2B_FINDTIME}秒内 → 封禁${F2B_BANTIME}秒 (-1=永久)"
}

# ═══════════════════════════════════════════════════════════════
# 分析并封禁恶意网段
# ═══════════════════════════════════════════════════════════════
block_bad_subnets() {
    log_step "分析SSH登录失败日志并封禁恶意网段..."

    if ! command -v fail2ban-client &>/dev/null; then
        log_error "fail2ban未安装，无法使用此功能"
        return 1
    fi

    if ! systemctl is-active --quiet fail2ban 2>/dev/null; then
        log_warn "fail2ban服务未运行，尝试启动..."
        execute systemctl start fail2ban 2>/dev/null || {
            log_error "无法启动fail2ban服务"
            return 1
        }
    fi

    # 判断日志文件
    shopt -s nullglob
    LOG_FILES=()
    declare -A seen_files=()
    for candidate in /var/log/auth.log /var/log/auth.log* /var/log/secure /var/log/secure*; do
        [[ -f "$candidate" ]] || continue
        if [[ -z "${seen_files[$candidate]:-}" ]]; then
            LOG_FILES+=("$candidate")
            seen_files[$candidate]=1
        fi
    done
    shopt -u nullglob

    if [[ ${#LOG_FILES[@]} -eq 0 ]]; then
        log_warn "未找到 /var/log/auth.log* 或 /var/log/secure*"
        return 1
    fi

    log_info "使用日志文件: ${LOG_FILES[*]}"

    # [Q6] 分别提取 IPv4 和 IPv6 失败 IP
    local FAILED_IPS_V4
    FAILED_IPS_V4=$(zgrep -h "Failed password" "${LOG_FILES[@]}" 2>/dev/null \
        | awk '{print $(NF-3)}' \
        | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' || true)

    local FAILED_IPS_V6
    FAILED_IPS_V6=$(zgrep -h "Failed password" "${LOG_FILES[@]}" 2>/dev/null \
        | awk '{print $(NF-3)}' \
        | grep -Eo '([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}' || true)

    if [[ -z "$FAILED_IPS_V4" ]] && [[ -z "$FAILED_IPS_V6" ]]; then
        log_info "未发现失败登录记录"
        return 0
    fi

    local banned_count=0

    # --- IPv4: 按 /24 分组封禁 ---
    if [[ -n "$FAILED_IPS_V4" ]]; then
        local SUBNETS_V4 BAD_SUBNETS_V4
        SUBNETS_V4=$(echo "$FAILED_IPS_V4" | awk -F. '{print $1"."$2"."$3".0/24"}' | sort | uniq -c | sort -nr)
        BAD_SUBNETS_V4=$(echo "$SUBNETS_V4" | awk -v threshold="$BAD_SUBNET_THRESHOLD" '$1>threshold {print $1,$2}')

        if [[ -n "$BAD_SUBNETS_V4" ]]; then
            echo ""
            echo "以下 IPv4 网段出现超过 $BAD_SUBNET_THRESHOLD 次失败登录（将封禁 /24 网段）："
            while read -r count subnet; do
                echo "封禁: $subnet  (${count} 次失败)"
                log_info "封禁IPv4网段: $subnet"
                if [[ "$DRY_RUN" == true ]]; then
                    log_dry "  fail2ban-client set sshd banip $subnet"
                    ((banned_count++))
                else
                    if fail2ban-client set sshd banip "$subnet" 2>/dev/null; then
                        ((banned_count++))
                    else
                        log_warn "封禁IPv4网段 $subnet 失败"
                    fi
                fi
            done <<< "$BAD_SUBNETS_V4"
        else
            log_info "IPv4 地址：没有出现超过 $BAD_SUBNET_THRESHOLD 次失败登录的 /24 段"
        fi
    fi

    # --- IPv6: 按 /48 分组封禁 ---
    if [[ -n "$FAILED_IPS_V6" ]]; then
        local SUBNETS_V6 BAD_SUBNETS_V6
        # 取 IPv6 地址的前3个十六进制组（/48）
        SUBNETS_V6=$(echo "$FAILED_IPS_V6" \
            | awk -F: '{printf "%s:%s:%s::/48\n",$1,$2,$3}' \
            | sort | uniq -c | sort -nr)
        BAD_SUBNETS_V6=$(echo "$SUBNETS_V6" | awk -v threshold="$BAD_SUBNET_THRESHOLD" '$1>threshold {print $1,$2}')

        if [[ -n "$BAD_SUBNETS_V6" ]]; then
            echo ""
            echo "以下 IPv6 网段出现超过 $BAD_SUBNET_THRESHOLD 次失败登录（将封禁 /48 网段）："
            while read -r count subnet; do
                echo "封禁: $subnet  (${count} 次失败)"
                log_info "封禁IPv6网段: $subnet"
                if [[ "$DRY_RUN" == true ]]; then
                    log_dry "  fail2ban-client set sshd banip $subnet"
                    ((banned_count++))
                else
                    if fail2ban-client set sshd banip "$subnet" 2>/dev/null; then
                        ((banned_count++))
                    else
                        log_warn "封禁IPv6网段 $subnet 失败"
                    fi
                fi
            done <<< "$BAD_SUBNETS_V6"
        else
            log_info "IPv6 地址：没有出现超过 $BAD_SUBNET_THRESHOLD 次失败登录的 /48 段"
        fi
    fi

    echo ""
    echo "操作完成。已封禁 $banned_count 个网段（包含 IPv4/IPv6）"
    log_info "本次封禁任务完成，共封禁 $banned_count 个网段"
}

# ═══════════════════════════════════════════════════════════════
# [E3] 美化状态显示
# ═══════════════════════════════════════════════════════════════
show_status() {
    log_step "生成配置状态报告..."

    local separator="${CYAN}═══════════════════════════════════════════════════════════${NC}"
    local sub_sep="${DIM}───────────────────────────────────────────────────────────${NC}"

    echo ""
    echo -e "$separator"
    echo -e "${CYAN}${BOLD}  📋 配置完成总结 — inition.sh v2.0${NC}"
    if [[ "$DRY_RUN" == true ]]; then
        echo -e "${MAGENTA}  ⚠ DRY-RUN 模式（未实际修改系统）${NC}"
    fi
    echo -e "$separator"

    # ── 系统信息 ──
    echo -e "\n${BOLD}  🖥️  系统信息${NC}"
    echo -e "$sub_sep"
    echo -e "  操作系统:        ${GREEN}${OS}${NC}"
    echo -e "  SSH 端口:        ${GREEN}${SSH_PORT}${NC}"

    local iptables_ver=$(iptables --version 2>/dev/null | head -1 || echo "未安装")
    echo -e "  iptables:        ${GREEN}${iptables_ver}${NC}"

    if command -v timedatectl &>/dev/null; then
        local tz=$(timedatectl show -p Timezone --value 2>/dev/null || echo "unknown")
        echo -e "  时区:            ${GREEN}${tz}${NC}"
    fi

    # ── 安全状态 ──
    echo -e "\n${BOLD}  🛡️  安全状态${NC}"
    echo -e "$sub_sep"

    # iptables
    local ipt_status="未配置"
    if iptables -L INPUT -n 2>/dev/null | grep -q "ACCEPT\|DROP"; then
        ipt_status="${GREEN}● 已配置${NC}"
    fi
    echo -e "  IPv4 防火墙:     ${ipt_status}"

    # ip6tables
    if command -v ip6tables &>/dev/null; then
        local ip6_status="未配置"
        if ip6tables -L INPUT -n 2>/dev/null | grep -q "ACCEPT\|DROP"; then
            ip6_status="${GREEN}● 已配置${NC}"
        fi
        echo -e "  IPv6 防火墙:     ${ip6_status}"
    fi

    # fail2ban
    if systemctl is-active --quiet fail2ban 2>/dev/null; then
        echo -e "  fail2ban:        ${GREEN}● 运行中${NC}"
        echo -e "  SSH 防护:        ${F2B_MAXRETRY}次失败 → 永久封禁"
    else
        echo -e "  fail2ban:        ${RED}○ 未运行${NC}"
    fi

    # rsyslog
    if systemctl is-active --quiet rsyslog 2>/dev/null; then
        echo -e "  rsyslog:         ${GREEN}● 运行中${NC}"
    else
        echo -e "  rsyslog:         ${RED}○ 未运行${NC}"
    fi

    # ── 性能优化 ──
    echo -e "\n${BOLD}  ⚡ 性能优化${NC}"
    echo -e "$sub_sep"

    # BBR
    local bbr_status="${RED}✗ 未启用${NC}"
    if sysctl net.ipv4.tcp_congestion_control 2>/dev/null | grep -q bbr; then
        bbr_status="${GREEN}✓ 已启用${NC}"
    fi
    echo -e "  TCP BBR:         ${bbr_status}"

    # Swap
    local swap_info
    swap_info=$(free -m | awk '/Swap/ {print $2}')
    if [[ "$swap_info" -gt 0 ]]; then
        echo -e "  Swap:            ${GREEN}${swap_info}MB${NC}"
    else
        echo -e "  Swap:            ${YELLOW}未配置${NC}"
    fi

    # Sysctl
    if [[ -f /etc/sysctl.d/99-inition.conf ]]; then
        echo -e "  内核参数:        ${GREEN}✓ 已优化${NC}"
    else
        echo -e "  内核参数:        ${YELLOW}默认${NC}"
    fi

    # NTP
    if command -v chronyc &>/dev/null && systemctl is-active --quiet chrony 2>/dev/null || systemctl is-active --quiet chronyd 2>/dev/null; then
        echo -e "  NTP 同步:        ${GREEN}✓ chrony 运行中${NC}"
    else
        echo -e "  NTP 同步:        ${YELLOW}未配置${NC}"
    fi

    # ── 端口信息 ──
    echo -e "\n${BOLD}  🌐 已开放端口${NC}"
    echo -e "$sub_sep"
    echo -e "  SSH:             ${SSH_PORT} (DDoS防护)"
    echo -e "  TCP:             ${OPEN_TCP_PORTS[*]}"
    echo -e "  管理:            ${MANAGEMENT_PORTS[*]} (DDoS防护)"
    echo -e "  FTP:             ${FTP_PORTS[*]}, 被动 ${FTP_PASSIVE_RANGE}"
    echo -e "  高端口:          ${HIGH_PORT_START}-${HIGH_PORT_END} (TCP/UDP)"

    # ── 防护规则 ──
    echo -e "\n${BOLD}  🔒 防护规则${NC}"
    echo -e "$sub_sep"
    echo -e "  DDoS 防护:       ${DDOS_WINDOW}秒内 ${DDOS_THRESHOLD} 次新连接触发限制"
    echo -e "  ICMP ping:       ${GREEN}已开放${NC}"
    echo -e "  FORWARD 链:      ACCEPT (Docker/PVE 兼容)"

    # ── 工具状态 ──
    echo -e "\n${BOLD}  🔧 已安装工具${NC}"
    echo -e "$sub_sep"
    local check_tools=("btop" "curl" "wget" "mtr" "traceroute" "bandwhich" "tcping")
    for tool in "${check_tools[@]}"; do
        if command -v "$tool" &>/dev/null; then
            echo -e "  ${tool}:$(printf '%*s' $((16 - ${#tool})) '')${GREEN}✓${NC}"
        else
            echo -e "  ${tool}:$(printf '%*s' $((16 - ${#tool})) '')${DIM}✗${NC}"
        fi
    done

    # ── 常用命令 ──
    echo -e "\n${BOLD}  📌 常用命令${NC}"
    echo -e "$sub_sep"
    echo -e "  查看防火墙:      ${DIM}iptables -L -n${NC}"
    echo -e "  查看 fail2ban:    ${DIM}fail2ban-client status sshd${NC}"
    echo -e "  解封 IP:          ${DIM}fail2ban-client set sshd unbanip <IP>${NC}"
    echo -e "  查看封禁列表:    ${DIM}iptables -L -n | grep DROP${NC}"
    echo -e "  查看监听端口:    ${DIM}ss -tulnp${NC}"
    echo -e "  测试 Docker:      ${DIM}docker run hello-world${NC}"
    echo -e "  卸载本脚本配置:  ${DIM}bash inition.sh --uninstall${NC}"

    # ── ICMP / Docker / 代理说明 ──
    echo -e "\n${BOLD}  📝 使用提示${NC}"
    echo -e "$sub_sep"
    echo -e "  • ICMP ping 已开放；禁用命令:"
    echo -e "    ${DIM}iptables -D INPUT -p icmp --icmp-type echo-request -j ACCEPT${NC}"
    echo -e "    ${DIM}iptables -A INPUT -p icmp --icmp-type echo-request -j DROP${NC}"
    echo -e "  • Docker 已使用 iptables-legacy 确保兼容"
    echo -e "  • fail2ban 白名单文件: ${DIM}${F2B_WHITELIST_FILE}${NC}"

    echo -e "\n$separator"
}

# ═══════════════════════════════════════════════════════════════
# [E2] 交互式菜单
# ═══════════════════════════════════════════════════════════════
show_main_menu() {
    echo ""
    echo -e "${CYAN}╔══════════════════════════════════════════════════╗${NC}"
    echo -e "${CYAN}║${NC}${BOLD}        请选择要执行的配置模式                  ${NC}${CYAN}║${NC}"
    echo -e "${CYAN}╠══════════════════════════════════════════════════╣${NC}"
    echo -e "${CYAN}║${NC}  ${GREEN}1${NC}) 全部执行（推荐）                            ${CYAN}║${NC}"
    echo -e "${CYAN}║${NC}  ${GREEN}2${NC}) 仅基础安全（防火墙 + fail2ban + 工具）       ${CYAN}║${NC}"
    echo -e "${CYAN}║${NC}  ${GREEN}3${NC}) 仅系统优化（内核参数 + Swap + 时区）          ${CYAN}║${NC}"
    echo -e "${CYAN}║${NC}  ${GREEN}4${NC}) 自定义选择                                  ${CYAN}║${NC}"
    echo -e "${CYAN}║${NC}  ${GREEN}0${NC}) 退出                                        ${CYAN}║${NC}"
    echo -e "${CYAN}╚══════════════════════════════════════════════════╝${NC}"
    echo -ne "${YELLOW}请输入选项 [0-4]: ${NC}"
    read -r menu_choice

    case $menu_choice in
        1)
            log_info "已选择: 全部执行"
            # 所有模块保持默认（全开）
            ;;
        2)
            log_info "已选择: 仅基础安全"
            MOD_SYSCTL=false
            MOD_SWAP=false
            MOD_TIMEZONE=false
            ;;
        3)
            log_info "已选择: 仅系统优化"
            MOD_UPDATE=false
            MOD_BASIC_TOOLS=false
            MOD_CUSTOM_TOOLS=false
            MOD_RSYSLOG=false
            MOD_FIREWALL=false
            MOD_IPV6_FIREWALL=false
            MOD_FAIL2BAN=false
            MOD_EXTERNAL_SCRIPT=false
            MOD_BLOCK_SUBNETS=false
            ;;
        4)
            show_custom_menu
            ;;
        0)
            log_info "用户选择退出"
            exit 0
            ;;
        *)
            log_error "无效选项，使用默认配置（全部执行）"
            ;;
    esac
}

show_custom_menu() {
    echo ""
    echo -e "${CYAN}可选执行模块：${NC}"
    echo -e "  ${GREEN} 1${NC}) 系统更新"
    echo -e "  ${GREEN} 2${NC}) 基础工具安装 (btop, net-tools, curl, wget, mtr, traceroute)"
    echo -e "  ${GREEN} 3${NC}) 自定义工具安装 (bandwhich, tcping)"
    echo -e "  ${GREEN} 4${NC}) rsyslog 配置"
    echo -e "  ${GREEN} 5${NC}) 防火墙配置 (iptables IPv4)"
    echo -e "  ${GREEN} 6${NC}) IPv6 防火墙配置"
    echo -e "  ${GREEN} 7${NC}) fail2ban 安装与配置"
    echo -e "  ${GREEN} 8${NC}) 外部脚本执行"
    echo -e "  ${GREEN} 9${NC}) 内核参数优化 (sysctl + BBR)"
    echo -e "  ${GREEN}10${NC}) Swap 配置"
    echo -e "  ${GREEN}11${NC}) 时区 / NTP 配置"
    echo -e "  ${GREEN}12${NC}) 恶意网段封禁"
    echo ""
    echo -e "${YELLOW}请输入要执行的模块编号（空格分隔，直接回车 = 全部执行）：${NC}"
    read -r selected_modules

    if [[ -z "$selected_modules" ]]; then
        log_info "未选择，全部执行"
        return
    fi

    # 先全部关闭，再按选择打开
    MOD_UPDATE=false
    MOD_BASIC_TOOLS=false
    MOD_CUSTOM_TOOLS=false
    MOD_RSYSLOG=false
    MOD_FIREWALL=false
    MOD_IPV6_FIREWALL=false
    MOD_FAIL2BAN=false
    MOD_EXTERNAL_SCRIPT=false
    MOD_SYSCTL=false
    MOD_SWAP=false
    MOD_TIMEZONE=false
    MOD_BLOCK_SUBNETS=false

    for num in $selected_modules; do
        case $num in
            1)  MOD_UPDATE=true ;;
            2)  MOD_BASIC_TOOLS=true ;;
            3)  MOD_CUSTOM_TOOLS=true ;;
            4)  MOD_RSYSLOG=true ;;
            5)  MOD_FIREWALL=true ;;
            6)  MOD_IPV6_FIREWALL=true ;;
            7)  MOD_FAIL2BAN=true ;;
            8)  MOD_EXTERNAL_SCRIPT=true ;;
            9)  MOD_SYSCTL=true ;;
            10) MOD_SWAP=true ;;
            11) MOD_TIMEZONE=true ;;
            12) MOD_BLOCK_SUBNETS=true ;;
            *)  log_warn "无效编号: $num，跳过" ;;
        esac
    done

    # 显示最终选择
    echo ""
    log_info "已选择的模块："
    [[ "$MOD_UPDATE" == true ]] && log_info "  ✓ 系统更新"
    [[ "$MOD_BASIC_TOOLS" == true ]] && log_info "  ✓ 基础工具安装"
    [[ "$MOD_CUSTOM_TOOLS" == true ]] && log_info "  ✓ 自定义工具安装"
    [[ "$MOD_RSYSLOG" == true ]] && log_info "  ✓ rsyslog 配置"
    [[ "$MOD_FIREWALL" == true ]] && log_info "  ✓ 防火墙配置"
    [[ "$MOD_IPV6_FIREWALL" == true ]] && log_info "  ✓ IPv6 防火墙"
    [[ "$MOD_FAIL2BAN" == true ]] && log_info "  ✓ fail2ban"
    [[ "$MOD_EXTERNAL_SCRIPT" == true ]] && log_info "  ✓ 外部脚本"
    [[ "$MOD_SYSCTL" == true ]] && log_info "  ✓ 内核参数优化"
    [[ "$MOD_SWAP" == true ]] && log_info "  ✓ Swap 配置"
    [[ "$MOD_TIMEZONE" == true ]] && log_info "  ✓ 时区 / NTP"
    [[ "$MOD_BLOCK_SUBNETS" == true ]] && log_info "  ✓ 恶意网段封禁"
}

# ═══════════════════════════════════════════════════════════════
# [E4] 卸载/还原
# ═══════════════════════════════════════════════════════════════
do_uninstall() {
    echo -e "${RED}═══════════════════════════════════════════════════════════${NC}"
    echo -e "${RED}${BOLD}  卸载/还原 inition.sh 配置${NC}"
    echo -e "${RED}═══════════════════════════════════════════════════════════${NC}"

    check_root
    detect_os

    echo ""
    echo -e "${YELLOW}此操作将：${NC}"
    echo "  1. 清空所有 iptables / ip6tables 规则，设策略为 ACCEPT"
    echo "  2. 停止并禁用 fail2ban，解封所有 IP"
    echo "  3. 删除 fail2ban 自定义配置 (jail.local)"
    echo "  4. 删除内核参数优化文件 (/etc/sysctl.d/99-inition.conf)"
    echo "  5. 删除自定义工具 (${CUSTOM_TOOL_NAMES[*]})"
    echo "  6.（可选）删除 swap 文件"
    echo ""
    echo -e "${RED}${BOLD}是否继续？(y/N)${NC}"
    read -r confirm

    if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
        echo "已取消"
        exit 0
    fi

    echo ""

    # 1. 清空 iptables
    log_info "清空 IPv4 iptables 规则..."
    iptables -F 2>/dev/null || true
    iptables -X 2>/dev/null || true
    iptables -t nat -F 2>/dev/null || true
    iptables -t nat -X 2>/dev/null || true
    iptables -t mangle -F 2>/dev/null || true
    iptables -t mangle -X 2>/dev/null || true
    iptables -P INPUT ACCEPT
    iptables -P FORWARD ACCEPT
    iptables -P OUTPUT ACCEPT

    if command -v ip6tables &>/dev/null; then
        log_info "清空 IPv6 ip6tables 规则..."
        ip6tables -F 2>/dev/null || true
        ip6tables -X 2>/dev/null || true
        ip6tables -P INPUT ACCEPT
        ip6tables -P FORWARD ACCEPT
        ip6tables -P OUTPUT ACCEPT
    fi

    # 保存空规则
    save_iptables
    log_info "✓ 防火墙规则已清空"

    # 2. 停止 fail2ban
    if command -v fail2ban-client &>/dev/null; then
        log_info "停止 fail2ban 并解封所有 IP..."
        fail2ban-client unban --all 2>/dev/null || true
        systemctl stop fail2ban 2>/dev/null || true
        systemctl disable fail2ban 2>/dev/null || true
        log_info "✓ fail2ban 已停止"
    fi

    # 3. 删除 fail2ban 配置
    if [[ -f /etc/fail2ban/jail.local ]]; then
        rm -f /etc/fail2ban/jail.local
        log_info "✓ fail2ban 配置已删除"
    fi

    # 4. 删除 sysctl 配置
    if [[ -f /etc/sysctl.d/99-inition.conf ]]; then
        rm -f /etc/sysctl.d/99-inition.conf
        sysctl --system 2>/dev/null || true
        log_info "✓ 内核参数优化配置已删除并还原"
    fi

    # 5. 删除自定义工具
    for tool_name in "${CUSTOM_TOOL_NAMES[@]}"; do
        if [[ -f "${CUSTOM_TOOLS_DIR}/${tool_name}" ]]; then
            rm -f "${CUSTOM_TOOLS_DIR}/${tool_name}"
            log_info "✓ 已删除 ${tool_name}"
        fi
    done

    # 6. 可选：删除 swap
    if [[ -f /swapfile ]]; then
        echo ""
        echo -e "${YELLOW}是否删除 swap 文件 (/swapfile)？(y/N)${NC}"
        read -r rm_swap
        if [[ "$rm_swap" =~ ^[Yy]$ ]]; then
            swapoff /swapfile 2>/dev/null || true
            rm -f /swapfile
            sed -i '/\/swapfile/d' /etc/fstab 2>/dev/null || true
            log_info "✓ swap 文件已删除"
        fi
    fi

    # 清理日志
    rm -f "$LOG_FILE" 2>/dev/null || true

    echo ""
    echo -e "${GREEN}${BOLD}═══════════════════════════════════════════════════════════${NC}"
    echo -e "${GREEN}${BOLD}  卸载/还原完成${NC}"
    echo -e "${GREEN}${BOLD}═══════════════════════════════════════════════════════════${NC}"
    echo -e "${YELLOW}提示: 基础工具 (btop, mtr, traceroute 等) 和 chrony 未卸载${NC}"
    echo -e "${YELLOW}如需卸载请手动使用 apt/yum remove${NC}"
}

# ═══════════════════════════════════════════════════════════════
# 计算需要执行的步骤数 [E1]
# ═══════════════════════════════════════════════════════════════
count_steps() {
    local count=0
    [[ "$MOD_UPDATE" == true ]] && ((count++))
    [[ "$MOD_BASIC_TOOLS" == true ]] && ((count++))
    [[ "$MOD_CUSTOM_TOOLS" == true ]] && ((count++))
    [[ "$MOD_RSYSLOG" == true ]] && ((count++))
    if [[ "$MOD_FIREWALL" == true ]]; then
        ((count += 4))  # clean + disable + install + configure
    fi
    [[ "$MOD_IPV6_FIREWALL" == true ]] && ((count++))
    if [[ "$MOD_FAIL2BAN" == true ]]; then
        ((count += 2))  # clean + install
    fi
    [[ "$MOD_EXTERNAL_SCRIPT" == true ]] && ((count++))
    # SSH port update + fail2ban configure (if firewall or fail2ban enabled)
    if [[ "$MOD_FIREWALL" == true ]] || [[ "$MOD_FAIL2BAN" == true ]]; then
        ((count++))  # update ssh port
    fi
    [[ "$MOD_FAIL2BAN" == true ]] && ((count++))  # configure fail2ban
    [[ "$MOD_SYSCTL" == true ]] && ((count++))
    [[ "$MOD_SWAP" == true ]] && ((count++))
    [[ "$MOD_TIMEZONE" == true ]] && ((count++))
    [[ "$MOD_BLOCK_SUBNETS" == true ]] && ((count++))
    ((count++))  # show_status
    TOTAL_STEPS=$count
}

# ═══════════════════════════════════════════════════════════════
# [S7] 配置日志轮转，防止 /var/log/inition.log 无限增长
# ═══════════════════════════════════════════════════════════════
setup_logrotate() {
    log_step "配置日志轮转规则..."

    if [[ "$DRY_RUN" == true ]]; then
        log_dry "  写入 /etc/logrotate.d/inition"
        return 0
    fi

    cat > /etc/logrotate.d/inition << 'LOGROTATE_EOF'
/var/log/inition.log {
    weekly
    rotate 8
    compress
    delaycompress
    missingok
    notifempty
    create 640 root adm
}
LOGROTATE_EOF

    chmod 644 /etc/logrotate.d/inition
    log_info "✓ 日志轮转已配置: 每周轮转，保留8份，自动压缩"
}
# ═══════════════════════════════════════════════════════════════
# 主函数
# ═══════════════════════════════════════════════════════════════
main() {
    echo -e "${GREEN}${BOLD}═══════════════════════════════════════════════════════════${NC}"
    echo -e "${GREEN}${BOLD}   Linux系统安全配置脚本 - Docker兼容版 v2.0${NC}"
    echo -e "${GREEN}${BOLD}   支持: 动态SSH检测 | IPv6 | BBR | 交互菜单 | 回滚${NC}"
    echo -e "${GREEN}${BOLD}═══════════════════════════════════════════════════════════${NC}"

    if [[ "$DRY_RUN" == true ]]; then
        echo -e "${MAGENTA}${BOLD}  ⚠ DRY-RUN 模式：所有修改操作将被模拟，不会实际执行${NC}"
        echo ""
    fi

    check_root
    detect_os

    # [Q3] 非交互式 apt 安装，防止 debconf 弹出界面导致脚本挂起
    if [[ "$PACKAGE_MANAGER" == "apt" ]]; then
        export DEBIAN_FRONTEND=noninteractive
    fi

    # [C2] 初始化日志文件
    mkdir -p "$(dirname "$LOG_FILE")" 2>/dev/null || true
    echo "═══ inition.sh v2.0 执行日志 - $(date) ═══" >> "$LOG_FILE" 2>/dev/null || true
    log_to_file "OS: $OS | DRY_RUN: $DRY_RUN | INTERACTIVE: $INTERACTIVE_MODE"

    # [E2] 交互式菜单
    if [[ "$INTERACTIVE_MODE" == true ]]; then
        show_main_menu
    fi

    # [C4] 网络连通性预检（如果需要下载）
    if [[ "$MOD_UPDATE" == true ]] || [[ "$MOD_BASIC_TOOLS" == true ]] || \
       [[ "$MOD_CUSTOM_TOOLS" == true ]] || [[ "$MOD_EXTERNAL_SCRIPT" == true ]]; then
        if ! check_network; then
            echo -e "${YELLOW}网络不可用，是否继续？(y/N)${NC}"
            read -r net_continue
            if [[ ! "$net_continue" =~ ^[Yy]$ ]]; then
                exit 1
            fi
        fi
    fi

    # [E1] 计算步骤数并初始化进度
    count_steps
    init_progress "$TOTAL_STEPS"
    log_info "共 ${TOTAL_STEPS} 个步骤需要执行"

    # ────────────────────────────────────────────────────────
    # 第一阶段：基础配置
    # ────────────────────────────────────────────────────────

    if [[ "$MOD_UPDATE" == true ]]; then
        advance_step "系统更新"
        update_system
    fi

    if [[ "$MOD_BASIC_TOOLS" == true ]]; then
        advance_step "安装基础工具"
        install_basic_tools
    fi

    if [[ "$MOD_CUSTOM_TOOLS" == true ]]; then
        advance_step "安装自定义工具"
        install_custom_tools
    fi

    if [[ "$MOD_RSYSLOG" == true ]]; then
        advance_step "配置 rsyslog"
        install_and_configure_rsyslog
    fi

    if [[ "$MOD_FIREWALL" == true ]]; then
        advance_step "清理已有防火墙配置"
        clean_existing_iptables

        advance_step "关闭系统自带防火墙"
        disable_system_firewalls

        advance_step "安装 iptables-legacy"
        install_iptables_legacy

        advance_step "配置 IPv4 防火墙规则"
        # [C1] 使用 trap 确保失败时回滚
        if ! configure_basic_iptables; then
            log_error "iptables 配置失败，执行回滚..."
            rollback_iptables
        fi
        add_custom_ports
        save_iptables
    fi

    if [[ "$MOD_IPV6_FIREWALL" == true ]]; then
        advance_step "配置 IPv6 防火墙规则"
        configure_ipv6_firewall
        save_iptables
    fi

    if [[ "$MOD_FAIL2BAN" == true ]]; then
        advance_step "清理已有 fail2ban 配置"
        clean_existing_fail2ban

        advance_step "安装 fail2ban"
        install_fail2ban
    fi

    # ────────────────────────────────────────────────────────
    # 第二阶段：外部脚本
    # ────────────────────────────────────────────────────────

    if [[ "$MOD_EXTERNAL_SCRIPT" == true ]]; then
        advance_step "运行外部脚本"
        if ! run_external_script; then
            log_warn "外部脚本运行失败，继续使用默认配置"
        fi
    fi

    # ────────────────────────────────────────────────────────
    # 第三阶段：SSH端口更新与fail2ban配置
    # ────────────────────────────────────────────────────────

    if [[ "$MOD_FIREWALL" == true ]] || [[ "$MOD_FAIL2BAN" == true ]]; then
        advance_step "检测SSH端口并更新规则"
        update_ssh_port_iptables
    fi

    if [[ "$MOD_FAIL2BAN" == true ]]; then
        advance_step "配置 fail2ban 安全策略"
        configure_fail2ban_after_external
    fi

    # ────────────────────────────────────────────────────────
    # 第四阶段：系统优化
    # ────────────────────────────────────────────────────────

    if [[ "$MOD_SYSCTL" == true ]]; then
        advance_step "优化内核参数 (sysctl + BBR)"
        configure_sysctl
    fi

    if [[ "$MOD_SWAP" == true ]]; then
        advance_step "配置 Swap"
        configure_swap
    fi

    if [[ "$MOD_TIMEZONE" == true ]]; then
        advance_step "配置时区 / NTP"
        configure_timezone_ntp
    fi

    # ────────────────────────────────────────────────────────
    # 第五阶段：恶意网段封禁（需确认）
    # ────────────────────────────────────────────────────────

    if [[ "$MOD_BLOCK_SUBNETS" != true ]] && [[ "$MOD_FAIL2BAN" == true ]] && [[ "$INTERACTIVE_MODE" == true ]]; then
        echo ""
        echo -e "${YELLOW}是否分析SSH登录失败日志并封禁恶意网段？(y/N)${NC}"
        read -p "此功能将封禁超过${BAD_SUBNET_THRESHOLD}次失败登录的/24网段: " block_choice
        if [[ "$block_choice" =~ ^[Yy]$ ]]; then
            MOD_BLOCK_SUBNETS=true
        fi
    fi

    if [[ "$MOD_BLOCK_SUBNETS" == true ]]; then
        advance_step "分析并封禁恶意网段"
        block_bad_subnets
    fi

    # ────────────────────────────────────────────────────────
    # 完成：显示状态报告
    # ────────────────────────────────────────────────────────

    # [S7] 配置日志轮转
    setup_logrotate

    advance_step "生成配置报告"
    show_status

    # [C1] 清理备份文件（成功完成后）
    cleanup_backup

    echo ""
    echo -e "${GREEN}${BOLD}═══════════════════════════════════════════════════════════${NC}"
    echo -e "${GREEN}${BOLD}  ✅ 所有配置已完成！${NC}"
    echo -e "${GREEN}${BOLD}═══════════════════════════════════════════════════════════${NC}"

    log_info "系统安全配置完成"

    echo -e "\n${YELLOW}${BOLD}重要提示:${NC}"
    echo -e "  • 最终 SSH 端口: ${GREEN}${SSH_PORT}${NC}"
    echo -e "  • 执行日志: ${DIM}${LOG_FILE}${NC}"
    echo -e "  • 建议重启系统确保所有配置生效"
    echo -e "  • 卸载命令: ${DIM}bash inition.sh --uninstall${NC}"
}

# ═══════════════════════════════════════════════════════════════
# 入口
# ═══════════════════════════════════════════════════════════════
parse_args "$@"

if [[ "$UNINSTALL_MODE" == true ]]; then
    do_uninstall
    exit 0
fi

main



