功能简介

本教程分为两个脚本,可单独使用或组合使用。

流量监控脚本

该脚本可用于监控Linux服务器的网络流量,并根据设定的流量阈值自动执行相应操作:

  • 流量达到限制时,仅允许SSH(端口22)访问;
  • 流量达到更高阈值时,自动关机;
  • 支持每日自动流量重置;
  • 支持多种流量检查方式。

所有参数均为可选,未指定时将使用默认值。

参数说明

参数名作用说明默认值
LIMIT\_GB流量限制(仅允许SSH),单位GB190
LIMIT\_GB\_POWEROFF流量限制(达到阈值关机),单位GB195
reset\_day流量重置日(每月第几天重置)1
CHECK\_TYPE流量检查类型(见下方详细说明)3
INTERFACE网络接口(如 eth0),默认自动检测系统默认

流量检查类型说明:

  • 1:只检查上传流量
  • 2:只检查下载流量
  • 3:检查上传和下载流量中的最大值
  • 4:检查上传和下载流量的总和

依赖安装

脚本运行需依赖以下工具:

  • vnstat:网络流量监控工具
  • iptables:Linux防火墙配置工具
  • bc:任意精度计算器语言

说明:

  • -y 参数表示在安装过程中自动回答所有提示为 'yes'

Debian/Ubuntu系统:

apt update && apt install -y iptables bc vnstat

CentOS/RPM系统:

yum update && yum install iptables bc vnstat

创建脚本文件并编辑

以 /root/ 目录为例,脚本名建议设置为 traffic\_monitor.sh。

cd /root && vim traffic_monitor.sh

脚本源码示例

以下为完整脚本源码,建议根据实际需求修改部分参数(如Bark通知KEY等)。

#!/bin/bash



LIMIT_GB=${1:-1950}
LIMIT_GB_POWEROFF=${2:-1980}
reset_day=${3:-1}
CHECK_TYPE=${4:-3}
INTERFACE=${5:-$(ip route | grep default | awk '{print $5}')}

LIMIT=$(echo "$LIMIT_GB * 1024" | bc)
LIMIT_POWEROFF=$(echo "$LIMIT_GB_POWEROFF * 1024" | bc)

echo "防火墙流量限制:$LIMIT MiB"
echo "关机流量限制:$LIMIT_POWEROFF MiB"
echo "流量将在每月的第 $reset_day 天重置"

current_day=$(date +'%-d')
last_day_of_month=$(date -d "$(date +'%Y%m01') +1 month -1 day" +%d)

# Bark设备的服务器地址及唯一key,需要替换成你自己的服务器地址及key
BARK_KEY="http://api.day.app/xxxxxxxxxxxxx"

# 流量达到阈值时创建临时文件目录
NOTIFY_FILE="/tmp/traffic_limit_notified"

if [ "$current_day" -eq "$reset_day" ] || ([ "$reset_day" -gt "$last_day_of_month" ] && [ "$current_day" -eq "$last_day_of_month" ]); then
  if [ ! -f "/tmp/vnstat_reset" ]; then
    touch /tmp/vnstat_reset
    rm /var/lib/vnstat/*
    sudo systemctl restart vnstat
    echo "流量已经重置,下次重置将在下个月的第 $reset_day 天"
  else
    echo "今天已经进行过流量重置,无需再次重置"
  fi
else
  if [ -f "/tmp/vnstat_reset" ]; then
    rm /tmp/vnstat_reset
  fi
  if [ "$current_day" -lt "$reset_day" ]; then
    days_until_reset=$(($reset_day - $current_day))
    echo "还有 $days_until_reset 天流量将会重置"
  else
    days_until_reset=$(( $last_day_of_month - $current_day + $reset_day ))
    echo "还有 $days_until_reset 天流量将会重置"
  fi
fi

if [ -z "$INTERFACE" ]; then
  echo "错误:无法自动检测网络接口。请手动指定。"
  exit 1
fi

echo "正在监控的网络接口:$INTERFACE"

DATA=$(vnstat -i $INTERFACE --oneline)
CURRENT_DATE=$(echo $DATA | cut -d ';' -f 8)
TRAFFIC_RX=$(echo $DATA | cut -d ';' -f 13 | tr -d ' ' | sed 's/MiB//;s/GiB/*1024/;s/KiB/\/1024/' | bc)
TRAFFIC_TX=$(echo $DATA | cut -d ';' -f 14 | tr -d ' ' | sed 's/MiB//;s/GiB/*1024/;s/KiB/\/1024/' | bc)

echo "当前月份:$CURRENT_DATE"

if [ "$CHECK_TYPE" = "1" ]; then
  TRAFFIC_TO_CHECK=$TRAFFIC_TX
  echo "只检查上传流量。当前上传流量为:$TRAFFIC_TX MiB。"
elif [ "$CHECK_TYPE" = "2" ]; then
  TRAFFIC_TO_CHECK=$TRAFFIC_RX
  echo "只检查下载流量。当前下载流量为:$TRAFFIC_RX MiB。"
elif [ "$CHECK_TYPE" = "3" ]; then
  TRAFFIC_TO_CHECK=$(echo "$TRAFFIC_TX $TRAFFIC_RX" | awk '{print ($1>$2)?$1:$2}')
  if [ "$TRAFFIC_TO_CHECK" = "$TRAFFIC_TX" ]; then
    echo "当前上传流量为:$TRAFFIC_TX MiB,下载流量为:$TRAFFIC_RX MiB。"
    echo "当前最大的流量是:上传流量。"
  else
    echo "当前上传流量为:$TRAFFIC_TX MiB,下载流量为:$TRAFFIC_RX MiB。"
    echo "当前最大的流量是:下载流量。"
  fi
elif [ "$CHECK_TYPE" = "4" ]; then
  TRAFFIC_TO_CHECK=$(echo "$TRAFFIC_TX + $TRAFFIC_RX" | bc)
  echo "检查上传和下载流量的总和。当前上传流量为:$TRAFFIC_TX MiB,下载流量为:$TRAFFIC_RX MiB。"
  echo "上传和下载流量的总和($TRAFFIC_TO_CHECK MiB)。"
else
  echo "错误:未提供有效的流量检查参数。参数应为1(只检查上传流量)、2(只检查下载流量)、3(检查上传和下载流量中的最大值)或4(检查上传和下载流量的总和)。"
  exit 1
fi

# 固定时间发送当前流量通知
send_daily_traffic_notification() {
  local current_hour=$(date +'%H')
  local current_minute=$(date +'%M')
  # 检查当前时间是否为 18:00
  if [ "$current_hour" -eq "18" ] && [ "$current_minute" -eq "00" ]; then
    local TRAFFIC_RX_MB=$(echo "scale=2; $TRAFFIC_RX / 1024" | bc)
    local TRAFFIC_TX_MB=$(echo "scale=2; $TRAFFIC_TX / 1024" | bc)

    curl -s -X POST "${BARK_KEY}/华为SG111/每日流量汇报:当前上传流量为%20$TRAFFIC_TX_MB%20GiB,下载流量为%20$TRAFFIC_RX_MB%20GiB。?group=VPS&icon=https://pic2.ziyuan.wang/user/biu/2024/09/icon_cloud_cfb765ae0df8b.jpg"

    echo "每日流量通知已发送:上传流量 $TRAFFIC_TX_MB GiB,下载流量 $TRAFFIC_RX_MB GiB。"
  fi
}

# 调用函数发送流量通知
send_daily_traffic_notification

if (( $(echo "$TRAFFIC_TO_CHECK > $LIMIT" | bc -l) )); then
  if [ ! -f "$NOTIFY_FILE" ]; then
    curl -s -X POST "${BARK_KEY}/华为SG111/警告:流量已超出限制!除SSH(端口22)外,所有端口已被阻止!?group=VPS&icon=https://pic2.ziyuan.wang/user/biu/2024/09/icon_cloud_cfb765ae0df8b.jpg"
    echo "已发送流量超出通知。"
    touch "$NOTIFY_FILE" # 创建通知文件,避免重复发送
  else
    echo "流量超出通知已发送,不再重复发送。"
  fi

  # --- 修改部分:流量超限开启“铁桶阵” ---
  BLOCK_CHAIN="STRICT_TRAFFIC_BLOCK"
  /usr/sbin/iptables -N "$BLOCK_CHAIN" 2>/dev/null
  /usr/sbin/iptables -F "$BLOCK_CHAIN"
  /usr/sbin/iptables -A "$BLOCK_CHAIN" -i lo -j ACCEPT
  /usr/sbin/iptables -A "$BLOCK_CHAIN" -o lo -j ACCEPT
  /usr/sbin/iptables -A "$BLOCK_CHAIN" -p tcp --dport 22 -j ACCEPT
  /usr/sbin/iptables -A "$BLOCK_CHAIN" -p tcp --sport 22 -j ACCEPT
  /usr/sbin/iptables -A "$BLOCK_CHAIN" -j DROP

  /usr/sbin/iptables -C INPUT -j "$BLOCK_CHAIN" 2>/dev/null || /usr/sbin/iptables -I INPUT 1 -j "$BLOCK_CHAIN"
  /usr/sbin/iptables -C OUTPUT -j "$BLOCK_CHAIN" 2>/dev/null || /usr/sbin/iptables -I OUTPUT 1 -j "$BLOCK_CHAIN"
  /usr/sbin/iptables -C FORWARD -j "$BLOCK_CHAIN" 2>/dev/null || /usr/sbin/iptables -I FORWARD 1 -j "$BLOCK_CHAIN"
  # ----------------------------------------------------------

  echo "警告:流量已超出限制!除SSH(端口22)外,所有端口已被阻止。"
else
  if [ -f "$NOTIFY_FILE" ]; then
    rm "$NOTIFY_FILE" # 如果流量回到限制内,删除通知文件
    curl -s -X POST "${BARK_KEY}/华为SG111/警告:流量已回到限制内!放开所有限制!?group=VPS&icon=https://pic2.ziyuan.wang/user/biu/2024/09/icon_cloud_cfb765ae0df8b.jpg"
    echo "流量已回到限制内,移除通知文件。"
  fi

  # --- 修改部分:流量恢复正常,撤销拦截开关,执行防护脚本 ---
  BLOCK_CHAIN="STRICT_TRAFFIC_BLOCK"
  /usr/sbin/iptables -D INPUT -j "$BLOCK_CHAIN" 2>/dev/null
  /usr/sbin/iptables -D OUTPUT -j "$BLOCK_CHAIN" 2>/dev/null
  /usr/sbin/iptables -D FORWARD -j "$BLOCK_CHAIN" 2>/dev/null
  /usr/sbin/iptables -F "$BLOCK_CHAIN" 2>/dev/null
  /usr/sbin/iptables -X "$BLOCK_CHAIN" 2>/dev/null

  # 执行同目录下的地区防护脚本
  SCRIPT_DIR=$(cd $(dirname $0); pwd)
  FIREWALL_SCRIPT="$SCRIPT_DIR/secure_firewall.sh"
  if [ -f "$FIREWALL_SCRIPT" ]; then
      bash "$FIREWALL_SCRIPT" > /dev/null
      echo "流量在限制内,已应用地区防护规则。"
  else
      echo "流量在设定的限制内,所有流量都被允许。"
  fi
  # ----------------------------------------------------------
fi

if (( $(echo "$TRAFFIC_TO_CHECK > $LIMIT_POWEROFF" | bc -l) )); then
  curl -s -X POST "${BARK_KEY}/华为SG111/警告:流量已超出最大限制!将立即关机!?group=VPS&icon=https://pic2.ziyuan.wang/user/biu/2024/09/icon_cloud_cfb765ae0df8b.jpg"
  echo "已发送流量超出通知。"
  echo "警告:流量已超出限制!立即关机"
  /usr/sbin/shutdown -h now
fi

脚本权限设置

运行前需为脚本添加执行权限:

chmod +x traffic_monitor.sh

使用示例

  1. 使用所有默认参数运行:
./traffic_monitor.sh
  1. 设置流量限制为500GB,其他参数默认:
./traffic_monitor.sh 500
  1. 设置流量限制为500GB,关机阈值510GB,每月10号重置:
./traffic_monitor.sh 500 510 10
  1. 设置流量限制为500GB,关机阈值510GB,每月10号重置,只检查下载流量,并指定网络接口为eth0:
./traffic_monitor.sh 500 510 10 2 eth0

自动定时运行

可通过 crontab 实现脚本定时运行。例如每分钟执行一次,并将脚本输出重定向到 /root/traffic\_monitor.log 文件:

(crontab -l ; echo "* * * * * /bin/bash /root/traffic_monitor.sh 190 195 1 3 eth0 >> /root/traffic_monitor.log 2>&1") | crontab -
  • * * * * *:每分钟执行一次
  • 可修改为 */5 * * * *(每5分钟)或 30 * * * *(每小时第30分钟)
  • 日志输出至 /root/traffic\_monitor.log

补充说明

  • crontab目录(Debian): 编辑定时任务可通过 vim /var/spool/cron/crontabs/root
  • 查看iptables路径: 使用 which iptables​,一般为 /usr/sbin/iptables
  • 脚本建议放置于root目录下,如 /root/traffic_monitor.sh

地区限制脚本(如不需要,可以不加)

设置只允许中国IP、白名单IP访问,其他丢弃。

安装依赖

apt update && apt install -y ipset curl iptables kmod grep gawk

创建脚本文件并编辑

cd /root && vim secure_firewall.sh

脚本源码示例

#!/bin/bash

# 修复 Cron 环境下找不到命令的问题
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# =========================================================
# 配置项
# =========================================================
# 建议加入你目前操作这台电脑的公网 IP,防止误杀
WHITELIST="127.0.0.1,1.203.149.102" 

# 定义所有需要保护的端口(包含 Docker 映射端口 和 宿主机SSH 端口)
PROTECTED_PORTS="80,443,22"

# =========================================================

echo "正在初始化环境..."

# 1. 确保 ipset 已安装
if ! command -v ipset &> /dev/null; then
    apt-get update && apt-get install -y ipset
fi

# 2. 建立集合
ipset create china hash:net maxelem 100000 2>/dev/null
ipset create whitelist hash:ip 2>/dev/null

# 3. 更新白名单
echo "正在更新白名单..."
ipset flush whitelist
IFS=',' read -ra ADDR <<< "$WHITELIST"
for ip in "${ADDR[@]}"; do
    ipset add whitelist $ip 2>/dev/null
done

# =========================================================
#【新增:看大门检测】如果发现已经下载过IP规则,直接退出,避免重复拉取
# =========================================================
FLAG_FILE="/var/run/guard_applied.flag"
if [ -f "$FLAG_FILE" ]; then
    echo "白名单已动态更新。中国 IP 库与核心规则已在内核中,跳过重复写入。"
    exit 0
fi
# =========================================================

# 4. 获取并更新中国 IP 段
echo "正在下载中国 IP 库..."
curl -s --connect-timeout 10 https://ispip.clang.cn/all_cn.txt > /tmp/china_ip.txt

if [ -s /tmp/china_ip.txt ] && grep -qE '[0-9]{1,3}\.[0-9]{1,3}' /tmp/china_ip.txt; then
    echo "下载成功,正在应用到内核..."
    ipset flush china
    grep -E '^[0-9]' /tmp/china_ip.txt | sed -e 's/^/add china /' | ipset restore -!
    
    COUNT=$(ipset list china | grep 'Number of entries' | awk '{print $4}')
    echo "成功载入 $COUNT 条中国网段数据。"
else
    echo "------------------------------------------------------"
    echo "【错误】下载的 IP 库内容不合法(可能是 404 页面或网络连接被拦截)。"
    echo "脚本已紧急停止执行,未修改防火墙规则,防止误杀自己。"
    echo "------------------------------------------------------"
    exit 1
fi

# 5. 【智能状态防护】注入 Filter 表相关链
echo "正在应用 Filter 表状态防火墙规则..."

iptables -t raw -F PREROUTING 2>/dev/null

iptables -N DOCKER_GUARD 2>/dev/null
iptables -F DOCKER_GUARD

iptables -A DOCKER_GUARD -m conntrack --ctstate RELATED,ESTABLISHED -j RETURN
iptables -A DOCKER_GUARD -s 172.16.0.0/12 -j RETURN
iptables -A DOCKER_GUARD -d 172.16.0.0/12 -j RETURN
iptables -A DOCKER_GUARD -m set --match-set whitelist src -j RETURN
iptables -A DOCKER_GUARD -m set --match-set china src -j RETURN
iptables -A DOCKER_GUARD -j DROP

iptables -D INPUT -p tcp -m multiport --dports $PROTECTED_PORTS -j DOCKER_GUARD 2>/dev/null
iptables -D FORWARD -p tcp -m multiport --dports $PROTECTED_PORTS -j DOCKER_GUARD 2>/dev/null
iptables -D INPUT -p udp -m multiport --dports $PROTECTED_PORTS -j DOCKER_GUARD 2>/dev/null
iptables -D FORWARD -p udp -m multiport --dports $PROTECTED_PORTS -j DOCKER_GUARD 2>/dev/null

iptables -I INPUT 1 -p tcp -m multiport --dports $PROTECTED_PORTS -j DOCKER_GUARD
iptables -I FORWARD 1 -p tcp -m multiport --dports $PROTECTED_PORTS -j DOCKER_GUARD
iptables -I INPUT 1 -p udp -m multiport --dports $PROTECTED_PORTS -j DOCKER_GUARD
iptables -I FORWARD 1 -p udp -m multiport --dports $PROTECTED_PORTS -j DOCKER_GUARD

# =========================================================
# 创建拉取IP库规则标记文件
touch "$FLAG_FILE"
# =========================================================

echo "------------------------------------------------------"
echo "【全方位防护已开启 (Smart Mode)】"
echo "保护端口: $PROTECTED_PORTS"
echo "拦截逻辑: 允许 [中国 IP]、[白名单] 和 [Docker 内部/回程流量] 访问,其余丢弃。"
echo "------------------------------------------------------"
echo "验证方法: iptables -L DOCKER_GUARD -v -n"

脚本权限设置

chmod +x secure_firewall.sh

使用示例

手动执行脚本:

./secure_firewall.sh

成功输出示例:

正在初始化环境...
正在更新白名单...
正在下载中国 IP 库...
下载成功,正在应用到内核...
成功载入 4210 条中国网段数据。
正在应用 Filter 表状态防火墙规则...
------------------------------------------------------
【全方位防护已开启 (Smart Mode)】
保护端口: 80,443,22
拦截逻辑: 允许 [中国 IP]、[白名单] 和 [Docker 内部/回程流量] 访问,其余丢弃。
------------------------------------------------------
验证方法: iptables -L DOCKER_GUARD -v -n

自动定时运行

可通过 crontab 实现脚本定时运行。例如每分钟执行一次。

(crontab -l ; echo "* * * * * /bin/bash /root/secure_firewall.sh") | crontab -

[!TIP] 💡 注意

  • 单独使用此脚本时使用
  • 与流量监控脚本联合使用时,不需要加定时运行

常用操作

  • 检查某个 IP 是否属于中国IP或白名单:
ipset test china x.x.x.x ipset test whitelist x.x.x.x
  • 查看端口防护规则及拦截统计信息:
iptables -t raw -L PREROUTING -v -n

[!TIP] 💡 注意事项

  • 如遇自动检测网络接口失败,请手动指定 INTERFACE 参数。
  • Bark 通知 KEY 需替换为个人设备的 KEY,否则无法收到推送。
  • 本配置仅禁止除中国 IP 外的地址通过 22、80 和 443 端口访问服务器。
  • 其他端口并未做任何干预。
  • 请根据实际需求修改 WHITELIST​ 配置项增加白名单IP地址。
  • 如需保护更多端口,请根据实际需求修改PROTECTED\_PORTS​ 配置项。

如果有更多疑问,欢迎留言交流!

标签: 流量监控, Linux, 网络管理, iptables, vnstat

添加新评论