From fed71d17cfa38d8ee1c0b81a7cb02db77f74c19f Mon Sep 17 00:00:00 2001 From: sushen339 Date: Tue, 18 Nov 2025 13:59:48 +0800 Subject: [PATCH] --- README.md | 6 +- blockip/bip | Bin 54192 -> 58288 bytes blockip/src/stats.c | 140 ++++++--- v3.1.sh | 742 -------------------------------------------- 4 files changed, 98 insertions(+), 790 deletions(-) delete mode 100644 v3.1.sh diff --git a/README.md b/README.md index eaae919..6129085 100644 --- a/README.md +++ b/README.md @@ -110,16 +110,16 @@ bash <(curl -fsSL "https://raw.githubusercontent.com/sushen339/tools/main/nft.sh bash <(curl -fsSL "https://gh-proxy.com/https://raw.githubusercontent.com/sushen339/tools/main/nft.sh") ``` -## 8. block-ip.sh —— 基于SSH登陆的IP 封禁脚本 +## 8. block-ip.sh —— 基于SSH登陆的IP 封禁 一键运行: ```bash wget https://raw.githubusercontent.com/sushen339/tools/main/block-ip.sh -O /tmp/block-ip.sh && bash /tmp/block-ip.sh install ``` -代理加速: +### C 语言版本 ```bash -wget https://gh-proxy.com/https://raw.githubusercontent.com/sushen339/tools/main/block-ip.sh -O /tmp/block-ip.sh && bash /tmp/block-ip.sh install +wget https://raw.githubusercontent.com/sushen339/tools/main/blockip/bip -O /usr/local/bin/bip && chmod +x /usr/local/bin/bip && bip install ``` > 建议所有脚本以 root 权限运行,详细参数和说明请阅读各脚本头部注释。 diff --git a/blockip/bip b/blockip/bip index fb258d33d9cfcb7820f04d884f198d7b41a03815..22007d29bcc22b163d0f009846c0867487791141 100644 GIT binary patch delta 12025 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 diff --git a/blockip/src/stats.c b/blockip/src/stats.c index 786858e..cf4bbcf 100644 --- a/blockip/src/stats.c +++ b/blockip/src/stats.c @@ -90,11 +90,9 @@ void show_subnet_aggregation(void) { return; } - /* 创建临时文件存储IPv4地址 */ - char temp_v4_file[MAX_PATH_LEN]; - snprintf(temp_v4_file, sizeof(temp_v4_file), "/tmp/blockip_v4_$$"); - FILE *temp_v4 = fopen(temp_v4_file, "w"); - + /* 统计各级网段 */ + struct { char subnet[64]; int count; int mask; } agg[256]; + int agg_count = 0; int v6_count = 0; char line[MAX_LINE_LEN]; @@ -108,51 +106,104 @@ void show_subnet_aggregation(void) { if (strchr(line, ':')) { v6_count++; - } else if (temp_v4) { - fprintf(temp_v4, "%s\n", line); + continue; + } + + /* 解析IPv4,统计/8, /16, /24 */ + unsigned int a, b, c, d; + if (sscanf(line, "%u.%u.%u.%u", &a, &b, &c, &d) == 4) { + char subnet_24[64], subnet_16[64], subnet_8[64]; + snprintf(subnet_24, sizeof(subnet_24), "%u.%u.%u", a, b, c); + snprintf(subnet_16, sizeof(subnet_16), "%u.%u", a, b); + snprintf(subnet_8, sizeof(subnet_8), "%u", a); + + /* 统计/24 */ + int found = 0; + for (int i = 0; i < agg_count; ++i) { + if (agg[i].mask == 24 && strcmp(agg[i].subnet, subnet_24) == 0) { + agg[i].count++; + found = 1; + break; + } + } + if (!found && agg_count < 256) { + snprintf(agg[agg_count].subnet, sizeof(agg[agg_count].subnet), "%s", subnet_24); + agg[agg_count].count = 1; + agg[agg_count].mask = 24; + agg_count++; + } + + /* 统计/16 */ + found = 0; + for (int i = 0; i < agg_count; ++i) { + if (agg[i].mask == 16 && strcmp(agg[i].subnet, subnet_16) == 0) { + agg[i].count++; + found = 1; + break; + } + } + if (!found && agg_count < 256) { + snprintf(agg[agg_count].subnet, sizeof(agg[agg_count].subnet), "%s", subnet_16); + agg[agg_count].count = 1; + agg[agg_count].mask = 16; + agg_count++; + } + + /* 统计/8 */ + found = 0; + for (int i = 0; i < agg_count; ++i) { + if (agg[i].mask == 8 && strcmp(agg[i].subnet, subnet_8) == 0) { + agg[i].count++; + found = 1; + break; + } + } + if (!found && agg_count < 256) { + snprintf(agg[agg_count].subnet, sizeof(agg[agg_count].subnet), "%s", subnet_8); + agg[agg_count].count = 1; + agg[agg_count].mask = 8; + agg_count++; + } + } + } + fclose(fp); + + /* 排序:先按count降序,再按mask升序 */ + for (int i = 0; i < agg_count - 1; ++i) { + for (int j = i + 1; j < agg_count; ++j) { + if (agg[j].count > agg[i].count || + (agg[j].count == agg[i].count && agg[j].mask < agg[i].mask)) { + char tmp_subnet[64]; + int tmp_count = agg[i].count, tmp_mask = agg[i].mask; + strcpy(tmp_subnet, agg[i].subnet); + agg[i].count = agg[j].count; + agg[i].mask = agg[j].mask; + strcpy(agg[i].subnet, agg[j].subnet); + agg[j].count = tmp_count; + agg[j].mask = tmp_mask; + strcpy(agg[j].subnet, tmp_subnet); + } } } - fclose(fp); - if (temp_v4) fclose(temp_v4); - - /* 聚合分析 */ - char command[MAX_COMMAND_LEN * 2]; - snprintf(command, sizeof(command), - "if [ -f %s ]; then " - " cat %s | cut -d. -f1-3 | sort | uniq -c | awk '$1>=2 {printf \"%%d|%%s|24\\n\", $1, $2}' > /tmp/agg24_$$; " - " cat %s | cut -d. -f1-2 | sort | uniq -c | awk '$1>=2 {printf \"%%d|%%s|16\\n\", $1, $2}' > /tmp/agg16_$$; " - " cat %s | cut -d. -f1 | sort | uniq -c | awk '$1>=2 {printf \"%%d|%%s|8\\n\", $1, $2}' > /tmp/agg8_$$; " - " cat /tmp/agg24_$$ /tmp/agg16_$$ /tmp/agg8_$$ | sort -t'|' -k1,1rn -k3,3n | head -n 10; " - " rm -f /tmp/agg24_$$ /tmp/agg16_$$ /tmp/agg8_$$; " - "fi", - temp_v4_file, temp_v4_file, temp_v4_file, temp_v4_file); - - fp = popen(command, "r"); + /* 输出聚合结果,只显示count>=2的 */ bool has_output = false; - - if (fp) { - while (fgets(line, sizeof(line), fp)) { - line[strcspn(line, "\n")] = 0; - - int count, mask; - char subnet[MAX_IP_LEN]; - - if (sscanf(line, "%d|%[^|]|%d", &count, subnet, &mask) == 3) { - has_output = true; - if (mask == 8) { - printf(" - %-18s %s(%d 个)%s\n", - strcat(subnet, ".0.0.0/8"), C_RED, count, C_RESET); - } else if (mask == 16) { - printf(" - %-18s %s(%d 个)%s\n", - strcat(subnet, ".0.0/16"), C_RED, count, C_RESET); - } else if (mask == 24) { - printf(" - %-18s %s(%d 个)%s\n", - strcat(subnet, ".0/24"), C_RED, count, C_RESET); - } + int show_count = 0; + for (int i = 0; i < agg_count && show_count < 10; ++i) { + if (agg[i].count >= 2) { + has_output = true; + if (agg[i].mask == 8) { + printf(" - %-18s %s(%d 个)%s\n", + strcat(agg[i].subnet, ".0.0.0/8"), C_RED, agg[i].count, C_RESET); + } else if (agg[i].mask == 16) { + printf(" - %-18s %s(%d 个)%s\n", + strcat(agg[i].subnet, ".0.0/16"), C_RED, agg[i].count, C_RESET); + } else if (agg[i].mask == 24) { + printf(" - %-18s %s(%d 个)%s\n", + strcat(agg[i].subnet, ".0/24"), C_RED, agg[i].count, C_RESET); } + show_count++; } - pclose(fp); } if (!has_output) { @@ -163,7 +214,6 @@ void show_subnet_aggregation(void) { printf(" - (IPv6 地址) (%d 个)\n", v6_count); } - remove(temp_v4_file); printf("\n"); } diff --git a/v3.1.sh b/v3.1.sh deleted file mode 100644 index 5efcce5..0000000 --- a/v3.1.sh +++ /dev/null @@ -1,742 +0,0 @@ -#!/bin/sh - -# ================= 配置区域 ================= -LOG_FILE="/var/log/block-ip.log" -MAX_LOG_SIZE=10485760 # 10MB (单位:字节) -MAX_RETRIES=3 -BAN_TIME="24h" - -RECORD_DIR="/tmp/block_ip_counts" -PERSIST_FILE="/etc/block-ip.list" -COUNTRY_FILE="/etc/block-ip.country" -WHITELIST_FILE="/etc/block-ip.whitelist" -INSTALL_PATH="/usr/local/bin/block-ip" -NFT_TABLE="inet filter" -NFT_SET="blacklist" -NFT_SET_V6="blacklist_v6" -NFT_WHITELIST="whitelist" -NFT_WHITELIST_V6="whitelist_v6" - -export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin -# =========================================== - -# --- 颜色 --- -C_RESET="\033[0m" -C_GREEN="\033[32m" -C_CYAN="\033[36m" -C_YELLOW="\033[33m" -C_RED="\033[31m" - -msg() { printf "%b%s%b\n" "$1" "$2" "$C_RESET"; } -log() { - rotate_log - echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE" -} -check_root() { [ "$(id -u)" -ne 0 ] && msg "$C_RED" "❌ 需 root 权限" && exit 1; } - -get_country_name() { - case "$1" in - CN) echo "中国" ;; - US) echo "美国" ;; - RU) echo "俄罗斯" ;; - NL) echo "荷兰" ;; - DE) echo "德国" ;; - GB) echo "英国" ;; - FR) echo "法国" ;; - JP) echo "日本" ;; - KR) echo "韩国" ;; - SG) echo "新加坡" ;; - HK) echo "香港" ;; - TW) echo "台湾" ;; - IN) echo "印度" ;; - BR) echo "巴西" ;; - CA) echo "加拿大" ;; - AU) echo "澳大利亚" ;; - IT) echo "意大利" ;; - ES) echo "西班牙" ;; - SE) echo "瑞典" ;; - PL) echo "波兰" ;; - UA) echo "乌克兰" ;; - TR) echo "土耳其" ;; - ID) echo "印度尼西亚" ;; - TH) echo "泰国" ;; - VN) echo "越南" ;; - MX) echo "墨西哥" ;; - AR) echo "阿根廷" ;; - CL) echo "智利" ;; - RO) echo "罗马尼亚" ;; - CZ) echo "捷克" ;; - *) echo "$1" ;; - esac -} - -rotate_log() { - [ ! -f "$LOG_FILE" ] && return - LOG_SIZE=$(stat -f%z "$LOG_FILE" 2>/dev/null || stat -c%s "$LOG_FILE" 2>/dev/null || echo 0) - if [ "$LOG_SIZE" -ge "$MAX_LOG_SIZE" ]; then - [ -f "${LOG_FILE}.1" ] && rm -f "${LOG_FILE}.1" - mv "$LOG_FILE" "${LOG_FILE}.1" - touch "$LOG_FILE" && chmod 666 "$LOG_FILE" - fi -} - -is_ipv6() { - # 移除CIDR后缀再判断 - IP="${1%%/*}" - echo "$IP" | grep -q ':' -} - -get_ip() { - if [ -n "$PAM_RHOST" ]; then echo "$PAM_RHOST" - elif [ -n "$RHOST" ]; then echo "$RHOST" - else echo ""; fi -} - -check_and_install_env() { - if ! command -v nft >/dev/null 2>&1; then - . /etc/os-release - case "$ID" in - debian|ubuntu|kali) apt-get update && apt-get install -y nftables ;; - centos|rhel|alma) dnf install -y nftables || yum install -y nftables ;; - alpine) apk add nftables ;; - *) return 1 ;; - esac - fi - nft list tables >/dev/null 2>&1 || modprobe nf_tables >/dev/null 2>&1 - [ -x "$(command -v systemctl)" ] && systemctl enable --now nftables >/dev/null 2>&1 - return 0 -} - -# --- 封禁 --- -ban_ip() { - TARGET_IP="$1" - SAVE_DISK="$2" - - # 标准化IP格式:单IP自动添加/32或/128 - case "$TARGET_IP" in - */*) ELEMENT="$TARGET_IP" ;; # 已包含CIDR - *:*) ELEMENT="$TARGET_IP/128" ;; # IPv6单IP - *) ELEMENT="$TARGET_IP/32" ;; # IPv4单IP - esac - - [ -n "$BAN_TIME" ] && ELEMENT="$ELEMENT timeout $BAN_TIME" - - # 根据IP类型选择不同的集合 - if is_ipv6 "$TARGET_IP"; then - SET_NAME="$NFT_SET_V6" - else - SET_NAME="$NFT_SET" - fi - - OUT=$(nft add element $NFT_TABLE $SET_NAME "{ $ELEMENT }" 2>&1) - if echo "$OUT" | grep -q "No such file"; then - init_nft_rules >/dev/null 2>&1 - nft add element $NFT_TABLE $SET_NAME "{ $ELEMENT }" >/dev/null 2>&1 - fi - - # 查询并记录国家信息(仅IPv4且不是CIDR) - COUNTRY_CODE="" - BASE_IP="${TARGET_IP%%/*}" - if [ "$SAVE_DISK" -eq 1 ] && ! is_ipv6 "$BASE_IP" && ! echo "$TARGET_IP" | grep -q '/'; then - if command -v curl >/dev/null 2>&1; then - COUNTRY_CODE=$(curl -s --max-time 2 "https://ipinfo.io/$BASE_IP/country" 2>/dev/null | tr -d '\n\r ') - if [ -n "$COUNTRY_CODE" ] && [ ${#COUNTRY_CODE} -eq 2 ]; then - # 检查是否已存在记录 - if ! grep -q "^$BASE_IP|" "$COUNTRY_FILE" 2>/dev/null; then - echo "$BASE_IP|$COUNTRY_CODE" >> "$COUNTRY_FILE" - fi - fi - fi - fi - - if [ "$SAVE_DISK" -eq 1 ]; then - if ! grep -q "^$TARGET_IP$" "$PERSIST_FILE" 2>/dev/null; then - echo "$TARGET_IP" >> "$PERSIST_FILE" - if [ -n "$COUNTRY_CODE" ]; then - COUNTRY_NAME=$(get_country_name "$COUNTRY_CODE") - log "[执行封禁] IP=$TARGET_IP 国家=$COUNTRY_NAME 已封禁" - else - log "[执行封禁] IP=$TARGET_IP 已封禁" - fi - fi - elif [ "$SAVE_DISK" -ne 2 ]; then - log "[执行封禁] IP=$TARGET_IP 已封禁" - fi -} - -init_nft_rules() { - nft add table $NFT_TABLE 2>/dev/null - # 创建黑名单集合 - nft add set $NFT_TABLE $NFT_SET "{ type ipv4_addr; flags interval,timeout; }" 2>/dev/null - nft add set $NFT_TABLE $NFT_SET_V6 "{ type ipv6_addr; flags interval,timeout; }" 2>/dev/null - # 创建白名单集合(无超时) - nft add set $NFT_TABLE $NFT_WHITELIST "{ type ipv4_addr; flags interval; }" 2>/dev/null - nft add set $NFT_TABLE $NFT_WHITELIST_V6 "{ type ipv6_addr; flags interval; }" 2>/dev/null - nft add chain $NFT_TABLE input "{ type filter hook input priority 0; }" 2>/dev/null - # 白名单规则(优先级最高,先匹配先返回) - nft list chain $NFT_TABLE input | grep -q "@$NFT_WHITELIST" || \ - nft insert rule $NFT_TABLE input ip saddr @"$NFT_WHITELIST" accept - nft list chain $NFT_TABLE input | grep -q "@$NFT_WHITELIST_V6" || \ - nft insert rule $NFT_TABLE input ip6 saddr @"$NFT_WHITELIST_V6" accept - # 黑名单规则 - nft list chain $NFT_TABLE input | grep -q "@$NFT_SET" || \ - nft insert rule $NFT_TABLE input ip saddr @"$NFT_SET" drop - nft list chain $NFT_TABLE input | grep -q "@$NFT_SET_V6" || \ - nft insert rule $NFT_TABLE input ip6 saddr @"$NFT_SET_V6" drop -} - -# --- 列表与统计 --- -do_list() { - # 数据获取与清洗 - RAW_V4=$(nft list set $NFT_TABLE $NFT_SET 2>/dev/null) - RAW_V6=$(nft list set $NFT_TABLE $NFT_SET_V6 2>/dev/null) - RAW="$RAW_V4 -$RAW_V6" - - CLEAN_DATA=$(echo "$RAW" | sed 's/,/\n/g' | sed 's/elements = {//g; s/}//g' | \ - awk '{ - for(i=1;i<=NF;i++) { - if($i=="expires") { - time=$(i+1); gsub("ms","",time) - print $1, time - } - } - }') - - IP_LIST=$(echo "$CLEAN_DATA" | awk '{print $1}') - IP_V4_LIST=$(echo "$IP_LIST" | grep -v ':' || true) - IP_V6_LIST=$(echo "$IP_LIST" | grep ':' || true) - NFT_COUNT=0 - [ -n "$CLEAN_DATA" ] && NFT_COUNT=$(echo "$CLEAN_DATA" | awk 'NF>0' | wc -l) - if [ -f "$PERSIST_FILE" ]; then LOCAL_COUNT=$(wc -l < "$PERSIST_FILE"); else LOCAL_COUNT=0; fi - - # 概览 - msg "$C_CYAN" "=== 🛡️ Block-IP 防护概览 ===" - printf "当前生效: %b%s%b 条 | 本地记录: %b%s%b 条\n" "$C_GREEN" "$NFT_COUNT" "$C_RESET" "$C_YELLOW" "$LOCAL_COUNT" "$C_RESET" - echo "" - - # 活跃列表(按剩余时间升序,显示最新封禁的5个) - msg "$C_CYAN" "=== 🔥 活跃封禁列表 (最新 5 条) ===" - if [ "$NFT_COUNT" -eq 0 ]; then - echo "(目前没有被封禁的 IP)" - else - printf "%b%-45s %-15s%b\n" "$C_YELLOW" "IP 地址" "剩余时间" "$C_RESET" - echo "--------------------------------------------------------------" - echo "$CLEAN_DATA" | sort -t' ' -k2 | tail -n 5 | awk '{printf "%-45s %s\n", $1, $2}' - [ "$NFT_COUNT" -gt 5 ] && echo "... (还有 $((NFT_COUNT - 5)) 条未显示)" - fi - echo "" - - # 智能IP段聚合统计 - msg "$C_CYAN" "=== 📊 攻击源聚合统计 (自动识别 IP 段) ===" - - if [ "$NFT_COUNT" -gt 0 ]; then - V6_COUNT=0 - [ -n "$IP_V6_LIST" ] && V6_COUNT=$(echo "$IP_V6_LIST" | awk 'NF>0' | wc -l) - HAS_OUTPUT=0 - - # 收集聚合数据 - TEMP_AGG_FILE="/tmp/block_ip_agg_$$" - : > "$TEMP_AGG_FILE" - - # 收集 /24 聚合 - echo "$IP_V4_LIST" | cut -d. -f1-3 | sort | uniq -c | awk '$1>=2 {split($2,a,"."); printf "%d|%s|24|%s\n", $1, $2, a[1]}' >> "$TEMP_AGG_FILE" - # 收集 /16 聚合 - echo "$IP_V4_LIST" | cut -d. -f1-2 | sort | uniq -c | awk '$1>=2 {split($2,a,"."); printf "%d|%s|16|%s\n", $1, $2, a[1]}' >> "$TEMP_AGG_FILE" - # 收集 /8 聚合 - echo "$IP_V4_LIST" | cut -d. -f1 | sort | uniq -c | awk '$1>=2 {printf "%d|%s|8|%s\n", $1, $2, $2}' >> "$TEMP_AGG_FILE" - - # 去重: 子段数量等于父段时隐藏父段 - TEMP_FILTER="/tmp/block_ip_filter_$$" - : > "$TEMP_FILTER" - - while IFS='|' read -r count subnet mask a_seg; do - [ -z "$count" ] && continue - SKIP=0 - - case "$mask" in - 8) - if grep -E "^$count\|$subnet\.[0-9]+(\.[0-9]+)?\|(16|24)\|" "$TEMP_AGG_FILE" >/dev/null 2>&1; then - SKIP=1 - fi - ;; - 16) - if grep -E "^$count\|$subnet\.[0-9]+\|24\|" "$TEMP_AGG_FILE" >/dev/null 2>&1; then - SKIP=1 - fi - ;; - esac - - [ "$SKIP" -eq 0 ] && echo "$count|$subnet|$mask|$a_seg" >> "$TEMP_FILTER" - done < "$TEMP_AGG_FILE" - - # 按数量降序,然后按A段分组,最后按掩码升序(同组内大段优先) - SORTED_AGGS=$(sort -t'|' -k1,1rn -k4,4n -k3,3n "$TEMP_FILTER") - rm -f "$TEMP_AGG_FILE" "$TEMP_FILTER" - - # 输出所有聚合并收集已统计的子网 - TEMP_SUBNETS="/tmp/block_ip_subnets_$$" - : > "$TEMP_SUBNETS" - - if [ -n "$SORTED_AGGS" ]; then - echo "$SORTED_AGGS" | while IFS='|' read -r count subnet mask _; do - [ -z "$count" ] && continue - - # 输出该段 - case "$mask" in - 8) printf " - %-18s %b(%s 个)%b\n" "${subnet}.0.0.0/8" "$C_RED" "$count" "$C_RESET" ;; - 16) printf " - %-18s %b(%s 个)%b\n" "${subnet}.0.0/16" "$C_RED" "$count" "$C_RESET" ;; - 24) printf " - %-18s %b(%s 个)%b\n" "${subnet}.0/24" "$C_RED" "$count" "$C_RESET" ;; - esac - - echo "$subnet" >> "$TEMP_SUBNETS" - done - HAS_OUTPUT=1 - fi - - # 统计未被任何段包含的散乱IP - REMAIN_LIST="$IP_V4_LIST" - if [ -f "$TEMP_SUBNETS" ] && [ -s "$TEMP_SUBNETS" ]; then - while IFS= read -r subnet; do - [ -n "$subnet" ] && REMAIN_LIST=$(echo "$REMAIN_LIST" | grep -v "^$subnet\." || true) - done < "$TEMP_SUBNETS" - fi - rm -f "$TEMP_SUBNETS" - - # 散乱IP统计 - REMAIN_COUNT=0 - if [ -n "$REMAIN_LIST" ]; then - REMAIN_COUNT=$(echo "$REMAIN_LIST" | awk 'NF>0' | wc -l) - fi - - if [ "$HAS_OUTPUT" -eq 1 ]; then - [ "$REMAIN_COUNT" -gt 0 ] && echo " - (其他散乱分布 IPv4) ($REMAIN_COUNT 个)" - else - echo "(无数据)" - fi - - # IPv6统计 - if [ "$V6_COUNT" -gt 0 ]; then - echo " - (IPv6 地址) ($V6_COUNT 个)" - fi - else - echo "(无数据)" - fi - - echo "" - - # 国家统计 - msg "$C_CYAN" "=== 🌍 攻击源国家/地区统计 ===" - - if [ -f "$COUNTRY_FILE" ] && [ -s "$COUNTRY_FILE" ]; then - # 直接统计country文件中的国家代码 - cut -d'|' -f2 "$COUNTRY_FILE" | sort | uniq -c | sort -rn | while read -r count code; do - [ -n "$count" ] && [ -n "$code" ] && { - COUNTRY_NAME=$(get_country_name "$code") - printf " - %-15s %b(%s 个)%b\n" "$COUNTRY_NAME" "$C_RED" "$count" "$C_RESET" - } - done - else - echo "(暂无国家信息)" - fi - - echo "" - - # 最新日志 - msg "$C_CYAN" "=== 📝 最新拦截日志 (Last 10) ===" - if [ -f "$LOG_FILE" ]; then tail -n 10 "$LOG_FILE"; else echo "(暂无日志)"; fi -} - -do_show() { - msg "$C_CYAN" "=== 📋 本地持久化封禁列表 ===" - - if [ ! -f "$PERSIST_FILE" ] || [ ! -s "$PERSIST_FILE" ]; then - echo "(暂无持久化记录)" - return - fi - - TOTAL=$(wc -l < "$PERSIST_FILE") - IPV4_COUNT=$(grep -c -v ':' "$PERSIST_FILE" 2>/dev/null || echo 0) - IPV6_COUNT=$(grep -c ':' "$PERSIST_FILE" 2>/dev/null || echo 0) - - printf "总计: %b%s%b 条 | IPv4: %b%s%b 条 | IPv6: %b%s%b 条\n\n" \ - "$C_GREEN" "$TOTAL" "$C_RESET" \ - "$C_CYAN" "$IPV4_COUNT" "$C_RESET" \ - "$C_YELLOW" "$IPV6_COUNT" "$C_RESET" - - printf "%b%-45s%b\n" "$C_YELLOW" "IP 地址" "$C_RESET" - echo "---------------------------------------------" - - awk '{printf "%-45s\n", $1}' "$PERSIST_FILE" - echo "" - - msg "$C_CYAN" "📌 文件位置: $PERSIST_FILE" -} - -do_vip_add() { - INPUT="$1" - # 验证输入格式 - case "$INPUT" in - */*) # CIDR格式 - IP="${INPUT%%/*}" - MASK="${INPUT##*/}" - if ! echo "$MASK" | grep -qE '^[0-9]+$'; then - msg "$C_RED" "❌ 无效的CIDR格式: $INPUT" - exit 1 - fi - ;; - *:*|*.*.*.*) # IPv6或IPv4单IP - ;; - *) - msg "$C_RED" "❌ 无效的IP格式: $INPUT" - exit 1 - ;; - esac - - # 标准化格式 - case "$INPUT" in - */*) ELEMENT="$INPUT" ;; # 已包含CIDR - *:*) ELEMENT="$INPUT/128" ;; # IPv6单IP - *) ELEMENT="$INPUT/32" ;; # IPv4单IP - esac - - # 添加到nftables白名单 - if is_ipv6 "$INPUT"; then - SET_NAME="$NFT_WHITELIST_V6" - else - SET_NAME="$NFT_WHITELIST" - fi - - OUT=$(nft add element $NFT_TABLE $SET_NAME "{ $ELEMENT }" 2>&1) - if echo "$OUT" | grep -q "No such file"; then - init_nft_rules >/dev/null 2>&1 - nft add element $NFT_TABLE $SET_NAME "{ $ELEMENT }" >/dev/null 2>&1 - fi - - # 保存到持久化文件 - if ! grep -q "^$INPUT$" "$WHITELIST_FILE" 2>/dev/null; then - echo "$INPUT" >> "$WHITELIST_FILE" - fi - - log "[白名单添加] IP=$INPUT" - msg "$C_GREEN" "✅ 已添加到白名单: $INPUT" -} - -do_vip_del() { - INPUT="$1" - - # 标准化格式 - case "$INPUT" in - */*) DEL_ELEMENT="$INPUT" ;; # 已包含CIDR - *:*) DEL_ELEMENT="$INPUT/128" ;; # IPv6单IP - *) DEL_ELEMENT="$INPUT/32" ;; # IPv4单IP - esac - - # 从nftables删除 - if is_ipv6 "$INPUT"; then - nft delete element $NFT_TABLE $NFT_WHITELIST_V6 "{ $DEL_ELEMENT }" >/dev/null 2>&1 - else - nft delete element $NFT_TABLE $NFT_WHITELIST "{ $DEL_ELEMENT }" >/dev/null 2>&1 - fi - - # 从持久化文件删除 - if [ -f "$WHITELIST_FILE" ]; then - ESCAPED=$(echo "$INPUT" | sed 's/[.[\/]/\\&/g') - sed -i "/^$ESCAPED$/d" "$WHITELIST_FILE" - fi - - log "[白名单移除] IP=$INPUT" - msg "$C_GREEN" "✅ 已从白名单移除: $INPUT" -} - -do_vip_list() { - msg "$C_CYAN" "=== 📋 VIP 白名单列表 ===" - - if [ ! -f "$WHITELIST_FILE" ] || [ ! -s "$WHITELIST_FILE" ]; then - echo "(暂无白名单记录)" - return - fi - - TOTAL=$(wc -l < "$WHITELIST_FILE") - IPV4_COUNT=$(grep -c -v ':' "$WHITELIST_FILE" 2>/dev/null || echo 0) - IPV6_COUNT=$(grep -c ':' "$WHITELIST_FILE" 2>/dev/null || echo 0) - - printf "总计: %b%s%b 条 | IPv4: %b%s%b 条 | IPv6: %b%s%b 条\n\n" \ - "$C_GREEN" "$TOTAL" "$C_RESET" \ - "$C_CYAN" "$IPV4_COUNT" "$C_RESET" \ - "$C_YELLOW" "$IPV6_COUNT" "$C_RESET" - - printf "%b%-45s%b\n" "$C_YELLOW" "IP 地址" "$C_RESET" - echo "---------------------------------------------" - - awk '{printf "%-45s\n", $1}' "$WHITELIST_FILE" - echo "" - - msg "$C_CYAN" "📌 文件位置: $WHITELIST_FILE" -} - -do_add() { - INPUT="$1" - # 验证输入格式 - case "$INPUT" in - */*) - # CIDR 格式验证 - IP="${INPUT%%/*}" - MASK="${INPUT##*/}" - if ! echo "$MASK" | grep -qE '^[0-9]+$'; then - msg "$C_RED" "❌ 无效的CIDR格式: $INPUT" - exit 1 - fi - ;; - *:*|*.*.*.*) - # IPv6 或 IPv4 单IP - ;; - *) - msg "$C_RED" "❌ 无效的IP格式: $INPUT" - exit 1 - ;; - esac - - ban_ip "$INPUT" 1 - msg "$C_GREEN" "✅ 已封禁: $INPUT" -} -do_del() { - INPUT="$1" - - # 标准化IP格式:单IP自动添加/32或/128 - case "$INPUT" in - */*) DEL_ELEMENT="$INPUT" ;; # 已包含CIDR - *:*) DEL_ELEMENT="$INPUT/128" ;; # IPv6单IP - *) DEL_ELEMENT="$INPUT/32" ;; # IPv4单IP - esac - - if is_ipv6 "$INPUT"; then - nft delete element $NFT_TABLE $NFT_SET_V6 "{ $DEL_ELEMENT }" >/dev/null 2>&1 - else - nft delete element $NFT_TABLE $NFT_SET "{ $DEL_ELEMENT }" >/dev/null 2>&1 - fi - - # 从持久化文件删除(支持原始输入格式) - if [ -f "$PERSIST_FILE" ]; then - # 转义特殊字符用于sed - ESCAPED=$(echo "$INPUT" | sed 's/[.[\/]/\\&/g') - sed -i "/^$ESCAPED$/d" "$PERSIST_FILE" - fi - - log "[手动解封] IP=$INPUT" - msg "$C_GREEN" "✅ 已解封: $INPUT" -} -do_restore() { - check_and_install_env; init_nft_rules - - # 恢复黑名单 - if [ -f "$PERSIST_FILE" ]; then - count=0 - while IFS= read -r ip; do [ -n "$ip" ] && ban_ip "$ip" 2 && count=$((count+1)); done < "$PERSIST_FILE" - log "[系统恢复] 已从磁盘恢复 $count 个黑名单 IP" - msg "$C_GREEN" "✅ 已从磁盘恢复 $count 个黑名单 IP" - fi - - # 恢复白名单 - if [ -f "$WHITELIST_FILE" ]; then - wcount=0 - while IFS= read -r ip; do - if [ -n "$ip" ]; then - case "$ip" in - */*) ELEMENT="$ip" ;; - *:*) ELEMENT="$ip/128" ;; - *) ELEMENT="$ip/32" ;; - esac - - if is_ipv6 "$ip"; then - nft add element $NFT_TABLE $NFT_WHITELIST_V6 "{ $ELEMENT }" 2>/dev/null && wcount=$((wcount+1)) - else - nft add element $NFT_TABLE $NFT_WHITELIST "{ $ELEMENT }" 2>/dev/null && wcount=$((wcount+1)) - fi - fi - done < "$WHITELIST_FILE" - log "[系统恢复] 已从磁盘恢复 $wcount 个白名单 IP" - msg "$C_GREEN" "✅ 已从磁盘恢复 $wcount 个白名单 IP" - fi -} - -do_install() { - check_root - CURRENT=$(readlink -f "$0" 2>/dev/null || echo "$0") - if [ "$CURRENT" != "$INSTALL_PATH" ]; then cp "$0" "$INSTALL_PATH" && chmod +x "$INSTALL_PATH"; fi - mkdir -p "$RECORD_DIR" && chmod 700 "$RECORD_DIR" - touch "$PERSIST_FILE" && chmod 600 "$PERSIST_FILE" - touch "$COUNTRY_FILE" && chmod 600 "$COUNTRY_FILE" - touch "$LOG_FILE" && chmod 666 "$LOG_FILE" - check_and_install_env; init_nft_rules; do_restore - PAM_FILE="/etc/pam.d/sshd" - sed -i "\|$INSTALL_PATH|d" "$PAM_FILE" - sed -i "1s|^|auth optional pam_exec.so quiet $INSTALL_PATH check\n|" "$PAM_FILE" - echo "session optional pam_exec.so quiet $INSTALL_PATH clean" >> "$PAM_FILE" - cat > "/etc/systemd/system/block-ip.service" </dev/null - systemctl disable block-ip.service 2>/dev/null - rm -f "/etc/systemd/system/block-ip.service" - systemctl daemon-reload - msg "$C_GREEN" " ✓ 已移除 systemd 服务" - fi - - # 清除 nftables 规则 - nft delete rule $NFT_TABLE input ip saddr @"$NFT_SET" drop 2>/dev/null - nft delete rule $NFT_TABLE input ip6 saddr @"$NFT_SET_V6" drop 2>/dev/null - nft delete rule $NFT_TABLE input ip saddr @"$NFT_WHITELIST" accept 2>/dev/null - nft delete rule $NFT_TABLE input ip6 saddr @"$NFT_WHITELIST_V6" accept 2>/dev/null - nft delete set $NFT_TABLE $NFT_SET 2>/dev/null - nft delete set $NFT_TABLE $NFT_SET_V6 2>/dev/null - nft delete set $NFT_TABLE $NFT_WHITELIST 2>/dev/null - nft delete set $NFT_TABLE $NFT_WHITELIST_V6 2>/dev/null - msg "$C_GREEN" " ✓ 已清除防火墙规则" - - # 移除 PAM 配置 - PAM_FILE="/etc/pam.d/sshd" - if [ -f "$PAM_FILE" ]; then - sed -i "\|$INSTALL_PATH|d" "$PAM_FILE" - msg "$C_GREEN" " ✓ 已移除 PAM 钩子" - fi - - # 删除文件 (可选保留日志和封禁列表) - printf "是否删除封禁列表和日志? [y/N] " - read -r REPLY - if [ "$REPLY" = "y" ] || [ "$REPLY" = "Y" ]; then - rm -f "$PERSIST_FILE" "$LOG_FILE" "${LOG_FILE}.1" - msg "$C_GREEN" " ✓ 已删除数据文件" - else - msg "$C_CYAN" " ↳ 保留: $PERSIST_FILE, $LOG_FILE" - fi - - rm -rf "$RECORD_DIR" - rm -f "$INSTALL_PATH" - msg "$C_GREEN" " ✓ 已删除程序文件" - - msg "$C_GREEN" "\n✅ 卸载完成!" -} - -do_check() { - THE_IP=$(get_ip) - [ -z "$THE_IP" ] && return - - # 检查白名单 - if [ -f "$WHITELIST_FILE" ]; then - while IFS= read -r wip; do - [ -z "$wip" ] && continue - # 单IP精确匹配 - if [ "$THE_IP" = "$wip" ]; then - log "[白名单放行] IP=$THE_IP" - return - fi - # CIDR匹配(通过nftables集合) - case "$wip" in - */*) - PREFIX="${wip%%/*}" - MASK="${wip##*/}" - # 简化匹配:/8匹配第一段,/16匹配前两段,/24匹配前三段 - case "$MASK" in - 8) - A="${PREFIX%%.*.*.*}" - if echo "$THE_IP" | grep -q "^$A\."; then - log "[白名单放行] IP=$THE_IP 匹配白名单 $wip" - return - fi - ;; - 16) - AB="${PREFIX%.*.*}" - if echo "$THE_IP" | grep -q "^$AB\."; then - log "[白名单放行] IP=$THE_IP 匹配白名单 $wip" - return - fi - ;; - 24) - ABC="${PREFIX%.*}" - if echo "$THE_IP" | grep -q "^$ABC\."; then - log "[白名单放行] IP=$THE_IP 匹配白名单 $wip" - return - fi - ;; - esac - ;; - esac - done < "$WHITELIST_FILE" - fi - - [ ! -d "$RECORD_DIR" ] && mkdir -p "$RECORD_DIR" && chmod 700 "$RECORD_DIR" - IP_FILE="$RECORD_DIR/$THE_IP" - COUNT=0 - [ -f "$IP_FILE" ] && COUNT=$(cat "$IP_FILE") - COUNT=$((COUNT + 1)) - log "[验证失败] IP=$THE_IP (第 $COUNT/$MAX_RETRIES 次)" - if [ "$COUNT" -ge "$MAX_RETRIES" ]; then ban_ip "$THE_IP" 1; rm -f "$IP_FILE"; else echo "$COUNT" > "$IP_FILE"; fi -} - -do_clean() { - THE_IP=$(get_ip) - if [ -n "$THE_IP" ] && [ -f "$RECORD_DIR/$THE_IP" ]; then - log "[登录成功] IP=$THE_IP (计数已重置)" - rm -f "$RECORD_DIR/$THE_IP" - fi -} - -show_help() { - echo "Block-IP v16.2 (IPv6 + CIDR + Whitelist)" - echo "--------------------------------------" - echo "使用方法:" - echo " block-ip list 查看实时统计/活跃列表/日志" - echo " block-ip show 显示本地持久化封禁列表" - echo " block-ip add 手动封禁 IP (支持IPv4/IPv6/CIDR)" - echo " 示例: 1.1.1.1 或 1.1.1.0/24 或 2001:db8::/32" - echo " block-ip del 手动解封 IP (支持IPv4/IPv6/CIDR)" - echo " block-ip vip add 添加IP到白名单 (支持IPv4/IPv6/CIDR)" - echo " block-ip vip del 从白名单移除IP" - echo " block-ip vip list 显示白名单列表" - echo " block-ip restore 从持久化文件恢复黑白名单" - echo " block-ip install 安装/重装服务" - echo " block-ip uninstall 卸载服务" - echo "--------------------------------------" -} - -case "$1" in - check) do_check ;; - clean) do_clean ;; - list) do_list ;; - show) do_show ;; - vip) - case "$2" in - add) do_vip_add "$3" ;; - del) do_vip_del "$3" ;; - list) do_vip_list ;; - *) msg "$C_RED" "用法: block-ip vip {add|del|list} "; exit 1 ;; - esac - ;; - add) do_add "$2" ;; - del) do_del "$2" ;; - restore) do_restore ;; - install) do_install ;; - uninstall) do_uninstall ;; - "") show_help; exit 1 ;; - *) if [ -n "$RHOST" ] || [ -n "$PAM_RHOST" ]; then do_check; exit 0; fi - show_help; exit 1 ;; -esac