随机
Enter 搜索 ↑↓ 切换 Esc 清空

fail2ban_menu

脚本

Fail2Ban 配置与管理工具,支持规则查看、编辑、测试与服务控制。

fail2ban_menu

Fail2Ban 配置与管理工具,支持规则查看、编辑、测试与服务控制。

一键脚本

bash <(curl -sL gitee.com/meimolihan/linux-command_sh/raw/master/fail2ban_menu.sh)

效果预览

执行脚本效果预览

脚本源码

#!/bin/bash
set -uo pipefail

list_color_init() {
    export gl_hui=$'\033[38;5;59m'
    export gl_hong=$'\033[38;5;9m'
    export gl_lv=$'\033[38;5;10m'
    export gl_huang=$'\033[38;5;11m'
    export gl_lan=$'\033[38;5;32m'
    export gl_bai=$'\033[38;5;15m'
    export gl_zi=$'\033[38;5;13m'
    export gl_bufan=$'\033[38;5;14m'
    export reset=$'\033[0m'
}
list_color_init

log_info()  { echo -e "${gl_lan}[信息]${gl_bai} $*"; }
log_ok()    { echo -e "${gl_lv}[成功]${gl_bai} $*"; }
log_warn()  { echo -e "${gl_huang}[警告]${gl_bai} $*"; }
log_error() { echo -e "${gl_hong}[错误]${gl_bai} $*" >&2; }

break_end() {
    echo -e "${gl_lv}操作完成${gl_bai}"
    echo -e "${gl_bai}按任意键继续${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai} \c"
    read -r -n 1 -s
    echo ""
    clear
}

sleep_fractional() {
    local seconds=$1
    if sleep "$seconds" 2>/dev/null; then return 0; fi
    if command -v perl >/dev/null 2>&1; then perl -e "select(undef, undef, undef, $seconds)"; return 0; fi
    if command -v python3 >/dev/null 2>&1; then python3 -c "import time; time.sleep($seconds)"; return 0; fi
    if command -v python >/dev/null 2>&1; then python -c "import time; time.sleep($seconds)"; return 0; fi
    local int_seconds=$(awk -v s="$seconds" 'BEGIN{print int(s+0.999)}')
    sleep "$int_seconds"
}

exit_animation() {
    echo -ne "\r${gl_lv}即将退出 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.5
    echo -ne "${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.6
    echo ""
}

cancel_empty() {
    local menu_name="${1:-上一级选单}"
    echo -e "${gl_hong}空输入,返回 ${gl_huang}${menu_name}${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.5
    echo -ne "${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.6
    echo ""
    clear
}

cancel_return() {
    local menu_name="${1:-上一级选单}"
    echo -ne "${gl_lv}即将返回 ${gl_huang}${menu_name}${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.5
    echo -ne "${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.6
    echo ""
    clear
}

column_if_available() {
    if command -v column &> /dev/null; then
        column -t -s $'\t'
    else
        cat
    fi
}

list_beautify_docker_system() {
    {
        docker system df | awk -v gray="$gl_hui" -v green="$gl_lv" -v yellow="$gl_huang" \
            -v blue="$gl_lan" -v cyan="$gl_bufan" -v reset="$reset" '
        BEGIN {
            print gray "类型\t总数\t活跃\t大小\t可回收" reset
            print gray "----------\t--------\t--------\t----------\t----------" reset
        }
        NR > 1 {
            type = $1
            total = $2
            active = $3
            size = $4
            reclaim = $5
            if (type == "Local") {
                type = $1 " " $2
                total = $3
                active = $4
                size = $5
                reclaim = $6
            }
            if (type == "Images") color = green
            else if (type == "Containers") color = yellow
            else if (type == "Local Volumes") color = blue
            else if (type == "Build Cache") color = cyan
            else color = reset
            print color type "\t" total "\t" active "\t" size "\t" reclaim reset
        }' | column_if_available
    }
}

handle_invalid_input() {
    echo -ne "\r${gl_hong}无效的输入,请重新输入 ${gl_zi} 2 ${gl_hong}秒后返回 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.3
    echo -ne "\r${gl_huang}无效的输入,请重新输入 ${gl_zi} 1 ${gl_huang}秒后返回 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.3
    echo -e "\r${gl_lv}无效的输入,请重新输入 ${gl_zi} 0 ${gl_lv}秒后返回 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.6
    return 2
}

exit_script() {
    echo ""
    echo -ne "${gl_hong}感谢使用,再见!${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.5
    echo -ne "${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.6
    clear
    exit 0
}

install() {
    [[ $# -eq 0 ]] && {
        log_error "未提供软件包参数!"
        return 1
    }

    local pkg mgr ver cmd_ver pkg_ver installed=false
    for pkg in "$@"; do
        installed=false
        ver=""
        
        if command -v "$pkg" &>/dev/null; then
            cmd_ver=$("$pkg" --version 2>/dev/null | head -n1 | tr -cd '[:print:]' | grep -oE '[0-9]+(\.[0-9]+)+' | head -n1 || echo "")
            [[ -n "$cmd_ver" ]] && ver="$cmd_ver"
            installed=true
        fi

        if [[ "$pkg" == "7zip" || "$pkg" == "7z" ]]; then
            if command -v 7z &>/dev/null; then
                ver=$(7z 2>&1 | grep -oE '[0-9]+(\.[0-9]+)+' | head -n1 || echo "")
                [[ -n "$ver" ]] && installed=true
            fi
        fi

        if [[ "$installed" == false ]]; then
            if command -v opkg &>/dev/null; then
                if opkg list-installed | grep -q "^${pkg} "; then
                    installed=true
                    ver=$(opkg list-installed | grep "^${pkg} " | awk '{print $3}' 2>/dev/null || echo "")
                fi
            elif command -v dpkg-query &>/dev/null; then
                if dpkg-query -W -f='${Status}' "$pkg" 2>/dev/null | grep -q "install ok installed"; then
                    installed=true
                    ver=$(dpkg-query -W -f='${Version}' "$pkg" 2>/dev/null || echo "")
                fi
            elif command -v rpm &>/dev/null; then
                if rpm -q "$pkg" &>/dev/null; then
                    installed=true
                    ver=$(rpm -q --qf '%{VERSION}' "$pkg" 2>/dev/null || echo "")
                fi
            elif command -v apk &>/dev/null; then
                if apk info "$pkg" 2>/dev/null | grep -q "^installed"; then
                    installed=true
                    ver=$(apk info -a "$pkg" 2>/dev/null | grep -oE '[0-9]+(\.[0-9]+)+' | head -n1 || echo "")
                fi
            elif command -v pacman &>/dev/null; then
                if pacman -Qi "$pkg" &>/dev/null; then
                    installed=true
                    ver=$(pacman -Qi "$pkg" 2>/dev/null | grep -i "version" | grep -oE '[0-9]+(\.[0-9]+)+' | head -n1 || echo "")
                fi
            fi
        fi
        
        if [[ "$installed" == true ]]; then
            echo -e "${gl_huang}${pkg}${gl_bai} ${gl_lv}已安装${gl_bai}" \
                "$([[ -n "$ver" ]] && echo "版本 ${gl_lv}${ver}${gl_bai}")"
            continue
        fi
        
        echo -e ""
        echo -e "${gl_huang}开始安装:${gl_bai}${pkg}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        
        local install_success=false
        
        for mgr in opkg dnf yum apt apk pacman zypper pkg; do
            if ! command -v "$mgr" &>/dev/null; then
                continue
            fi
            
            case $mgr in
            opkg)
                echo -e "${gl_bai}使用包管理器: ${gl_zi}opkg (OpenWrt/iStoreOS)${gl_bai}"
                if [[ "$pkg" == "7zip" || "$pkg" == "7z" ]]; then
                    echo -e "${gl_bai}正在安装: ${gl_lv}p7zip${gl_bai}"
                    opkg update && opkg install p7zip && install_success=true
                else
                    opkg update && opkg install "$pkg" && install_success=true
                fi
                ;;
            dnf)
                echo -e "${gl_bai}使用包管理器: ${gl_zi}dnf (Fedora/RHEL)${gl_bai}"
                dnf -y update && dnf install -y "$pkg" && install_success=true
                ;;
            yum)
                echo -e "${gl_bai}使用包管理器: ${gl_zi}yum (CentOS/RHEL)${gl_bai}"
                yum -y update && yum install -y "$pkg" && install_success=true
                ;;
            apt)
                echo -e "${gl_bai}使用包管理器: ${gl_zi}apt (Debian/Ubuntu)${gl_bai}"
                apt update -y && apt install -y "$pkg" && install_success=true
                ;;
            apk)
                echo -e "${gl_bai}使用包管理器: ${gl_zi}apk (Alpine)${gl_bai}"
                apk update && apk add "$pkg" && install_success=true
                ;;
            pacman)
                echo -e "${gl_bai}使用包管理器: ${gl_zi}pacman (Arch/Manjaro)${gl_bai}"
                pacman -Syu --noconfirm && pacman -S --noconfirm "$pkg" && install_success=true
                ;;
            zypper)
                echo -e "${gl_bai}使用包管理器: ${gl_zi}zypper (openSUSE)${gl_bai}"
                zypper refresh && zypper install -y "$pkg" && install_success=true
                ;;
            pkg)
                echo -e "${gl_bai}使用包管理器: ${gl_zi}pkg (FreeBSD)${gl_bai}"
                pkg update && pkg install -y "$pkg" && install_success=true
                ;;
            esac
            
            [[ "$install_success" == true ]] && break
        done
        
        if [[ "$install_success" == true ]]; then
            echo -e "${gl_lv}${pkg} 安装成功${gl_bai}"
        else
            echo -e "${gl_hong}${pkg} 安装失败${gl_bai}"
        fi
        
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    done
}

column_if_available() {
    if command -v column &> /dev/null; then
        column -t -s $'\t'
    else
        cat
    fi
}

list_beautify_iptables_all() {
    {
        if ! iptables -L -n --line-numbers &>/dev/null; then
            echo -e "${gl_hong}错误: 需要 root 权限运行 iptables${reset}"
            return 1
        fi
        
        output=$(iptables -L -n --line-numbers 2>/dev/null)
        if [ -z "$output" ]; then
            echo -e "${gl_huang}iptables 规则为空${reset}"
            return 0
        fi
        
        echo "$output" | awk -v green="$gl_lv" -v red="$gl_hong" -v yellow="$gl_huang" \
            -v cyan="$gl_bufan" -v blue="$gl_lan" -v reset="$reset" '
        /^Chain / {
            chain = $2
            policy = ""
            for (i=4; i<=NF; i++) {
                if ($i == "(policy") {
                    policy = $(i+1)
                    gsub(/[()]/, "", policy)
                    break
                }
            }
            if (policy == "") {
                for (i=4; i<=NF; i++) {
                    if ($i == "references" || $i ~ /^[0-9]+$/) {
                        policy = "自定义"
                        break
                    }
                }
            }
            if (chain == "INPUT") {
                chain_display = "入站"
                chain_color = blue
            } else if (chain == "OUTPUT") {
                chain_display = "出站"
                chain_color = blue
            } else if (chain == "FORWARD") {
                chain_display = "转发"
                chain_color = blue
            } else if (chain ~ /^f2b-/) {
                chain_display = "⛓" chain
                chain_color = red
            } else if (chain ~ /^DOCKER/) {
                chain_display = "🐳" chain
                chain_color = cyan
            } else {
                chain_display = "🔗" chain
                chain_color = blue
            }
            if (policy == "ACCEPT") {
                policy_display = "允许"
                policy_color = green
            } else if (policy == "DROP") {
                policy_display = "丢弃"
                policy_color = red
            } else if (policy == "REJECT") {
                policy_display = "拒绝"
                policy_color = red
            } else if (policy == "1" || policy == "references" || policy == "自定义") {
                policy_display = "自定义"
                policy_color = yellow
            } else {
                policy_display = policy
                policy_color = yellow
            }
            printf "%s%s%s  策略: %s%s%s\n\n", chain_color, chain_display, reset, policy_color, policy_display, reset
        }
        
        /^[0-9]/ && NF >= 6 {
            num = $1
            target = $2
            source = $5
            dest = $6
            
            ports = ""
            for (i=7; i<=NF; i++) {
                if ($i ~ /dpt:[0-9]+/) {
                    match($i, /dpt:[0-9]+/)
                    ports = substr($i, RSTART+4, RLENGTH-4)
                    break
                } else if ($i ~ /multiport/) {
                    ports = "多端口"
                    break
                }
            }
            
            if (source == "0.0.0.0/0" && dest == "0.0.0.0/0" && ports == "" && target == "ACCEPT") {
                next
            }
            
            if (target == "ACCEPT") {
                target_display = "✓允许"
                target_color = green
            } else if (target == "DROP") {
                target_display = "✗丢弃"
                target_color = red
            } else if (target == "REJECT") {
                target_display = "✗拒绝"
                target_color = red
            } else if (target == "LOG") {
                target_display = "📋日志"
                target_color = cyan
            } else if (target == "RETURN") {
                target_display = "↩返回"
                target_color = yellow
            } else if (target ~ /DNAT|SNAT|MASQUERADE/) {
                target_display = "↔NAT"
                target_color = cyan
            } else if (target ~ /^f2b-/) {
                target_display = "⛓封禁"
                target_color = red
            } else if (target ~ /^DOCKER-/) {
                target_display = "🐳Docker"
                target_color = cyan
            } else {
                target_display = "•" target
                target_color = yellow
            }
            
            if (source == "0.0.0.0/0") source = ""
            if (dest == "0.0.0.0/0") dest = ""
            if (source != "" && dest != "") {
                addr = source "→" dest
            } else if (source != "") {
                addr = source
            } else if (dest != "") {
                addr = "→" dest
            } else {
                addr = ""
            }
            
            if (ports != "") {
                ports_display = "端口:" ports
            } else {
                ports_display = ""
            }
            
            printf "  %s%2s%s\t", yellow, num, reset
            printf "%s%-8s%s\t", target_color, target_display, reset
            printf "%s%s%s\t", cyan, addr, reset
            if (ports_display != "") {
                printf "%s%s%s", yellow, ports_display, reset
            } else {
                printf "%s", reset
            }
            printf "\n"
        }'
        
    } | column_if_available
}

list_beautify_iptables_input() {
    local chain="${1:-INPUT}"
    if ! iptables -L "$chain" -n --line-numbers &>/dev/null; then
        echo -e "\033[1;31m错误: 需要 root 权限或链 $chain 不存在\033[0m" >&2
        return 1
    fi
    local policy_line policy policy_display policy_color
    policy_line=$(iptables -L "$chain" -n 2>/dev/null | head -n1)
    if [[ "$policy_line" =~ \(policy\ ([A-Z]+)\) ]]; then
        policy="${BASH_REMATCH[1]}"
    else
        policy="自定义"
    fi
    case "$policy" in
        ACCEPT) policy_display="允许"; policy_color="\033[1;32m" ;;
        DROP)   policy_display="丢弃"; policy_color="\033[1;31m" ;;
        REJECT) policy_display="拒绝"; policy_color="\033[1;31m" ;;
        自定义) policy_display="自定义"; policy_color="\033[1;33m" ;;
        *)      policy_display="$policy"; policy_color="\033[1;33m" ;;
    esac
    echo -e "\033[1;34m链: $chain\033[0m  策略: ${policy_color}${policy_display}\033[0m\n"

    iptables -L "$chain" -n --line-numbers 2>/dev/null | awk -v green="\033[1;32m" -v red="\033[1;31m" -v yellow="\033[1;33m" -v cyan="\033[1;36m" -v blue="\033[1;34m" -v reset="\033[0m" '
    /^[0-9]/ {
        num = $1; target = $2; prot = $3; source = $5; dest = $6
        port_info = ""
        for (i=7; i<=NF; i++) {
            if ($i ~ /^dpt:/) { port_info = $i; break }
            else if ($i ~ /^spt:/) { port_info = $i; break }
            else if ($i ~ /^multiport/) { port_info = $i; break }
        }
        if (port_info ~ /^dpt:/) port = "→" substr(port_info,4)
        else if (port_info ~ /^spt:/) port = "←" substr(port_info,4)
        else if (port_info ~ /^multiport/) port = "多端口"
        else port = ""

        if (target == "ACCEPT") { t = "允许"; c = green }
        else if (target == "DROP") { t = "丢弃"; c = red }
        else if (target == "REJECT") { t = "拒绝"; c = red }
        else if (target ~ /^f2b-/) { t = "⛓封禁(" substr(target,5) ")"; c = red }
        else { t = target; c = yellow }

        if (prot == "tcp") proto = "TCP"
        else if (prot == "udp") proto = "UDP"
        else if (prot == "all") proto = "全部"
        else proto = prot

        s = (source == "0.0.0.0/0") ? "任意" : source
        d = (dest == "0.0.0.0/0") ? "任意" : dest

        printf "%s%2d%s|%s%-14s%s|%s%-6s%s|%s%-18s%s|%s%-18s%s|%s%-10s%s\n",
            yellow, num, reset,
            c, t, reset,
            cyan, proto, reset,
            blue, s, reset,
            blue, d, reset,
            yellow, port, reset
    }' | sed 's/|/\t/g' | column_if_available
}

f2b_status() {
    fail2ban-client reload
    sleep_fractional 3
    fail2ban-client status
}

check_f2b_status() {
    if command -v fail2ban-client >/dev/null 2>&1; then
        check_f2b_status="${gl_lv}已安装${gl_bai}"
    else
        check_f2b_status="${gl_hui}未安装${gl_bai}"
    fi
}

f2b_install_sshd() {
    docker rm -f fail2ban >/dev/null 2>&1
    install fail2ban
    systemctl start fail2ban
    enable fail2ban

    if command -v dnf &>/dev/null; then
        cd /etc/fail2ban/jail.d/
        curl -sS -O https://gitee.com/meimolihan/sh/raw/master/f2b/centos-ssh.conf
    fi
}

f2b_sshd() {
    local jail
    if grep -qi 'Alpine' /etc/issue 2>/dev/null; then
        jail=alpine-sshd
    else
        jail=sshd
    fi
    fail2ban-client status "$jail"
}

f2b_autostart() {
    status=$(systemctl is-enabled fail2ban 2>/dev/null || echo "unknown")

    if [[ $status == "enabled" ]]; then
        echo -e "\n[${gl_lv}${gl_bai}] 无需重复操作,${gl_huang}fail2ban ${gl_lv}已是开机自启状态。${gl_bai}"
    else
        echo -e "\n${gl_bai}正在设置 ${gl_huang}fail2ban${gl_bai} 开机自启,请稍候${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
        if systemctl enable fail2ban >/dev/null 2>&1; then
            echo -e "\n[${gl_lv}${gl_bai}] 设置成功!fail2ban 已设为开机自启。"
        else
            echo -e "\n[${gl_hong}${gl_bai}] 设置失败,请检查是否安装 fail2ban 或以 root 身份运行。"
        fi
    fi
}

update_fail2ban() {
    local DISTRO LOG_FILE CURRENT_VERSION NEW_VERSION

    LOG_FILE="/var/log/fail2ban_update.log"

    sudo mkdir -p "$(dirname "$LOG_FILE")"

    echo ""
    echo -e "${gl_huang}>>> 开始更新 fail2ban${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}" | sudo tee -a "$LOG_FILE"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    if [[ -f /etc/os-release ]]; then
        source /etc/os-release
        DISTRO="$ID"
    else
        echo "错误:无法检测系统发行版" | sudo tee -a "$LOG_FILE"
        return 1
    fi

    echo -e "${gl_bai}检测到系统: ${gl_lv}$DISTRO" | sudo tee -a "$LOG_FILE${gl_bai}"

    if ! command -v fail2ban-server &>/dev/null && ! systemctl is-active --quiet fail2ban; then
        echo -e "错误:fail2ban 未安装或未运行" | sudo tee -a "$LOG_FILE"
        return 1
    fi

    CURRENT_VERSION=$(fail2ban-client --version 2>/dev/null || echo "未知")
    echo -e "${gl_bai}当前fail2ban版本: ${gl_lv}$CURRENT_VERSION" | sudo tee -a "$LOG_FILE${gl_bai}"

    backup_fail2ban_config() {
        local BACKUP_DIR="/etc/fail2ban/backup_$(date +%Y%m%d_%H%M%S)"
        echo -e "${gl_bai}备份配置到: ${gl_lv}$BACKUP_DIR" | sudo tee -a "$LOG_FILE${gl_bai}"

        sudo mkdir -p "$BACKUP_DIR"
        sudo cp -r /etc/fail2ban/*.conf /etc/fail2ban/*.local "$BACKUP_DIR/" 2>/dev/null
        sudo cp -r /etc/fail2ban/jail.d/ "$BACKUP_DIR/" 2>/dev/null
    }

    update_via_package_manager() {
        case "$DISTRO" in
        ubuntu | debian)
            echo -e "${gl_bai}使用 ${gl_huang}apt ${gl_bai}更新${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}" | sudo tee -a "$LOG_FILE"
            sudo apt update || return 1
            sudo apt upgrade -y fail2ban || return 1
            ;;
        centos | rhel | fedora | rocky | almalinux)
            if [[ "$DISTRO" == "fedora" ]]; then
                echo -e "${gl_bai}使用 ${gl_huang}dnf${gl_bai} 更新${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}" | sudo tee -a "$LOG_FILE"
                sudo dnf update -y fail2ban || return 1
            else
                echo "${gl_bai}使用 ${gl_huang}yum ${gl_bai}更新${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}" | sudo tee -a "$LOG_FILE"
                if ! yum repolist | grep -q epel; then
                    sudo yum install -y epel-release || return 1
                fi
                sudo yum update -y fail2ban || return 1
            fi
            ;;
        *)
            echo "不支持的发行版: $DISTRO" | sudo tee -a "$LOG_FILE"
            return 1
            ;;
        esac
    }

    echo -e "${gl_bai}停止 fail2ban 服务${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}" | sudo tee -a "$LOG_FILE"
    sudo systemctl stop fail2ban || {
        echo -e "${gl_huang}错误:无法停止 fail2ban 服务${gl_bai}" | sudo tee -a "$LOG_FILE"
        exit_animation
        return 1
    }

    if ! update_via_package_manager; then
        echo -e "${gl_huang}错误:包管理器更新失败${gl_bai}" | sudo tee -a "$LOG_FILE"
        sudo systemctl start fail2ban
        exit_animation
        return 1
    fi

    NEW_VERSION=$(fail2ban-client --version 2>/dev/null || echo "未知")
    echo "更新后 fail2ban 版本: $NEW_VERSION" | sudo tee -a "$LOG_FILE"

    echo ""
    echo -e "${gl_huang}>>> 启动 fail2ban 服务${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}" | sudo tee -a "$LOG_FILE${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    fail2ban-client -t && systemctl restart fail2ban

    sleep_fractional 3
    
    systemctl status fail2ban

    if [[ "$CURRENT_VERSION" != "$NEW_VERSION" ]]; then
        echo -e "${gl_lv}✓ 版本已更新: $CURRENT_VERSION$NEW_VERSION" | sudo tee -a "$LOG_FILE${gl_bai}"
    else
        echo -e "${gl_huang}ℹ 版本未变更,可能已是最新版本" | sudo tee -a "$LOG_FILE${gl_bai}"
    fi

    echo ""
    echo -e "${gl_huang}>>> 更新摘要${gl_bai}" | sudo tee -a "$LOG_FILE${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    echo -e "${gl_bai}系统: ${gl_lv}$DISTRO${gl_bai}" | sudo tee -a "$LOG_FILE"
    echo -e "${gl_bai}旧版本: ${gl_lv}$CURRENT_VERSION${gl_bai}" | sudo tee -a "$LOG_FILE"
    echo -e "${gl_bai}新版本: ${gl_lv}$NEW_VERSION${gl_bai}" | sudo tee -a "$LOG_FILE"
    echo -e "${gl_bai}服务状态: ${gl_lv}$(systemctl is-active fail2ban)${gl_bai}" | sudo tee -a "$LOG_FILE"
    echo -e "${gl_bai}日志文件: ${gl_lv}$LOG_FILE${gl_bai}" | sudo tee -a "$LOG_FILE"

    echo -e "${gl_bai}当前被封禁的 IP:${gl_huang}" | sudo tee -a "$LOG_FILE"
    sudo fail2ban-client status | grep "Jail list" | sudo tee -a "$LOG_FILE${gl_bai}"

    return 0
}

check_directory_empty() {
    local dir="${1:-.}"
    local title="${2:-检查目录}"
    local exit_on_empty="${3:-true}"
    
    if [ ! -d "$dir" ]; then
        echo -e ""
        echo -e "${gl_zi}>>> ${title}${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_hong}错误:目录不存在 ${gl_huang}${dir}${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_lv}即将退出${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
        sleep_fractional 0.8
        echo -ne "${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
        sleep_fractional 1
        return 2
    fi
    
    if [ -z "$(ls -A "$dir" 2>/dev/null)" ]; then
        if [ "$exit_on_empty" != "true" ] && [ "$exit_on_empty" != "1" ]; then
            echo -e "${gl_huang}当前目录为空:${gl_bai}(${gl_lv}$(pwd)${gl_bai})"
            return 0
        fi
        
        echo ""
        echo -e "${gl_zi}>>> ${title}${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_huang}当前目录为空:${gl_bai}(${gl_lv}$(pwd)${gl_bai})"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_lv}即将退出${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
        sleep_fractional 1
        echo -ne "${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
        sleep_fractional 1
        return 1
    fi
    return 0
}

iptables_init() {
    echo -e ""
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    echo -e "${gl_huang}核心功能:${gl_bai}"
    echo -e "${gl_huang}  1. ${gl_bai}备份现有规则(含 Docker 链)"
    echo -e "${gl_huang}  2. ${gl_bai}仅清空 INPUT 链 & 自定义链,保留 Docker 相关链"
    echo -e "${gl_huang}  3. ${gl_bai}设置默认安全策略(DROP INPUT/FORWARD,ACCEPT OUTPUT)"
    echo -e "${gl_huang}  4. ${gl_bai}放行本地回环、已建立连接、关键端口"
    echo -e "${gl_huang}  5. ${gl_bai}自动保存新配置"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    read -r -e -p "$(echo -e "${gl_bai}确定要初始化 ${gl_huang}iptables ${gl_bai}吗? (${gl_lv}y${gl_bai}/${gl_hong}N${gl_bai}): ")"
    [ "$REPLY" = "0" ] && { cancel_return "上一级选单"; return 1; }
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        echo -e "${gl_huang}已取消操作${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai} \c"
        exit_animation
        return
    fi

    clear
    echo -e ""
    echo -e "${gl_zi}>>> 正在初始化iptables(Docker 兼容)${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    mkdir -p /etc/iptables
    local bak_file
    bak_file="/etc/iptables/rules.v4.bak.$(date +%F_%T)"
    if iptables-save >"$bak_file" 2>/dev/null; then
        log_ok "已备份当前规则到 $bak_file"
    else
        log_warn "备份失败,继续初始化"
    fi

    iptables -F INPUT
    log_ok "已清空 INPUT 链"

    local custom_chains
    custom_chains=$(iptables -t filter -S | grep '^-N' | awk '{print $2}' 2>/dev/null || true)

    for chain in $custom_chains; do
        if [[ $chain =~ ^DOCKER ]]; then
            continue
        fi

        if [[ $chain == "INPUT" || $chain == "FORWARD" || $chain == "OUTPUT" ]]; then
            continue
        fi

        if iptables -t filter -L "$chain" &>/dev/null; then
            iptables -F "$chain" 2>/dev/null
            iptables -X "$chain" 2>/dev/null
        fi
    done
    log_ok "已清理自定义链,Docker 链保留"

    iptables -P INPUT DROP
    iptables -P FORWARD DROP
    iptables -P OUTPUT ACCEPT
    log_ok "默认策略已设置"

    iptables -A INPUT -i lo -j ACCEPT
    log_ok "已放行本地回环"

    iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
    log_ok "已放行已建立及关联连接"

    for port in 22 80 443 445 139 666; do
        iptables -A INPUT -p tcp --dport "$port" -j ACCEPT
        log_ok "已放行 TCP 端口 $port"
    done

    if iptables -t filter -L DOCKER-USER &>/dev/null; then
        iptables -A DOCKER-USER -j RETURN
        log_ok "已配置 DOCKER-USER 链"
    fi

    if iptables -t filter -L DOCKER-ISOLATION &>/dev/null; then
        if ! iptables -C FORWARD -j DOCKER-ISOLATION &>/dev/null; then
            iptables -A FORWARD -j DOCKER-ISOLATION
        fi
        log_ok "已配置 DOCKER-ISOLATION 链"
    fi

    if command -v iptables-save &>/dev/null; then
        mkdir -p /etc/iptables
        if iptables-save >/etc/iptables/rules.v4 2>/dev/null; then
            log_ok "规则已保存至 /etc/iptables/rules.v4"
        else
            log_error "保存规则失败"
        fi
    else
        log_error "未找到 iptables-save,请手动保存"
    fi

    if command -v docker &>/dev/null && systemctl is-active --quiet docker 2>/dev/null; then
        echo -e "${gl_huang}正在重新加载 Docker 规则${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
        systemctl restart docker
        sleep_fractional 2
        if systemctl is-active --quiet docker; then
            log_ok "Docker 重启完成"
        else
            log_warn "Docker 重启后未运行"
        fi
    fi

    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

iptables_on() {
    clear
    echo -e "${gl_zi}>>> 开启iptables防火墙${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    log_info "正在安装必要的软件包${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
    if command -v apt-get >/dev/null 2>&1; then
        apt-get update
        apt-get install -y iptables-persistent
        local service_name="netfilter-persistent"
    elif command -v yum >/dev/null 2>&1 || command -v dnf >/dev/null 2>&1; then
        yum install -y iptables-services || dnf install -y iptables-services
        local service_name="iptables"
    else
        log_error "不支持的Linux发行版,无法确定包管理器。"
        return 1
    fi

    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    
    log_info "加载必要的内核模块${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
    modprobe -a iptable_filter iptable_nat iptable_mangle iptable_raw 2>/dev/null

    log_info "正在启动并设置开机启动${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"

    local max_attempts=3
    local attempt=1
    local service_started=0

    while [ $attempt -le $max_attempts ]; do
        if systemctl start "$service_name" 2>/dev/null; then
            systemctl enable "$service_name" 2>/dev/null
            log_ok "iptables 服务启动命令已执行(尝试 ${gl_lv}$attempt${gl_bai}/${gl_huang}$max_attempts${gl_bai})"

            sleep_fractional 2
            if systemctl is-active "$service_name" >/dev/null 2>&1; then
                log_ok "iptables 服务确认已运行"
                service_started=1
                break
            else
                log_warn "服务启动但状态检查未通过,重试${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
            fi
        else
            log_warn "无法通过systemctl启动服务(尝试 ${gl_lv}$attempt${gl_bai}/${gl_huang}$max_attempts${gl_bai})"

            if service "$service_name" start 2>/dev/null; then
                log_ok "通过传统service命令启动成功"
                service_started=1
                break
            fi
        fi

        attempt=$((attempt + 1))
        sleep_fractional 2
    done

    log_info "正在保存当前防火墙规则${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"

    mkdir -p /etc/iptables 2>/dev/null

    if iptables-save >/etc/iptables/rules.v4 2>/dev/null; then
        log_ok "IPv4 防火墙规则已保存"
    else
        echo "# Generated by iptables_on function" >/etc/iptables/rules.v4
        echo "*filter" >>/etc/iptables/rules.v4
        echo ":INPUT ACCEPT [0:0]" >>/etc/iptables/rules.v4
        echo ":FORWARD ACCEPT [0:0]" >>/etc/iptables/rules.v4
        echo ":OUTPUT ACCEPT [0:0]" >>/etc/iptables/rules.v4
        echo "COMMIT" >>/etc/iptables/rules.v4
        log_ok "创建基本防火墙规则文件"
    fi

    if command -v ip6tables-save >/dev/null 2>&1; then
        ip6tables-save >/etc/iptables/rules.v6 2>/dev/null && log_ok "IPv6 防火墙规则已保存"
    fi

    sleep_fractional 2

    log_info "进行最终验证${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"

    local final_check_passed=0

    if systemctl is-active "$service_name" >/dev/null 2>&1; then
        log_ok "✓ 服务状态验证通过"
        final_check_passed=$((final_check_passed + 1))
    fi

    if lsmod | grep -q iptable_filter; then
        log_ok "✓ 内核模块验证通过"
        final_check_passed=$((final_check_passed + 1))
    fi

    if [ -s "/etc/iptables/rules.v4" ]; then
        log_ok "✓ 规则文件验证通过"
        final_check_passed=$((final_check_passed + 1))
    fi

    if [ $final_check_passed -ge 2 ]; then
        log_ok "iptables 防火墙已成功启用并运行正常"
    else
        log_warn "iptables 部分组件就绪,建议检查详细状态"
    fi

    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

iptables_off() {
    clear
    echo -e "${gl_zi}>>> 关闭iptables防火墙(保活22端口)${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    iptables -P INPUT ACCEPT
    iptables -P FORWARD ACCEPT
    log_info "默认策略已临时设为 ACCEPT"

    iptables -F
    iptables -X
    iptables -t nat -F && iptables -t nat -X
    iptables -t mangle -F && iptables -t mangle -X
    log_warn "已清空所有规则"

    iptables -A INPUT -p tcp --dport 22 -j ACCEPT
    log_ok "已保留 TCP 22 端口"

    systemctl stop iptables 2>/dev/null || service iptables stop 2>/dev/null
    systemctl disable iptables 2>/dev/null || chkconfig iptables off 2>/dev/null
    log_warn "iptables 服务已停止并取消开机启动"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

show_port_rules() {
    local total=0
    local allowed=0
    local blocked=0

    printf "%-8s %-10s %-10s %-10s %-8s\n" "规则" "协议" "端口" "状态" "动作"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    iptables -nL INPUT --line-numbers | while read -r line; do
        if echo "$line" | grep -q "dpt:"; then
            rule_num=$(echo "$line" | awk '{print $1}')
            action=$(echo "$line" | awk '{print $2}')
            protocol=$(echo "$line" | awk '{print $3}')

            port=$(echo "$line" | grep -o "dpt:[0-9]\+" | cut -d: -f2)
            if [ -z "$port" ]; then
                port=$(echo "$line" | awk '{for(i=1;i<=NF;i++) if($i ~ /dpt:/) {split($i,a,":"); print a[2]; break}}')
            fi

            case "$action" in
            "ACCEPT")
                status="允许"
                color="${gl_lv}"
                ;;
            "DROP")
                status="禁止"
                color="${gl_hong}"
                ;;
            "REJECT")
                status="拒绝"
                color="${gl_hong}"
                ;;
            *)
                status="未知"
                color="${gl_bai}"
                ;;
            esac

            printf "${color}%-6s %-8s %-8s %-10s %-12s${gl_bai}\n" \
                "#$rule_num" "$protocol" "$port" "$status" "$action"
        fi
    done

    total=$(iptables -nL INPUT --line-numbers | grep -c "dpt:")
    allowed=$(iptables -nL INPUT --line-numbers | grep "dpt:" | grep -c "ACCEPT")
    blocked=$(iptables -nL INPUT --line-numbers | grep "dpt:" | grep -E -c "DROP|REJECT")

    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    local policy
    policy=$(iptables -L INPUT -n | grep -oP 'policy \K[^)]+')
    echo -e "${gl_bai}规则总计:${gl_zi}${total} ${gl_hong}|${gl_bai}允许:${gl_lv}${allowed} ${gl_hong}|${gl_bai}禁止/拒绝:${gl_hong}${blocked}${gl_hong} |${gl_bai}默认策略:${gl_zi}$policy${gl_bai}"

    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
}

check_iptables_status_enhanced() {
    local status=0
    local rule_count=0
    local service_status=""
    local active_method=""

    sleep_fractional 1

    if systemctl is-active iptables &>/dev/null; then
        service_status="${gl_lv}服务运行中(systemctl-iptables)${gl_bai}"
        active_method="iptables"
        status=0
    elif systemctl is-active netfilter-persistent &>/dev/null; then
        service_status="${gl_lv}服务运行中(netfilter-persistent)${gl_bai}"
        active_method="netfilter-persistent"
        status=0
    elif systemctl status iptables 2>/dev/null | grep -q "active (exited)"; then
        service_status="${gl_lv}服务运行中(active-exited)${gl_bai}"
        active_method="iptables"
        status=0
    elif service iptables status &>/dev/null; then
        service_status="${gl_lv}服务运行中(service)${gl_bai}"
        active_method="service"
        status=0
    else
        rule_count=$(iptables-save 2>/dev/null | grep -c '^-A' || echo 0)
        if [ $rule_count -gt 0 ]; then
            service_status="${gl_huang}服务未运行但规则已加载${gl_bai}"
            status=1
        else
            service_status="${gl_huang}服务未运行${gl_bai}"
            status=1
        fi
    fi

    rule_count=$(iptables-save 2>/dev/null | grep -c '^-A' || echo 0)

    local persisted_rules=0
    local config_file=""

    for config in "/etc/iptables/rules.v4" "/etc/sysconfig/iptables" "/etc/iptables/rules"; do
        if [ -f "$config" ]; then
            config_file="$config"
            persisted_rules=$(grep -c '^-A' "$config" 2>/dev/null || echo 0)
            break
        fi
    done

    echo -e "${gl_bai}防火墙状态: $service_status ${gl_hong}|${gl_bai} 内存规则: ${gl_huang}$rule_count${gl_bai}"
    if [ -n "$config_file" ]; then
        echo -e "${gl_bai}配置文件: ${gl_huang}$config_file${gl_bai} ${gl_hong}|${gl_bai} 持久化规则: ${gl_huang}$persisted_rules${gl_bai}"
    else
        echo -e "${gl_huang}未找到持久化配置文件${gl_bai}"
    fi

    if [ $rule_count -ne $persisted_rules ] && [ $persisted_rules -gt 0 ]; then
        echo -e "${gl_huang}警告: 内存规则($rule_count)与持久化规则($persisted_rules)数量不一致${gl_bai}"
        status=2
    fi

    local input_policy output_policy forward_policy
    input_policy=$(iptables -L INPUT -n 2>/dev/null | grep -oP 'policy \K\S+' || echo "未知")
    forward_policy=$(iptables -L FORWARD -n 2>/dev/null | grep -oP 'policy \K\S+' || echo "未知")
    output_policy=$(iptables -L OUTPUT -n 2>/dev/null | grep -oP 'policy \K\S+' || echo "未知")

    echo -e "${gl_bai}默认策略: INPUT[${gl_huang}$input_policy${gl_bai}] FORWARD[${gl_huang}$forward_policy${gl_bai}] OUTPUT[${gl_huang}$output_policy${gl_bai}]"

    return $status
}

iptables_manager() {
    while true; do
        root_use
        clear
        echo -e "${gl_huang}>>> 当前iptables规则${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        check_iptables_status_enhanced
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        list_beautify_iptables_input INPUT
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e ""
        echo -e "${gl_zi}>>> iptables规则管理${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_bufan}1.  ${gl_bai}开启防火墙          ${gl_bufan}2.  ${gl_bai}关闭防火墙"
        echo -e "${gl_bufan}3.  ${gl_bai}允许指定端口        ${gl_bufan}4.  ${gl_bai}删除指定端口规则"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_bufan}5.  ${gl_bai}封禁指定端口        ${gl_bufan}6.  ${gl_bai}解封指定端口并允许"
        echo -e "${gl_bufan}7.  ${gl_bai}所有端口规则        ${gl_bufan}8.  ${gl_bai}初始化iptables"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_bufan}9.  ${gl_bai}当前规则列表        ${gl_bufan}10. ${gl_bai}高级防火墙管理"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_huang}0.  ${gl_bai}返回上一级选单      ${gl_hong}00. ${gl_bai}退出脚本"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        read -r -e -p "请输入你的选择: " choice

        case $choice in
        1) iptables_on ;;
        2) iptables_off ;;
        3)
            echo -e ""
            echo -e "${gl_zi}>>> 允许指定端口${gl_bai}"
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

            read -r -e -p "$(echo -e "${gl_bai}请输入要放行的端口号 (${gl_huang}0${gl_bai}返回): ")" port

            [[ -z "$port" ]] && { cancel_empty "上一级选单"; continue; }
            [[ "$port" == "0" ]] && { cancel_return "上一级选单"; continue; }

            if ! [[ $port =~ ^[0-9]+$ && $port -ge 1 && $port -le 65535 ]]; then
                echo -e ""
                log_warn "端口号非法,请输入 ${gl_hong}1-65535${gl_bai} 之间的数字"
                echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
                exit_animation
                continue
            fi

            if iptables -C INPUT -p tcp --dport "$port" -j ACCEPT 2>/dev/null; then
                echo -e ""
                log_info "端口 ${gl_huang}$port${gl_bai} 的允许规则已存在,无需重复添加"
            else
                conflict=$(iptables -nL INPUT --line-numbers | grep "dpt:$port" | head -1)
                if [ -n "$conflict" ]; then
                    echo -e ""
                    log_info "端口 ${gl_huang}$port${gl_bai} 已有其他防火墙规则"
                    echo -e "${gl_bai}现有规则: ${gl_zi}$conflict${gl_bai}"
                    echo -e ""
                    read -r -e -p "$(echo -e "${gl_bai}是否继续添加允许规则?(${gl_lv}y${gl_bai}/${gl_hong}N${gl_bai}): ")" confirm
                    if [[ $confirm =~ ^[Yy]$ ]]; then
                        iptables -A INPUT -p tcp --dport "$port" -j ACCEPT
                        log_ok "已放行 TCP 端口 ${gl_lv}$port${gl_bai}"
                    else
                        log_info "已取消操作"
                    fi
                else
                    iptables -A INPUT -p tcp --dport "$port" -j ACCEPT
                    echo -e ""
                    log_ok "已放行 TCP 端口 ${gl_lv}$port${gl_bai}"
                fi
            fi

            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            break_end
            ;;
        4)
            clear
            echo -e "${gl_bufan}"
            echo -e "${gl_huang}>>> 当前规则列表${gl_bai}"
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            if iptables -L -n --line-numbers; then
                log_ok "规则列表显示完成"
            else
                log_error "规则列表显示失败"
            fi
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            echo -e ""
            echo -e "${gl_zi}>>> 删除指定端口规则${gl_bai}"
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            read -r -e -p "$(echo -e "${gl_bai}请输入要删除的端口号 (${gl_huang}0${gl_bai}返回): ")" port

            [[ -z "$port" ]] && { cancel_empty "上一级选单"; continue; }
            [[ "$port" == "0" ]] && { cancel_return "上一级选单"; continue; }

            if [[ $port =~ ^[0-9]+$ && $port -ge 1 && $port -le 65535 ]]; then
                if iptables -D INPUT -p tcp --dport "$port" -j ACCEPT 2>/dev/null ||
                    iptables -D INPUT -p tcp --dport "$port" -j DROP 2>/dev/null; then
                    echo -e ""
                    log_ok "已删除 TCP 端口 $port 相关规则"
                else
                    echo -e ""
                    log_warn "未找到 TCP 端口 $port 相关规则"
                fi
            else
                log_error "端口号非法,已跳过"
            fi
            echo -e ""
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            echo -e "${gl_lv}>>> 修改后的规则列表${gl_bai}"
            if iptables -L -n --line-numbers; then
                log_ok "规则列表显示完成"
            else
                log_error "规则列表显示失败"
            fi
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            break_end
            ;;
        5)
            clear
            echo -e ""
            echo -e "${gl_huang}>>> 当前所有端口规则列表${gl_bai}"
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            show_port_rules
            echo -e ""
            echo -e "${gl_zi}>>> 封禁指定端口(TCP)${gl_bai}"
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            read -r -e -p "$(echo -e "${gl_bai}请输入要封禁的端口号 (${gl_huang}0${gl_bai}返回): ")" port

            [[ -z "$port" ]] && { cancel_empty "上一级选单"; continue; }
            [[ "$port" == "0" ]] && { cancel_return "上一级选单"; continue; }

            if [[ $port =~ ^[0-9]+$ && $port -ge 1 && $port -le 65535 ]]; then
                if iptables -C INPUT -p tcp --dport "$port" -j DROP 2>/dev/null; then
                    echo -e ""
                    log_warn "端口 $port 已存在封禁规则,无需重复添加"
                    exit_animation
                    continue
                fi

                while iptables -D INPUT -p tcp --dport "$port" -j ACCEPT 2>/dev/null; do
                    echo -e "${gl_huang}删除端口 $port 的允许规则${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
                done

                local insert_position=3
                local accept_rules_count
                accept_rules_count=$(iptables -L INPUT -n --line-numbers | grep -c "ACCEPT")
                if [[ $accept_rules_count -gt 0 ]]; then
                    insert_position=$((accept_rules_count + 1))
                fi

                iptables -I INPUT $insert_position -p tcp --dport "$port" -j DROP

                if [[ -f /etc/redhat-release ]]; then
                    if systemctl is-active --quiet firewalld; then
                        firewall-cmd --permanent --remove-port="$port"/tcp >/dev/null 2>&1
                        firewall-cmd --permanent --add-rich-rule="rule family='ipv4' port port='$port' protocol='tcp' reject" >/dev/null 2>&1
                        firewall-cmd --reload >/dev/null 2>&1
                        echo -e "${gl_lv}检测到使用 firewalld,已通过 firewalld 添加封禁规则${gl_bai}"
                    else
                        iptables-save >/etc/sysconfig/iptables 2>/dev/null
                        if command -v systemctl >/dev/null 2>&1; then
                            systemctl enable iptables --now 2>/dev/null || true
                        fi
                    fi
                elif [[ -f /etc/debian_version ]]; then
                    mkdir -p /etc/iptables 2>/dev/null
                    iptables-save >/etc/iptables/rules.v4 2>/dev/null

                    if systemctl is-enabled netfilter-persistent >/dev/null 2>&1; then
                        systemctl restart netfilter-persistent 2>/dev/null || true
                    else
                        if command -v apt-get >/dev/null 2>&1; then
                            apt-get update >/dev/null 2>&1
                            DEBIAN_FRONTEND=noninteractive apt-get install -y iptables-persistent >/dev/null 2>&1
                            systemctl enable netfilter-persistent 2>/dev/null || true
                            systemctl start netfilter-persistent 2>/dev/null || true
                        fi
                    fi
                else
                    echo -e "${gl_huang}警告:未识别发行版,请手动保存规则${gl_bai}"
                fi

                echo -e ""
                log_ok "已封禁 TCP 端口 ${gl_huang}$port${gl_bai} 并已保存,重启仍生效"
            else
                echo -e ""
                log_error "端口号非法,请输入 ${gl_huang}1-65535${gl_bai} 之间的数字"
            fi
            echo -e ""
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            echo -e "${gl_lv}>>> 修改后的规则列表${gl_bai}"
            show_port_rules
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            break_end
            ;;
        6)
            clear
            echo -e "${gl_huang}>>> 当前所有端口规则列表${gl_bai}"
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            show_port_rules
            echo -e ""
            echo -e "${gl_zi}>>> 解除端口封禁并允许指定端口${gl_bai}"
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            read -r -e -p "$(echo -e "${gl_bai}请输入要解除封禁的端口号 (${gl_huang}0${gl_bai}返回): ")" port

            [[ -z "$port" ]] && { cancel_empty "上一级选单"; continue; }
            [[ "$port" == "0" ]] && { cancel_return "上一级选单"; continue; }

            if [[ $port =~ ^[0-9]+$ && $port -ge 1 && $port -le 65535 ]]; then
                local deleted_rules=0

                while iptables -D INPUT -p tcp --dport "$port" -j DROP 2>/dev/null; do
                    deleted_rules=$((deleted_rules + 1))
                done

                while iptables -D INPUT -p tcp --dport "$port" -j REJECT 2>/dev/null; do
                    deleted_rules=$((deleted_rules + 1))
                done

                if iptables -C INPUT -p tcp --dport "$port" -j ACCEPT 2>/dev/null; then
                    echo -e ""
                    log_warn "端口 $port 已存在允许规则,无需重复添加"
                else
                    echo -e ""
                    read -r -e -p "$(echo -e "${gl_bai}是否要允许该端口 (${gl_lv}y${gl_bai}/${gl_hong}N${gl_bai})? ")" allow_port
                    allow_port=${allow_port:-N}

                    if [[ $allow_port =~ ^[Yy]$ ]]; then
                        local insert_position=3
                        local accept_rules_count
                        accept_rules_count=$(iptables -L INPUT -n --line-numbers | grep -c "ACCEPT")
                        if [[ $accept_rules_count -gt 0 ]]; then
                            insert_position=$((accept_rules_count + 1))
                        fi

                        iptables -I INPUT $insert_position -p tcp --dport "$port" -j ACCEPT
                        echo -e "${gl_lv}已添加端口 $port 的允许规则${gl_bai}"
                    else
                        echo -e "${gl_huang}未添加允许规则,仅解除封禁${gl_bai}"
                    fi
                fi

                if [[ $deleted_rules -gt 0 ]] || [[ $allow_port =~ ^[Yy]$ ]]; then
                    if [[ -f /etc/redhat-release ]]; then
                        if systemctl is-active --quiet firewalld; then
                            firewall-cmd --permanent --remove-rich-rule="rule family='ipv4' port port='$port' protocol='tcp' reject" >/dev/null 2>&1

                            if [[ $allow_port =~ ^[Yy]$ ]]; then
                                firewall-cmd --permanent --add-port="$port"/tcp >/dev/null 2>&1
                            fi

                            firewall-cmd --reload >/dev/null 2>&1
                            echo -e "${gl_lv}检测到使用 firewalld,已通过 firewalld 更新规则${gl_bai}"
                        else
                            iptables-save >/etc/sysconfig/iptables 2>/dev/null
                            if command -v systemctl >/dev/null 2>&1; then
                                systemctl enable iptables --now 2>/dev/null || true
                            fi
                        fi
                    elif [[ -f /etc/debian_version ]]; then
                        mkdir -p /etc/iptables 2>/dev/null
                        iptables-save >/etc/iptables/rules.v4 2>/dev/null

                        if systemctl is-enabled netfilter-persistent >/dev/null 2>&1; then
                            systemctl restart netfilter-persistent 2>/dev/null || true
                        else
                            if command -v apt-get >/dev/null 2>&1; then
                                apt-get update >/dev/null 2>&1
                                DEBIAN_FRONTEND=noninteractive apt-get install -y iptables-persistent >/dev/null 2>&1
                                systemctl enable netfilter-persistent 2>/dev/null || true
                                systemctl start netfilter-persistent 2>/dev/null || true
                            fi
                        fi
                    else
                        echo -e "${gl_huang}警告:未识别发行版,请手动保存规则${gl_bai}"
                    fi
                fi

                echo -e ""
                echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
                if [[ $deleted_rules -gt 0 ]]; then
                    log_ok "已删除 $deleted_rules 条封禁规则,端口 $port 已解除封禁"
                    if [[ $allow_port =~ ^[Yy]$ ]]; then
                        log_ok "并已添加允许规则,重启仍生效"
                    else
                        log_ok "请手动添加允许规则以开放该端口"
                    fi
                else
                    log_warn "未找到端口 $port 的封禁规则"
                fi
            else
                echo -e ""
                echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
                log_error "端口号非法,请输入 1-65535 之间的数字"
            fi
            echo -e ""
            echo -e "${gl_lv}>>> 修改后的规则列表${gl_bai}"
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            show_port_rules
            break_end
            ;;
        7)
            show_port_rules
            break_end
            ;;
        8) iptables_init ;;
        9)
            clear
            echo -e "${gl_zi}>>> 当前规则列表${gl_bai}"
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            list_beautify_iptables_all
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            break_end
            ;;
        10) linux_iptables_panel "iptables规则管理" ;;
        0)  cancel_return "安全防御"; break ;;
        00 | 000 | 0000) exit_script ;;
        *) handle_invalid_input ;;
        esac
    done
}

fail2ban_stop() {
    echo -e ""
    echo -e "${gl_zi}>>> 停止防御程序${gl_bai}"
    echo -e "${gl_bufan}———————————————————————————————————————————————${gl_bai}"
    systemctl stop fail2ban
    log_ok "防御程序已停止"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

fail2ban_start() {
    echo -e ""
    echo -e "${gl_zi}>>> 启动防御程序${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    systemctl start fail2ban
    log_ok "防御程序已启动"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

fail2ban_restart() {
    echo -e ""
    echo -e "${gl_zi}>>> 重启防御程序${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    fail2ban-client -t && systemctl restart fail2ban
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

fail2ban_status() {
    clear
    echo -e ""
    echo -e "${gl_zi}>>> 查看防御程序服务状态${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    systemctl status fail2ban
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

fail2ban_ssh_logs() {
    echo -e ""
    echo -e "${gl_zi}>>> 查看SSH拦截记录${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    f2b_sshd
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

fail2ban_web_logs() {
    clear
    echo -e ""
    echo -e "${gl_zi}>>> 查看网站拦截记录${gl_bai}"
    local jails=(
        fail2ban-nginx-cc
        nginx-418
        nginx-bad-request
        nginx-badbots
        nginx-botsearch
        nginx-deny
        nginx-http-auth
        nginx-unauthorized
        php-url-fopen
        nginx-pve
        nginx-403
    )

    for jail in "${jails[@]}"; do
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        fail2ban-client status "$jail"
    done
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

fail2ban_rules_list() {
    echo -e ""
    echo -e "${gl_zi}>>> 查看防御规则列表${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    fail2ban-client status
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

fail2ban_edit_blacklist() {
    install nano
    clear
    nano /etc/fail2ban/jail.d/nginx-cc.conf
    echo -e ""
    echo -e "${gl_zi}>>> 重启防御程序${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    echo -e "${gl_huang}修改配置文件后需要重启防御程序生效${gl_bai}"
    echo -e "${gl_huang}未修改配置文件不必重启防御程序${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    read -r -e -p "$(echo -e "${gl_bai}是否要重启防御程序? (${gl_lv}y${gl_bai}/${gl_hong}N${gl_bai}): ")" confirm
    [ "$confirm" = "0" ] && { cancel_return "上一级选单"; return 1; }
    if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
        echo -e ""
        echo -e "${gl_bai}正在重启防御程序${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        fail2ban-client -t && systemctl restart fail2ban
        f2b_status
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_lv}防御程序已重启${gl_bai}"
        exit_animation
        clear
    else
        echo -e "${gl_huang}已取消重启防御程序${gl_bai}"
        exit_animation
        clear
    fi
}

fail2ban_view_banned_ips() {
    clear
    echo -e ""
    echo -e "${gl_zi}>>> 常规查看所有封禁的IP${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    fail2ban-client status recidive
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    echo -e ""
    echo -e "${gl_zi}>>> 详细查看所有封禁的IP${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    for j in $(fail2ban-client status | awk -F: '/Jail list/ {gsub(/,/,""); print $2}'); do
        echo -e "${gl_bufan} $j ${gl_bai}"
        sudo fail2ban-client get "$j" banip --with-time
    done
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

fail2ban_ban_ip_manual() {
    echo -e ""
    echo -e "${gl_zi}>>> 手动永久封禁IP${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    read -r -e -p "请输入要批量封禁的 IP(空格分隔,输入 q 或回车返回): " ips

    [ "$ips" = "0" ] && { cancel_return "上一级选单"; return 1; }
    [[ -z "$ips" || "$ips" == "q" ]] && echo -e "${gl_hui}已取消操作,返回主菜单。${gl_bai}" && return

    fail2ban-client set recidive banip "$ips"
    fail2ban-client -t && systemctl restart fail2ban
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

fail2ban_unban_ip_manual() {
    echo -e ""
    echo -e "${gl_zi}>>> 手动永久解封 IP${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    read -r -e -p "请输入要永久解封的 IP(输入 q 或回车返回): " ip

    [ "$ip" = "0" ] && { cancel_return "上一级选单"; return 1; }
    [[ -z "$ip" || "$ip" == "q" ]] && echo -e "${gl_hui}已取消操作,返回主菜单。${gl_bai}" && return

    fail2ban-client set recidive unbanip "$ip"
    fail2ban-client unban "$ip"
    fail2ban-client -t && systemctl restart fail2ban
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

fail2ban_clear_all_bans() {
    echo -e ""
    echo -e "${gl_zi}>>> 清空所有封禁的IP${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    read -r -e -p "$(echo -e "${gl_bai}确定清空所有封禁? (${gl_lv}y${gl_bai}/${gl_hong}N${gl_bai}): ")" sure
    [[ $sure != y ]] && continue
    for jail in $(fail2ban-client status 2>/dev/null |
        sed -n '/Jail list:/ {s/.*Jail list://; s/,//g; p;}'); do
        for ip in $(fail2ban-client status "$jail" 2>/dev/null |
            awk '/Banned IP list:/ {for(i=4;i<=NF;i++) if($i~/[0-9]/) print $i}'); do
            fail2ban-client set "$jail" unbanip "$ip" >/dev/null 2>&1
        done
    done
    log_info "已解封所有 jail 的所有 IP"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

fail2ban_config_manager() {
    check_directory_empty "/etc/fail2ban" "网站防御配置文件管理" "true" || return 1
    clear
    echo -e "${gl_zi}>>> 网站防御配置文件管理${gl_bai}"
    bash <(curl -sL gitee.com/meimolihan/linux-command_sh/raw/master/linux_file_menu.sh)  "/etc/fail2ban"
}

fail2ban_edit_whitelist() {
    install nano
    clear
    nano /etc/fail2ban/jail.d/nginx-cc.conf
    echo -e ""
    echo -e "${gl_zi}>>> 重启防御程序${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    echo -e "${gl_huang}修改配置文件后需要重启防御程序生效${gl_bai}"
    echo -e "${gl_huang}未修改配置文件不必重启防御程序${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    read -r -e -p "$(echo -e "${gl_bai}是否要重启防御程序? (${gl_lv}y${gl_bai}/${gl_hong}N${gl_bai}): ")" confirm
    if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
        echo -e ""
        echo -e "${gl_bai}正在重启防御程序${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        fail2ban-client -t && systemctl restart fail2ban
        f2b_status
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_lv}防御程序已重启${gl_bai}"
        exit_animation
        clear
    else
        echo -e "${gl_huang}已取消重启防御程序${gl_bai}"
        exit_animation
        clear
    fi
}

fail2ban_view_whitelist() {
    clear
    echo -e "${gl_zi}>>> 所有监狱的白名单${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    for j in $(fail2ban-client status | awk -F: '/Jail list/ {gsub(/,/,""); print $2}'); do
        echo "=== $j ==="
        sudo fail2ban-client get "$j" ignoreip
    done
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

fail2ban_config_cloudflare() {
    echo "到cf后台右上角我的个人资料,选择左侧API令牌,获取Global API Key"
    echo "https://dash.cloudflare.com/login"
    read -r -e -p "输入CF的账号: " cfuser
    read -r -e -p "输入CF的Global API Key: " cftoken

    wget -O /etc/nginx/conf.d/default.conf ${gh_proxy}raw.githubusercontent.com/kejilion/nginx/main/default11.conf
    docker exec nginx nginx -s reload

    cd /etc/fail2ban/jail.d/
    curl -sS -O https://gitee.com/meimolihan/fail2ban/raw/master/nginx-docker-cc.conf

    cd /etc/fail2ban/action.d
    curl -sS -O ${gh_proxy}raw.githubusercontent.com/kejilion/config/main/fail2ban/cloudflare-docker.conf

    sed -i "s/kejilion@outlook.com/$cfuser/g" /etc/fail2ban/action.d/cloudflare-docker.conf
    sed -i "s/APIKEY00000/$cftoken/g" /etc/fail2ban/action.d/cloudflare-docker.conf
    f2b_status

    echo "已配置cloudflare模式,可在cf后台,站点-安全性-事件中查看拦截记录"
}

fail2ban_auto_under_attack() {
    echo -e "${gl_huang}网站每5分钟自动检测,当达检测到高负载会自动开盾,低负载也会自动关闭5秒盾。${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    echo "获取CF参数: "
    echo -e "到cf后台右上角我的个人资料,选择左侧API令牌,获取${gl_huang}Global API Key${gl_bai}"
    echo -e "到cf后台域名概要页面右下方获取${gl_huang}区域ID${gl_bai}"
    echo "https://dash.cloudflare.com/login"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    read -r -e -p "输入CF的账号: " cfuser
    [ "$cfuser" = "0" ] && { cancel_return "上一级选单"; return 1; }
    [ -z "$cfuser" ] && { cancel_empty "上一级选单"; return 1; }

    read -r -e -p "输入CF的Global API Key: " cftoken
    [ "$cftoken" = "0" ] && { cancel_return "上一级选单"; return 1; }
    [ -z "$cftoken" ] && { cancel_empty "上一级选单"; return 1; }

    read -r -e -p "输入CF中域名的区域ID: " cfzonID
    [ "$cfzonID" = "0" ] && { cancel_return "上一级选单"; return 1; }
    [ -z "$cfzonID" ] && { cancel_empty "上一级选单"; return 1; }

    cd ~
    install jq bc
    check_crontab_installed
    curl -sS -O ${gh_proxy}raw.githubusercontent.com/kejilion/sh/main/CF-Under-Attack.sh
    chmod +x CF-Under-Attack.sh
    sed -i "s/AAAA/$cfuser/g" ~/CF-Under-Attack.sh
    sed -i "s/BBBB/$cftoken/g" ~/CF-Under-Attack.sh
    sed -i "s/CCCC/$cfzonID/g" ~/CF-Under-Attack.sh

    local cron_job="*/5 * * * * ~/CF-Under-Attack.sh"

    local existing_cron
    existing_cron=$(crontab -l 2>/dev/null | grep -F "$cron_job")

    if [ -z "$existing_cron" ]; then
        (
            crontab -l 2>/dev/null
            echo "$cron_job"
        ) | crontab -
        echo "高负载自动开盾脚本已添加"
    else
        echo "自动开盾脚本已存在,无需添加"
    fi
}

fail2ban_nginx_waf_on() {
    nginx_waf on
    echo "站点WAF已开启"
}

fail2ban_nginx_waf_off() {
    nginx_waf off
    echo "站点WAF已关闭"
}

fail2ban_backup_config() {
    while true; do
        clear
        echo -e ""
        echo -e "${gl_zi}>>> 备份 Fail2Ban 配置${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        local backup_filename
        backup_filename="fail2ban_$(date +"%Y%m%d%H%M%S").tar.gz"
        echo -e "${gl_huang}正在备份 fail2ban ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
        tar czf "/$backup_filename" \
            --absolute-names \
            /etc/fail2ban /var/log/fail2ban.log 2>/dev/null
        echo -e "${gl_lv}备份文件已创建: /$backup_filename${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

        read -r -e -p "$(echo -e "${gl_bai}要传送到远程服务器吗? (${gl_lv}y${gl_bai}/${gl_hong}N${gl_bai}): ")" choice

        case "$choice" in
        [Yy])
            while true; do
                read -r -e -p "请输入远端服务器IP: " remote_ip
                read -r -e -p "目标SSH端口 [默认22]: " TARGET_PORT
                TARGET_PORT=${TARGET_PORT:-22}

                if [[ -z "$remote_ip" ]]; then
                    echo -e "${gl_hong}错误: 请输入远端服务器IP${gl_bai}"
                    read -r -e -p "$(echo -e "${gl_hong}是否重新输入? (${gl_lv}y${gl_bai}/${gl_hong}n${gl_bai}返回上级): ")" retry_choice
                    if [[ "$retry_choice" =~ [Nn] ]]; then
                        break
                    fi
                    continue
                fi

                echo -e ""
                echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
                ssh-keygen -f "/root/.ssh/known_hosts" -R "$remote_ip" 2>/dev/null
                scp -P "$TARGET_PORT" -o StrictHostKeyChecking=no "/$backup_filename" "root@$remote_ip:/"
                echo -e "${gl_lv}文件已传送至远程服务器根目录${gl_bai}"
                echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
                echo -e ""
                break 2
            done
            ;;
        [Nn] | "")
            break
            ;;
        0) cancel_return; return ;;
        *)
            echo -e "${gl_hong}无效输入,3秒后重新开始${gl_bai}"
            sleep_fractional 3
            continue
            ;;
        esac
    done
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

fail2ban_restore_config() {
    root_use
    echo -e ""
    echo -e "${gl_zi}>>> 恢复 Fail2Ban 配置${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    echo ""
    echo -e "${gl_bai}可用的 Fail2Ban 备份${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    echo ""
    ls -lt /fail2ban_*.tar.gz 2>/dev/null | awk '{print $NF}'
    echo ""
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    read -r -e -p "$(echo -e "输入文件名还原指定备份,${gl_bufan}回车${gl_bai}还原最新备份,${gl_huang}0${gl_bai}返回: ")" filename

    [[ -z "$filename" ]] && { cancel_empty "上一级选单"; return 1; } 
    [[ "$filename" == "0" ]] && { cancel_return "上一级选单"; return 1; }

    [[ -z "$filename" ]] && filename=$(ls -t /fail2ban_*.tar.gz 2>/dev/null | head -1)
    if [[ -f "$filename" ]]; then
        echo ""
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_bai}正在还原 ${gl_huang}$filename${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
        systemctl stop fail2ban
        tar -xzf "$filename" -C /
        systemctl start fail2ban
        echo -e "${gl_lv}fail2ban 配置 & 日志已还原,服务已重启${gl_bai}"
    else
        echo -e "${gl_hong}未找到备份文件${gl_bai}"
    fi

    f2b_status
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

fail2ban_install() {
    clear
    echo -e "${gl_zi}>>> 安装防御程序${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    f2b_install_sshd
    echo -e ""
    log_info "正在下载依赖文件${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    log_info "下载 ${gl_bufan}Fail2Ban${gl_bai} 过滤器文件${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
    log_warn "保存至: ${gl_bufan}/etc/fail2ban/filter.d${gl_bai} "
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    cd /etc/fail2ban/filter.d
    wget -c https://gitee.com/meimolihan/sh/raw/master/f2b/filter.d/fail2ban-nginx-cc.conf
    wget -c https://gitee.com/meimolihan/sh/raw/master/f2b/filter.d/nginx-418.conf
    wget -c https://gitee.com/meimolihan/sh/raw/master/f2b/filter.d/nginx-403.conf
    wget -c https://gitee.com/meimolihan/sh/raw/master/f2b/filter.d/nginx-deny.conf
    wget -c https://gitee.com/meimolihan/sh/raw/master/f2b/filter.d/nginx-unauthorized.conf
    wget -c https://gitee.com/meimolihan/sh/raw/master/f2b/filter.d/nginx-bad-request.conf
    wget -c https://gitee.com/meimolihan/sh/raw/master/f2b/filter.d/nginx-pve.conf

    mkdir -p /var/log/nginx && touch /var/log/nginx/access.log /var/log/nginx/error.log
    mkdir -p /etc/nginx && touch /etc/nginx/nginx.conf

    echo -e ""
    log_info "下载 ${gl_bufan}Fail2Ban${gl_bai} 的"监狱"配置文件${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
    log_warn "保存至: ${gl_bufan}/etc/fail2ban/jail.d${gl_bai} "
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    cd /etc/fail2ban/jail.d
    wget -c https://gitee.com/meimolihan/sh/raw/master/f2b/jail.d/sshd.local
    wget -c https://gitee.com/meimolihan/sh/raw/master/f2b/jail.d/nginx-cc.conf
    sed -i "/cloudflare/d" /etc/fail2ban/jail.d/nginx-cc.conf

    echo -e ""
    log_info "配置 ${gl_bufan}Fail2Ban${gl_bai} 开机自启动${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    f2b_status
    f2b_autostart
    echo -e ""
    log_info "当前${gl_bufan}Fail2Ban${gl_bai} 服务状态"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    fail2ban-client -t && systemctl restart fail2ban && systemctl status fail2ban
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

fail2ban_update() {
    clear
    echo -e "${gl_zi}>>> 更新防御程序${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    update_fail2ban
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

remove() {
    if [ $# -eq 0 ]; then
        echo "未提供软件包参数!"
        return 1
    fi

    for package in "$@"; do
        echo -e ""
        echo -e "${gl_huang}>>> 正在卸载: ${gl_huang}$package${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        if command -v dnf &>/dev/null; then
            dnf remove -y "$package"
        elif command -v yum &>/dev/null; then
            yum remove -y "$package"
        elif command -v apt &>/dev/null; then
            apt purge -y "$package"
        elif command -v apk &>/dev/null; then
            apk del "$package"
        elif command -v pacman &>/dev/null; then
            pacman -Rns --noconfirm "$package"
        elif command -v zypper &>/dev/null; then
            zypper remove -y "$package"
        elif command -v opkg &>/dev/null; then
            opkg remove "$package"
        elif command -v pkg &>/dev/null; then
            pkg delete -y "$package"
        else
            echo -e "${gl_huang}未知的包管理器!${gl_bai}"
            return 1
        fi
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    done
}

fail2ban_uninstall() {
    echo -e ""
    echo -e "${gl_zi}>>> 卸载防御程序${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    read -r -e -p "$(echo -e "${gl_bai}确定要卸载 Fail2Ban 防御程序吗? (${gl_lv}y${gl_bai}/${gl_hong}N${gl_bai}): ")" yn
    case "$yn" in
    [Yy]) 
        clear
        echo -e ""
        remove fail2ban
        rm -rf /etc/fail2ban
        crontab -l | grep -v "CF-Under-Attack.sh" | crontab - 2>/dev/null
        log_ok "Fail2Ban防御程序已卸载"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        break_end
        ;;
    0) cancel_return; return ;;
    *)
        echo -e "${gl_huang}已取消卸载${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        exit_animation
        return
        ;;
    esac
}

web_security() {
    while true; do
        root_use
        check_f2b_status
        check_waf_status
        check_cf_mode
        f2b_autostart
        clear
        echo -e "${gl_zi}>>> Fail2Ban 安全防御管理${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

        boot_en=$(systemctl is-enabled fail2ban 2>/dev/null)
        case "$boot_en" in
        enabled) boot_zh="${gl_lv}已开启" ;;
        disabled) boot_zh="${gl_hui}已禁用" ;;
        *) boot_zh="${gl_hui}未知" ;;
        esac
        echo -e "${gl_bai}网站防御程序自启动:${boot_zh}"

        fb_ver=$(fail2ban-client version 2>/dev/null || echo -e "${gl_hui}未安装")
        echo -e "${gl_bai}网站防御程序版本号:${gl_lv}${fb_ver}"

        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_bufan}1.  ${gl_bai}停止防御程序          ${gl_bufan}2.  ${gl_bai}启动防御程序"
        echo -e "${gl_bufan}3.  ${gl_bai}重启防御程序          ${gl_bufan}4.  ${gl_bai}检查服务状态"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_bufan}5.  ${gl_bai}查看SSH拦截记录       ${gl_bufan}6.  ${gl_bai}查看网站拦截记录"
        echo -e "${gl_bufan}7.  ${gl_bai}查看防御规则列表      ${gl_bufan}8.  ${gl_bai}查看日志实时监控"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_bufan}11. ${gl_bai}配置黑名单            ${gl_bufan}12. ${gl_bai}查看所有封禁的IP"
        echo -e "${gl_bufan}13. ${gl_bai}手动永久封禁IP        ${gl_bufan}14. ${gl_bai}手动解除封禁的IP"
        echo -e "${gl_bufan}15. ${gl_bai}清空所有封禁的IP      ${gl_bufan}16. ${gl_bai}配置文件管理器"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_bufan}17. ${gl_bai}配置白名单            ${gl_bufan}18. ${gl_bai}查看所有解封的IP"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_bufan}21. ${gl_bai}cloudflare模式        ${gl_bufan}22. ${gl_bai}高负载开启5秒盾"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_bufan}31. ${gl_bai}开启WAF               ${gl_bufan}32. ${gl_bai}关闭WAF"
        echo -e "${gl_bufan}33. ${gl_bai}开启DDOS防御          ${gl_bufan}34. ${gl_bai}关闭DDOS防御"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_bufan}41. ${gl_bai}更新防御程序          ${gl_bufan}42. ${gl_bai}防火墙管理"
        echo -e "${gl_bufan}b.  ${gl_bai}备份防御程序          ${gl_bufan}r.  ${gl_bai}恢复防御程序"
        echo -e "${gl_lv}66. ${gl_bai}安装防御程序          ${gl_hong}99. ${gl_bai}卸载防御程序"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_huang}0.  ${gl_bai}返回上一级选单        ${gl_hong}00. ${gl_bai}退出脚本"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        read -r -e -p "请输入你的选择: " sub_choice

        case $sub_choice in
        1)  fail2ban_stop ;;
        2)  fail2ban_start ;;
        3)  fail2ban_restart ;;
        4)  fail2ban_status ;;
        5)  fail2ban_ssh_logs ;;
        6)  fail2ban_web_logs ;;
        7)  fail2ban_rules_list ;;
        8)  tail -f /var/log/fail2ban.log ;;
        11) fail2ban_edit_blacklist ;;
        12) fail2ban_view_banned_ips ;;
        13) fail2ban_ban_ip_manual ;;
        14) fail2ban_unban_ip_manual ;;
        15) fail2ban_clear_all_bans ;;
        16) fail2ban_config_manager ;;
        17) fail2ban_edit_whitelist ;;
        18) fail2ban_view_whitelist ;;
        21) fail2ban_config_cloudflare ;;
        22) fail2ban_auto_under_attack ;;
        31) fail2ban_nginx_waf_on ;;
        32) fail2ban_nginx_waf_off ;;
        33) enable_ddos_defense ;;
        34) disable_ddos_defense ;;
        41) fail2ban_update ;;
        42) iptables_manager ;;
        b | B)  fail2ban_backup_config ;;
        r | R)  fail2ban_restore_config ;;
        66) fail2ban_install ;;
        99) fail2ban_uninstall ;;
        0) cancel_return "已经是主菜单"; web_security ;;
        00 | 000 | 0000) exit_script ;;
        *) handle_invalid_input ;;
        esac
    done
}

web_security

创建本地脚本

new_script="new_test.sh"

cat > "$new_script" << 'EOF'
#!/bin/bash

# 粘贴脚本源码

EOF

# 保留本地脚本,去掉 rm -f "$new_script"
chmod +x "$new_script" && ./"$new_script" && rm -f "$new_script"