调用 V2Ray 提供的 API 接口进行用户增删及流量统计查询操作

前不久 V2Ray 增加了一些 API,允许通过这些 API 从外部对一个运行中的 V2Ray 实例进行多种操作,详细信息可以看文档:https://www.v2ray.com/chapter_02/api.html

我对其中增加用户和删除用户这两个操作比较感兴趣,所以今天就稍微拿来玩一下。

不说什么废话,直接上配置代码了。

下面是服务器的配置文件,可以看到比起常规的配置文件,还多了:

  • 一个叫 api 的顶级配置项
  • 一个 dokodemo-door inboundDetour
  • routing 里一个以 inboundTag 为匹配项的路由配置
{
"api": {
"services": [
"HandlerService"
],
"tag": "api"
},
"inbound": {
"port": 10086,
"protocol": "vmess",
"settings": {
"clients": [
{
"alterId": 32,
"id": "fcecbd2b-3a34-4201-bd3d-7c67d89c26ba",
"level": 0
}
]
},
"streamSettings": {
"network": "tcp"
},
"tag": "proxy"
},
"inboundDetour": [
{
"listen": "127.0.0.1",
"port": 10085,
"protocol": "dokodemo-door",
"settings": {
"address": "127.0.0.1"
},
"tag": "api"
}
],
"log": {
"loglevel": "debug"
},
"outbound": {
"protocol": "freedom",
"settings": {}
},
"routing": {
"settings": {
"rules": [
{
"inboundTag": [
"api"
],
"outboundTag": "api",
"type": "field"
}
]
},
"strategy": "rules"
}
}

这个配置可以这样解释:

  • 当有顶级 api 配置项时,V2Ray 会自动创建一个以 api.tagtag 来标识的 outbound
  • 当有 API 请求发到 dokodemo-door 所监听的端口时,会理所当然地匹配到路由中配置的 api -> api 一项,所以 API 请求会从 dokodemo-door 被路由到上面所提到的 V2Ray 自动创建的 outbound 中,然后所有发送到这个 outbound 的请求都会被当作 gRPC API 请求处理。
  • 其中 dokodemo-doorsettings.address 可以随便填,因为在做路由时只用 inboundTag ,而不会用到这个地址(但必需要有一个合法地址值,不然会报错,这只是代码实现上的问题不用在意)

下面来写点代码调用这个 API 服务,这个代码增加/删除一个指定 inbound 中的 VMess 用户:

package mainimport (
"context"
"fmt"
"log"
"google.golang.org/grpc" "v2ray.com/core/app/proxyman/command"
"v2ray.com/core/common/protocol"
"v2ray.com/core/common/serial"
"v2ray.com/core/proxy/vmess"
)
const (
API_ADDRESS = "127.0.0.1"
API_PORT = 10085
INBOUND_TAG = "proxy" LEVEL = 0
EMAIL = "123@gmail.com"
UUID = "2601070b-ab53-4352-a290-1d44414581ee"
ALTERID = 32
)
func addUser(c command.HandlerServiceClient) {
resp, err := c.AlterInbound(context.Background(), &command.AlterInboundRequest{
Tag: INBOUND_TAG,
Operation: serial.ToTypedMessage(&command.AddUserOperation{
User: &protocol.User{
Level: LEVEL,
Email: EMAIL,
Account: serial.ToTypedMessage(&vmess.Account{
Id: UUID,
AlterId: ALTERID,
SecuritySettings: &protocol.SecurityConfig{Type: protocol.SecurityType_AUTO},
}),
},
}),
})
if err != nil {
log.Printf("failed to call grpc command: %v", err)
} else {
log.Printf("ok: %v", resp)
}
}
func removeUser(c command.HandlerServiceClient) {
resp, err := c.AlterInbound(context.Background(), &command.AlterInboundRequest{
Tag: INBOUND_TAG,
Operation: serial.ToTypedMessage(&command.RemoveUserOperation{
Email: EMAIL,
}),
})
if err != nil {
log.Printf("failed to call grpc command: %v", err)
} else {
log.Printf("ok: %v", resp)
}
}
func main() {
cmdConn, err := grpc.Dial(fmt.Sprintf("%s:%d", API_ADDRESS, API_PORT), grpc.WithInsecure())
if err != nil {
panic(err)
}
hsClient := command.NewHandlerServiceClient(cmdConn)
addUser(hsClient)
// removeUser(hsClient)
}

这里有些注意点:

  • 代码中可以看到,要操作的是 tagproxyinbound ,也就是说增加的用户会被配置到这个 inbound
  • 需要用 email 字段来区别用户
  • 删除用户后,并不会“立刻生效”,需要等几分钟;现在 Core 的代码实现中,删除用户操作仅仅是把用户从用户列表中移除,但没有把对应的 auth session 移除,这样的话就需要等这些 session 超时后,这个用户才会无法认证

提到多用户,V2Ray 有一个容易被忽略的设置就是 V2RAY_RAY_BUFFER_SIZE ,如果留默认值的话,用户多起来会非常吃内存,可以考虑设置一个较小的值,就算设置为 1 也似乎并不会对网络性能有太多影响,比如在 Linux 下可以这样:

V2RAY_RAY_BUFFER_SIZE=1 v2ray -config cfg.json

对于 V2Ray 服务端,还有一个比较影响内存使用量的因素是 AlterId,如果内存不足,不宜设置过大,官方文档里说 10 足够:https://www.v2ray.com/chapter_03/01_effective.html

2018.5 update(流量统计信息查询):

流量统计信息查询跟增删用户的配置方法非常相似,具体可以参考这个完整配置文件:https://gist.github.com/eycorsican/aa8cdc1d39c3fa355c499f89a15b9753

流量统计信息查询 API 的调用方法可以参考:https://github.com/v2ray/ext/blob/master/tools/control/api.go

上面那个是 v2ctl 的代码,其实可以直接调用 v2ctl 命令来查询流量统计信息,例如获取用户邮箱为 123@gmail.com 的 下行流量 ( downlink) 的命令为:

v2ctl api --server=127.0.0.1:10085 StatsService.GetStats 'name: "user>>>123@gmail.com>>>traffic>>>downlink" reset: false'
  • reset 表示是否在获取此用户的统计信息后把统计值重置为 0
  • 请注意在查询流量前要先使对应的用户产生一些流量,否则返回为空值。

References:

上面的代码是参考 core/testing/scenarios/command_test.go 中的代码所写,这个测试用例里有一个删除 inbound 的 API 调用例子。

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store