跳转到内容

VLESS

VLESS 是 V2Ray/Xray 家族中的一种轻量代理协议。客户端先发送一个简短的请求头,其中包含用户 UUID、命令和目标地址,之后连接上原样承载你的流量。etemenanki-app 既可以作为 VLESS 服务端(在 [[inbound]] 中写 protocol = "vless"),也可以作为客户端(在 [[outbound]] 中),并且能通过 TLS、WebSocket 和 gRPC 与 Xray 互通。

本页涵盖两种角色:所有设置项、VLESS 如何与传输层组合、UDP 和 mux 支持、有意未实现的 Xray 功能,以及可能遇到的错误。katana 使用同一份协议代码提供 VLESS 节点;在 katana 中,用户和传输层由面板下发,因此本页的设置只适用于 etemenanki-app。

入站(服务端) 出站(客户端)
凭据 users = [{ id = "…" }],只接受 UUID id = "…",一个 UUID
传输层 tcp、tls、ws 和 grpc;ws 和 grpc 可带或不带 TLS 相同
TCP 支持 支持
UDP 支持,始终开启 支持,每个 UDP 流只有一个目标
mux.cool 和 XUDP 自动接受,无需设置 不支持:每个流都单独建立连接
XTLS flow(Vision) 不支持 不支持
REALITY 不支持 不支持

一个在 443 端口上通过 WebSocket 和 TLS 接受 VLESS 的服务端,以及一个提供本地 SOCKS 端口、把所有流量发往该服务端的客户端。证书必须对 proxy.example.com 有效,因为客户端会按这个名称验证证书。

server.toml
[[inbound]]
tag = "vless-in"
protocol = "vless"
listen = "0.0.0.0" # 默认值是 127.0.0.1
port = 443
[inbound.stream]
network = "ws"
security = "tls"
[inbound.stream.ws]
path = "/vless"
[inbound.stream.tls]
cert_file = "/etc/etemenanki/fullchain.pem"
key_file = "/etc/etemenanki/privkey.pem"
[inbound.settings]
users = [
{ id = "11111111-2222-3333-4444-555555555555" },
{ id = "11111111-2222-3333-4444-666666666666" },
]
[[outbound]]
tag = "direct"
protocol = "freedom"
[route]
default = "direct"

启动前先用 etemenanki-app --test -c server.toml 检查每个文件。这里的客户端不需要 [outbound.stream.tls] 表:TLS 服务器名和 WebSocket Host 都会回退为 server。包含证书和测试请求的完整步骤,请参考 VLESS over WebSocket and TLS 配方。

用 uuidgen 或 cat /proc/sys/kernel/random/uuid 为每个用户生成真实的 UUID。

这些键写在 protocol = "vless" 的入站的 [inbound.settings] 中。通用入站键(tag、listen、port、sniffing、stream)见入站页面。

键类型必填默认值说明
usersarray of tables否[]允许连接的用户,每个用户用一个 UUID 标识。列表为空或不写时配置仍能构建,但所有客户端都会被拒绝。Xray 的 clients、decryption 和 fallbacks 键在这里不存在,写上会被视为未知字段,导致配置失败。
users[].idstring是—用户的 UUID。可接受的写法:带连字符(11111111-2222-3333-4444-555555555555)、不带连字符的 32 位十六进制,或者把带连字符的写法用花括号包起来、加上小写的 urn:uuid: 前缀;十六进制数字大小写均可。其他写法报 invalid uuid。id 是条目中唯一的键:flow、email 和 level 都会被拒绝。

UUID 无效时整个配置都会失败,错误信息会指出入站和出错的值:

configuration invalid: inbound vless-in: invalid uuid "not-a-uuid": invalid character: found `n` at 0

服务端如何匹配用户:

  • 服务端在 users 中查找该 UUID,找不到就关闭连接。不会回落到其他服务:未知客户端得不到任何响应。
  • 与 Xray 一样,服务端比较 id 时忽略 UUID 的第三组(11111111-2222-3333-4444-555555555555 中的 3333)。只在这一组上不同的两个条目被视为同一个用户,以后一个为准。
  • 条目不带 email 或其他标签。所有用户的待遇相同,没有按用户的路由。

这些键写在 protocol = "vless" 的出站的 [outbound.settings] 中。出站还需要通用键 server 和 port,即 VLESS 服务器的地址。缺少它们时构建会失败,报 outbound proxy: missing server 或 outbound proxy: missing port;如果 network 是 tls、ws 或 grpc 且没有 server,会更早因缺少 TLS 名称或 host 而失败。address_family 决定如何解析服务器名。这些键见出站页面。

键类型必填默认值说明
idstring是—用于认证的 UUID,必须与服务端的某个 users[].id 一致。可接受的写法与入站相同:带连字符、32 位十六进制,或者把带连字符的写法用花括号包起来、加上小写的 urn:uuid: 前缀。缺少时报 invalid settings: missing field,格式错误时报 invalid uuid。id 是唯一的键:Xray 的 flow、encryption 和 level 都会被拒绝,也没有 mux 设置。

出站不做多路复用。它承载的每个 TCP 流和每个 UDP 流都会单独建立一条到服务端的连接:先是 TCP 连接,然后是 TLS,若传输层需要,再进行 WebSocket 升级或建立 HTTP/2 连接。由于 VLESS 出站有 server 和 port,它可以作为负载均衡器的成员。

VLESS 可以运行在 [inbound.stream] 或 [outbound.stream] 中的任意流式传输层之上。两端必须使用相同的 network、相同的 WebSocket path 或 gRPC service_name,并且在是否使用 TLS 上保持一致。这些键本身见传输层页面。

network security 线上传输的内容 Xray 对应写法
"tls" 不写、"none" 或 "tls" TCP 上的 TLS 中的 VLESS "network": "tcp"、"security": "tls"
"ws" "tls" TLS 中的 WebSocket 中的 VLESS "network": "ws"、"security": "tls"
"grpc" "tls" TLS 中的 gRPC(HTTP/2)中的 VLESS "network": "grpc"、"security": "tls"
"tcp"(默认) 不写或 "none" 明文 VLESS:UUID 可被读取 "network": "tcp"
"ws" 或 "grpc" 不写或 "none" 明文 WebSocket 或 gRPC:UUID 可被读取 相同,但不写 security

明文的几行适用于已经由其他组件提供 TLS 的部署,例如同一主机上的反向代理终止 TLS,再把 WebSocket 转发到回环端口。监听 Unix socket(listen 为路径)的 VLESS 入站始终以明文运行。它拒绝 "tcp" 以外的任何 network 和 "none" 以外的任何 security,例如 inbound vless-in: protocol vless over a unix socket does not support stream network "ws"。

sequenceDiagram
    participant C as 客户端
    participant S as VLESS 入站
    participant T as 目标
    C->>S: TLS 和 WebSocket 握手
    C->>S: 包含 UUID、命令和目标的请求头,随后是载荷
    Note over S: 查找 UUID,未知则关闭连接
    S->>T: 通过路由选出的出站连接
    T-->>S: 已连接
    S-->>C: 响应头,2 字节
    S->>T: 载荷
    T-->>S: 回复
    S-->>C: 回复

客户端不会等待响应头:它紧跟在请求后面发送第一段载荷,服务端会先保留这段数据,直到连上目标。请求头格式如下:

字段 大小 接受的值
Version 1 字节 只接受 0
User id 16 字节 users 中的某个 UUID
Addons length 1 字节 只接受 0。非零值表示 XTLS flow,会被拒绝
Command 1 字节 1 TCP,2 UDP,3 mux
Target 可变 端口(2 字节),然后是地址类型(1 IPv4,2 域名,3 IPv6)和地址。mux 命令没有此字段

服务端会检查每个固定字段,遇到其他值就关闭连接。它用两字节的响应头应答(version 为 0,无 addons):

  • 对 TCP 请求,只在连上目标之后才应答。如果连接目标失败,服务端直接关闭客户端连接,不发送响应;
  • 对 UDP 或 mux 请求,在用户认证通过后立即应答。

客户端必须在 10 秒内完成 VLESS 请求;已建立的连接如果 300 秒内两个方向都没有数据,就会被关闭。这些限制对所有协议通用,见限制。

当入站的 sniffing 开启(默认开启)且 TCP 请求给出的是 IP 地址而不是域名时,服务端会暂存载荷的最初几个字节,最多 300 ms、4 KiB,用来还原 TLS 服务器名或 HTTP Host 以供路由。UDP 请求不做嗅探。

VLESS 在同一条流式连接内承载 UDP。请求头只指定一个目标,两个方向上的每个包都以 2 字节大端长度加载荷的形式分帧。包上不带地址:该连接上的所有包都发往请求头中的目标,客户端也把所有回复视为来自该目标。

  • 入站。 UDP 始终开启,没有关闭它的设置。每个 UDP 请求都像其他流一样经过路由,因此 network = "udp" 的 [[route.rule]] 对它生效。
  • 出站。 出站把一个 UDP 流作为一个 VLESS UDP 请求承载,目标是该流经这个出站发送的第一个目的地址。请求头固定了目标,因此该流之后路由到这个出站的每个包,无论原本要发往哪个地址,都会发往那个第一个目的地址,所有回复也都报告为来自它。因此,一个经 VLESS 出站与多个对端通信的 SOCKS UDP 关联只能到达第一个对端。Xray 用 XUDP 避免这个问题,而本出站不支持 XUDP。

Xray 客户端经常开启 mux,在一条 VLESS 连接上发送多个流。VLESS 入站会自动接受这种连接,没有启用或禁用它的键。

  • mux 连接是命令为 3 的 VLESS 请求。它本身没有目标:其伪目的地址 v1.mux.cool:0 永远不会被拨号。
  • 其中每个子流都指定自己的目的地址,并单独路由,就像它是通过独立连接到达的一样。开启 sniffing 时,发往 IP 地址的子流只根据其打开帧中携带的载荷进行嗅探;承载连接不会为此等待更多数据。子流可以是 TCP 流或 UDP 关联;XUDP 子流的每个包都带有地址,因此一个关联可以到达多个对端,并且每个回复都会归属到实际发送它的那个对端。
  • 一条 mux 连接同时最多承载 256 个子流。客户端如果打开更多子流,或者复用一个仍处于打开状态的子流的 id,该子流会被 End 帧拒绝,而 mux 连接本身保持不变。
  • 关闭 mux 连接会结束其中所有子流。

出站从不发送 mux。如果写了 Xray 风格的 [outbound.mux],配置会失败,报 unknown field `mux`。

以下 Xray 功能在 etemenanki-app 中不存在。每一项都会明确报错,而不是被忽略。

Xray 功能 在这里的结果
XTLS flow,包括 xtls-rprx-vision 两端的 flow 键都是未知字段。客户端如果仍然发送 flow,会被断开,并报 vless addons (xtls flow) are not supported。
REALITY security = "reality" 报 unknown stream security。请使用真实证书和 TLS。
fallbacks 在 [inbound.settings] 中是未知字段。未认证的连接会被关闭,从不转发。
decryption / encryption 未知字段。只支持明文 VLESS,即 Xray 中的 "none"。
任意文本形式的 id Xray 会从简短的文本 id 派生出 UUID;这里 id 必须是 UUID,否则构建失败,报 invalid uuid。
用户的 email 和 level 未知字段。
出站 mux 未知字段。出站为每个流单独建立一条连接。
Xray JSON etemenanki-app TOML
入站 settings.clients[].id [inbound.settings] users[].id
入站 settings.decryption = "none" 不写
出站 settings.vnext[0].address 和 .port [[outbound]] 本身的 server 和 port
出站 vnext[0].users[0].id [outbound.settings] id
出站 users[0].encryption = "none" 不写
streamSettings.network = "tcp" 加 security = "tls" [.stream] network = "tls"
wsSettings.path [.stream.ws] path
grpcSettings.serviceName [.stream.grpc] service_name
tlsSettings.serverName [.stream.tls] server_name
tlsSettings.certificates[0] [.stream.tls] cert_file 和 key_file

配置的其余部分见 Xray 迁移页面。

集成测试让 etemenanki-app 与真实的 xray-core 二进制程序对接,传输流量并比对字节。对于 VLESS,测试覆盖:

传输层 etemenanki-app 作客户端,Xray 作服务端 Xray 作客户端,etemenanki-app 作服务端
TCP 上的 TLS(network = "tls",Xray tcp + tls) 已测试 已测试
WebSocket,明文和 TLS 已测试 已测试
gRPC,明文和 TLS 已测试 已测试
开启 mux 的 Xray 客户端:单个流、多个并发流,以及经 WebSocket 加 TLS 不适用 已测试
XUDP(mux 上的 UDP):单个对端,以及同一关联中的两个对端,每个回复都归属到正确的对端 不适用 已测试

配置错误会在运行 --test 或启动程序时出现,热重载被拒绝时也会出现:

错误 原因 解决方法
inbound vless-in: invalid uuid "…": … 某个 users[].id 不是 UUID 使用真实的 UUID;可接受的写法见上文
outbound proxy: invalid uuid "…": … 出站的 id 不是 UUID 同上
invalid settings: unknown field `flow`, expected `id` 用户条目或出站设置中写了 flow、email 或 level 删除该键;不支持 XTLS
invalid settings: unknown field `decryption`, expected `users` [inbound.settings] 中有 Xray 独有的键,clients 或 fallbacks 同理 删除该键,或把 clients 改名为 users
outbound proxy: invalid settings: missing field `id` [outbound.settings] 中没有 id 添加 UUID
outbound proxy: missing server / missing port 出站没有上游地址 添加 server 和 port
outbound proxy: ws stream needs ws.host or server(或 tls stream needs tls.server_name or server) 传输层需要 host 或 TLS 名称的出站没有写 server;这项检查在 missing server 之前执行 添加 server 和 port
security = "tls" is not valid with network = "tcp"; … 使用了 Xray 对 TCP 上 TLS 的写法 使用 network = "tls"
unknown stream security "reality" (expected "tls" or "none") security 写了 REALITY、XTLS 或有拼写错误 使用 "tls"
inbound vless-in: tls stream needs tls.cert_file(或 tls.key_file) 入站开启了 TLS,但没有证书或私钥 在 [inbound.stream.tls] 下添加 cert_file 和 key_file
grpc stream needs grpc.service_name network = "grpc" 但没有服务名 在两端的 [.stream.grpc] 中设置 service_name

在协议层面出错的客户端会被断开。设置 [log] level = "debug" 后,服务端对每个失败的连接记录一行日志,格式为 vless connection from … ended: …。协议错误带有 proxy core: 前缀,后接以下原因之一:

原因 含义
invalid vless request user id UUID 不在 users 中。检查客户端和服务端是否使用同一个 id。
vless addons (xtls flow) are not supported 客户端设置了 flow,通常是 xtls-rprx-vision。在客户端上删除它。
invalid vless request version: … 客户端说的不是 VLESS,或者两端的 TLS 或传输层设置不一致。
invalid vless command: … 命令不是 TCP、UDP 或 mux。客户端使用了本服务端未实现的 VLESS 功能。
client did not complete its request in time 客户端开始发送请求,但没有在 10 秒内完成。完全不发送任何数据的客户端会被记录为 inbound handshake timed out after 10s,不带该前缀。

如果客户端报告的是 TLS 或 WebSocket 错误,说明两端的传输层设置不同:检查 network、security、WebSocket path 和证书名称。