说明

  • 说明:V2Ray配置流量监控
  • 主要包括:监控日志、触发阈值报警并发邮件、动态更新配置禁止公共账号访问
  • 实现方式:shell脚本
  • 前提条件:已经按照我的“V2Ray安装教程”一文配置好v2ray(vmess+ws+tls)
  • 注意:本教程最终的配置文件是基于流量伪装的,如果你没有配置流量伪装,配置文件略有不同,本文不再赘述

步骤

v2ray自带数据统计功能StatsService API,你只需要按以下要求修改你的v2ray配置文件即可

  • StatsService API获得数据统计功能,配置里面必须满足以下条件:
    • "stats":{}对象的存在,当此项存在时,开启统计信息;
    • "api"配置对象里面有StatsService
    • "policy"中的统计开关为true,除了各个用户的统计,还有全局统计 ;
    • clients里面要有email ,email用来区分用户(或者你可以直接统计tag名而不使用email,本案例使用tag名统计);
    • 有个专用的dokodemo-door协议的入口,tag为api ;
    • routing里面要有inboundTag:api和 outboundTag:api的规则 。
  • 配置好的config.json可能是这样的:
    {
    	"stats": {},
    	"log": {
    		"loglevel": "error"
    	},
    	"api": {
    		"tag": "api",
    		"services": [
    			"HandlerService",
    			"LoggerService",
    			"StatsService"
    		]
    	},
    	"policy": {
    		"levels": {
    			"0": {
    				"statsUserUplink": true,
    				"statsUserDownlink": true
    			},
    			"1": {
    				"statsUserUplink": true,
    				"statsUserDownlink": true
    			}
    		},
    		"system": {
    			"statsInboundUplink": true,
    			"statsInboundDownlink": true
    		}
    	},
    	"inbounds": [{
    			"tag": "tag123",
    			"port": 111,
    			"protocol": "vmess",
    			"settings": {
    				"clients": [{
    					"email": "[email protected]",
    					"id": "c433a4df-a4a2-4c8f-8d1f-c1cfdb5dde88",
    					"level": 1,
    					"alterId": 64
    				}, {
    					"email": "[email protected]",
    					"id": "686d6fda-3ad4-4069-b2ab-03c52cff9e2d",
    					"level": 1,
    					"alterId": 64
    				}]
    			},
    			"streamSettings": {
    				"network": "ws",
    				"wsSettings": {
    					"path": "/ws"
    				}
    			}
    		},
    		{
    			"listen": "127.0.0.1",
    			"port": 666,
    			"protocol": "dokodemo-door",
    			"settings": {
    				"address": "127.0.0.1"
    			},
    			"tag": "api"
    		}
    	],
    	"outbounds": [{
    		"protocol": "freedom",
    		"settings": {}
    	}, {
    		"protocol": "blackhole",
    		"settings": {},
    		"tag": "blocked"
    	}],
    	"routing": {
    		"settings": {
    			"rules": [{
    				"inboundTag": [
    					"api"
    				],
    				"outboundTag": "api",
    				"type": "field"
    			}]
    		},
    		"strategy": "rules"
    	}
    }
  • 查询命令:
    # 查询全局流量
    /usr/bin/v2ray/v2ctl api --server=127.0.0.1:666 StatsService.QueryStats 'pattern: "" reset: false'
        # 以上命令运行结果可能是这样的:
        stat: <
          name: "inbound>>>tag123>>>traffic>>>uplink"
        >
        stat: <
          name: "inbound>>>tag123>>>traffic>>>downlink"
        >
        stat: <
          name: "inbound>>>api>>>traffic>>>uplink"
          value: 174
        >
        stat: <
          name: "inbound>>>api>>>traffic>>>downlink"
          value: 24
        >
    # 查询某账号下载流量
    /usr/bin/v2ray/v2ctl api --server=127.0.0.1:666  StatsService.GetStats 'name: "user>>>[email protected]>>>traffic>>>downlink" reset: false'
    # 查询某账号上传流量
    /usr/bin/v2ray/v2ctl api --server=127.0.0.1:666  StatsService.GetStats 'name: "user>>>[email protected]>>>traffic>>>uplink" reset: false'
    # 查询某tag下载流量
    /usr/bin/v2ray/v2ctl api --server=127.0.0.1:666  StatsService.GetStats 'name: "inbound>>>tag123>>>traffic>>>downlink" reset: false'
    # 查询某tag上传流量
    /usr/bin/v2ray/v2ctl api --server=127.0.0.1:666  StatsService.GetStats 'name: "inbound>>>tag123>>>traffic>>>uplink" reset: false'
  • 注意:每次重启服务(start、restart、reload...)都会重置统计数据
  • 注意:确保相关端口是开放的
  • 参考链接:

两个脚本:

  • 原理:
    • 分配两个账号:[email protected][email protected]
    • 默认两个账号都能联网,当触发报警阈值后更新配置,删除一个账号,只留一个账号可以上网,已达到控制外部访问的目的

v2ray服务定时重启脚本

#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
########## start ##########
### 函数定义
# 替换config(允许公共账号访问)
changeConfigFun(){
    CONFIG01='{"stats":{},"log":{"loglevel":"error"},"api":{"tag":"api","services":["HandlerService","LoggerService","StatsService"]},"policy":{"levels":{"0":{"statsUserUplink":true,"statsUserDownlink":true},"1":{"statsUserUplink":true,"statsUserDownlink":true}},"system":{"statsInboundUplink":true,"statsInboundDownlink":true}},"inbounds":[{"tag":"tag123","port":111,"protocol":"vmess","settings":{"clients":['
    CONFIG02='{"email":"[email protected]","id":"c433a4df-a4a2-4c8f-8d1f-c1cfdb5dde88","level":1,"alterId":64},{"email":"[email protected]","id":"686d6fda-3ad4-4069-b2ab-03c52cff9e2d","level":1,"alterId":64}'
    CONFIG03=']},"streamSettings":{"network":"ws","wsSettings":{"path":"/ws"}}},{"listen":"127.0.0.1","port":666,"protocol":"dokodemo-door","settings":{"address":"127.0.0.1"},"tag":"api"}],"outbounds":[{"protocol":"freedom","settings":{}},{"protocol":"blackhole","settings":{},"tag":"blocked"}],"routing":{"settings":{"rules":[{"inboundTag":["api"],"outboundTag":"api","type":"field"}]},"strategy":"rules"}}'
    CONFIG_FILE="/path/to/v2ray/config.json"
    echo "${CONFIG01}${CONFIG02}${CONFIG03}" > $CONFIG_FILE
}

### 重启服务逻辑
# 停止服务
DATE=`date +"%Y-%m-%d %H:%M:%S"`
echo "[${DATE}]"
service v2ray stop
# 替换config(允许公共账号访问)
changeConfigFun
sleep 1s
# 启动服务
DATE=`date +"%Y-%m-%d %H:%M:%S"`
echo "[${DATE}]"
service v2ray start
########## end ##########

本案例基于宝塔1天执行一次,你也可以自己添加脚本并手动设置定时任务

  • 脚本执行逻辑:
    • 首先会停止v2ray服务
    • 修改(重置)v2ray的配置文件(允许两个账号联网:[email protected][email protected]
      • 注意:务必要替换脚本中的函数changeConfigFun中的四个变量CONFIG01CONFIG02CONFIG03CONFIG_FILE,具体根据你自己环境更改
    • 休眠1秒
    • 再次启动v2ray服务
  • 说明:经测,长时间运行v2ray服务而不重启v2ray服务的话,内存消耗会越来越大,本案例每天重启一次v2ray服务,具体自己根据自己服务器配置高低自行决定多久重启一次吧

v2ray流量监控脚本

########## start ##########
### 定义变量
# 务必需要自定义的变量区域开始----------
# 是否需要发送警告邮件(0=不发送,1=发送)
NEED_SEND_MAIL=1
# 接收警告邮件的邮箱
[email protected]
# 发送警告邮件的邮箱
[email protected]
# 发送警告邮件的昵称
FROM_NAME=demo.com
# 日志存放目录(确保已存在,结尾不要'/')
BASE_DIR=/path/to/v2ray_flow_log
# 务必需要自定义的变量区域结束----------

# 实际的下载流量值(单位:b)
DOWN=0
# 格式化后的下载流量值
DOWN_FORMAT=0
# 格式化后的下载流量单位
DOWN_UNIT=KB
# 实际的上传流量值(单位:b)
UP=0
# 格式化后的上传流量值
UP_FORMAT=0
# 格式化后的上传流量单位
UP_UNIT=KB

# 当前月(格式:202107)
MONTH=`date +"%Y%m"`
# 当前日(格式:20210702)
DAY=`date +"%Y%m%d"`
# 月目录(位置:/path/to/v2ray_flow_log/202107)
MONTH_DIR="${BASE_DIR}/$MONTH"
# 日目录(位置:/path/to/v2ray_flow_log/202107/20210702)
DAY_DIR="${MONTH_DIR}/${DAY}"
# 月流量总流量数标记文件名(位置:/path/to/v2ray_flow_log/202107/month_flow_total.txt)
# MONTH_FLOW_TOTAL_FILE="month_flow_total.txt"
# 日流量监控文件名(位置:/path/to/v2ray_flow_log/202107/20210702_flow.log)
DAY_FLOW_FILE="${MONTH_DIR}/${DAY}_flow.log"
# 日流量总流量数标记文件名(位置:/path/to/v2ray_flow_log/202107/20210702_flow_total.txt)
DAY_FLOW_TOTAL_FILE="${MONTH_DIR}/${DAY}_flow_total.txt"

### 函数定义
# 流量启动发送邮件
startSendMailFun(){
    _TO_EMAIL=$1
    _FROM_EMAIL=$2
    _FROM_NAME=$3
    _SUBJECT="今日上网流量开始了!"
    _CONTENT="----------------------------------------
今日上网流量开始了!请注意查看监控日志!
----------------------------------------"
    # 发送
    /usr/sbin/sendmail -t <<EOF
To: $_TO_EMAIL
From: $_FROM_NAME <$_FROM_EMAIL>
Subject: $_SUBJECT
$_CONTENT
EOF
}
# 流量超额发送邮件
overSendMailFun(){
    _TO_EMAIL=$1
    _FROM_EMAIL=$2
    _FROM_NAME=$3
    _FLOW=$4
    _SUBJECT="今日上网流量达到${_FLOW}M!"
    _CONTENT="----------------------------------------
今日上网流量达到${_FLOW}M!请注意查看监控日志!
----------------------------------------"
    # 发送
    /usr/sbin/sendmail -t <<EOF
To: $_TO_EMAIL
From: $_FROM_NAME <$_FROM_EMAIL>
Subject: $_SUBJECT
$_CONTENT
EOF
}
# 替换config(禁止公共账号访问)
changeConfigFun(){
    CONFIG01='{"stats":{},"log":{"loglevel":"error"},"api":{"tag":"api","services":["HandlerService","LoggerService","StatsService"]},"policy":{"levels":{"0":{"statsUserUplink":true,"statsUserDownlink":true},"1":{"statsUserUplink":true,"statsUserDownlink":true}},"system":{"statsInboundUplink":true,"statsInboundDownlink":true}},"inbounds":[{"tag":"tag123","port":111,"protocol":"vmess","settings":{"clients":['
    CONFIG02='{"email":"[email protected]","id":"c433a4df-a4a2-4c8f-8d1f-c1cfdb5dde88","level":1,"alterId":64}'
    CONFIG03=']},"streamSettings":{"network":"ws","wsSettings":{"path":"/ws"}}},{"listen":"127.0.0.1","port":666,"protocol":"dokodemo-door","settings":{"address":"127.0.0.1"},"tag":"api"}],"outbounds":[{"protocol":"freedom","settings":{}},{"protocol":"blackhole","settings":{},"tag":"blocked"}],"routing":{"settings":{"rules":[{"inboundTag":["api"],"outboundTag":"api","type":"field"}]},"strategy":"rules"}}'
    CONFIG_FILE="/path/to/v2ray/config.json"
    echo "${CONFIG01}${CONFIG02}${CONFIG03}" > $CONFIG_FILE
}

### 下载流量统计
DOWN=`/usr/bin/v2ray/v2ctl api --server=127.0.0.1:666 \
StatsService.GetStats 'name: "inbound>>>tag123>>>traffic>>>downlink" \
reset: false' |grep value`
if [[ $DOWN ]]; then
    # 去除冒号以及前边的内容
    DOWN=${DOWN#*:}
    if [[ $DOWN -gt 1048576 ]]; then
        # 如果大于1M,按MB单位
        DOWN_FORMAT=`expr $DOWN / 1048576`
        DOWN_UNIT=MB
    else
        # 否则按KB单位
        DOWN_FORMAT=`expr $DOWN / 1024`
    fi
fi
### 上传流量统计
UP=`/usr/bin/v2ray/v2ctl api --server=127.0.0.1:666 \
StatsService.GetStats 'name: "inbound>>>tag123>>>traffic>>>uplink" \
reset: false' |grep value`
if [[ $UP ]]; then
    # 去除冒号以及前边的内容
    UP=${UP#*:}
    if [[ $UP -gt 1048576 ]]; then
        # 如果大于1M,按MB单位
        UP_FORMAT=`expr $UP / 1048576`
        UP_UNIT=MB
    else
        # 否则按KB单位
        UP_FORMAT=`expr $UP / 1024`
    fi
fi

### 写入文件(当当前总流量大于0时)
# 当前总流量
CURRENT_TOTAL=$((DOWN+UP))
if [[ $CURRENT_TOTAL -gt 0 ]]; then
    # 月目录不存在则创建
    if [ ! -d $MONTH_DIR ]; then
        mkdir $MONTH_DIR
    fi
    # 日流量总流量数标记文件
    if [ ! -f $DAY_FLOW_TOTAL_FILE ]; then
        touch $DAY_FLOW_TOTAL_FILE
        # 写入默认值0
        echo 0 > $DAY_FLOW_TOTAL_FILE
        if [[ "$NEED_SEND_MAIL" == 1 ]]; then
            # 发送邮件
            startSendMailFun $TO_EMAIL $FROM_EMAIL $FROM_NAME
        fi
    fi
    # 日流量监控文件
    if [ ! -f $DAY_FLOW_FILE ]; then
        touch $DAY_FLOW_FILE
    fi

    # 读取标记的当前日总流量数
    DAY_FLOW_TOTAL=`head -n 1 ${DAY_FLOW_TOTAL_FILE}`

    # 当当前总流量大于标记的当前日总流量数时,更新日志记录
    if [[ $CURRENT_TOTAL -gt $DAY_FLOW_TOTAL ]]; then
        # 更新标记的当前日总流量数
        echo $CURRENT_TOTAL > $DAY_FLOW_TOTAL_FILE
        # 写入日志
        CONTENT="[`date +"%Y-%m-%d %H:%M:%S"`] | DOWN: ${DOWN_FORMAT}${DOWN_UNIT} | UP: ${UP_FORMAT}${UP_UNIT}"
        echo $CONTENT >> $DAY_FLOW_FILE
        # 判断超额,并发送邮件(每次触发超过100M发一次,最多发10次)
        # 起始流量阈值(100M)
        i=100
        while [[ $i -le 1000 ]]; do
            # 超过1G重启服务并禁止公共账号访问
            if [[ $i -eq 1000 ]]; then
                # 替换config(禁止公共账号访问)
                changeConfigFun
                # 重启服务
                service v2ray restart
            fi
            # 当流量总量大于${i}M
            if [[ $CURRENT_TOTAL -gt $((1048576*i)) ]]; then
                # 创建日目录
                if [ ! -d $DAY_DIR ]; then
                    mkdir $DAY_DIR
                fi
                # 日流量超过${i}M标记文件
                DAY_FLOW_OVER_FILE="${DAY_DIR}/${i}M.lock"
                if [[ ! -f $DAY_FLOW_OVER_FILE ]]; then
                    touch $DAY_FLOW_OVER_FILE
                    if [[ "$NEED_SEND_MAIL" == 1 ]]; then
                        # 发送邮件
                        overSendMailFun $TO_EMAIL $FROM_EMAIL $FROM_NAME $i
                    fi
                fi
                ((i+=100))
            else
                break
            fi
        done
    fi
fi
# 每个整点才输出脚本日志
MINUTE=`date +"%M"`
MINUTE=$((10#$MINUTE)) #转10进制
if [[ "$MINUTE" -eq "00" ]]; then
	endDate=`date +"%Y-%m-%d %H:%M:%S"`
	echo "★[$endDate] Successful"
fi
########## end ##########

本案例基于宝塔2秒执行一次,你也可以自己添加脚本并手动设置定时任务

  • 脚本执行逻辑:
    • 开头预定义了一些变量,其中最开始的几个变量需要自己务必更改
    • 首先会统计tag当前下载量和上传量
    • 如果当前总下载量和上传量大于0,则进行流量统计日志写入的逻辑(写入文件)
    • 同时,如果发现当天的日志文件不存在,也就是首次有流量产生时,会发一封电子邮件给报警邮箱
      • 注意:需要你手动测试一下你的环境是否能发送成功邮件,具体本文不再赘述
    • 然后会读取标记的当前流量使用量,只有当前流量值大于上一次标记的流量使用量才会写入日志并更新标记
    • 然后每次都会判断当前流量是否触发流量阈值(100M为一个起点,100M递增,最多增加到1G),每触发一个阈值会发送一封电子邮件到报警邮箱,最多发10次
    • 当触发第10次(触发1G)会执行替换配置文件(删除[email protected],只留[email protected]可以上网)并重启v2ray服务,重启后demo2账号将不能访问网络
    • 注意:务必要替换脚本中的函数changeConfigFun中的四个变量CONFIG01CONFIG02CONFIG03CONFIG_FILE,具体根据你自己环境更改
    • 注意:务必要替换脚本中变量DOWNUP中的端口和tag名字,具体根据你自己环境更改

附加说明:

  • 本文脚本所涉及到的主要知识点:
    • 变量定义以及变量读取和更改
    • 字符串替换(${})
    • $符号的多种用法($0=命令本身、$1=第一个参数、$?=上一个命令执行情况...)
    • 数值比较(-eq、-ne、-gt、-ge、-lt、-le...)
    • 数学运算(expr 2 / 2、$((A*10))...)
    • 获取当前时间(TIME="date +%Y-%m-%d %H:%M:%S"...)
    • EOF中变量$处理(直接$A就会读取代码前定义的变量)
    • shell函数以及传值(function name(){},或者直接name(){A=$1 B=$2 C=$3},调用及传值:name a b c
    • 读取文件头部(读取第一行:head -n 1 file.txt...)
    • 发送邮件
      #!/bin/bash
      /usr/sbin/sendmail -t <<EOF
      From: Mail testing <[email protected]>           
      To: [email protected]                                       
      Cc: [email protected]                                     
      Bcc: [email protected]                                   
      Subject: mail testing                                           
      ----------------------------------                                
      This is the mail content ...
      ---------------------------------
      EOF
      • 发送邮件说明:
        • sendmail服务默认安装完启动后不需要任何配置就可以代理发送邮件了,他的本质是邮件代发!
        • 发送邮件成功后发信人信息下方会显示(由xxx代发)并不是发件人的账号直接发送的
        • sendmail所在服务器甚至可以是内网服务器!
        • 跟某些系统后台的邮件功能有着本质的区别,常见的邮件代理功能都是配置好SMTP授权后,通过发件人直接发送邮件的

官方文档

关于作者 · AdminI am Yasin, let's talk about these first!

文章评论

还没有评论,欢迎留下第一条回复。