REALITY — sing-box
sing-box 实现 REALITY 两端。入站(内嵌 tls: 块中的 reality: 子块)在 TLS 握手期间冒充第三方站点;出站(同样嵌于 tls: 内部) 只承载公钥与 short ID。
入站 reality
位于 tls.reality。入站需同时设置 tls.enabled 与 tls.reality.enabled 为 true。
| 字段 | 类型 | 默认值 | 允许值 | 描述 |
|---|---|---|---|---|
enabled | bool | false | true | false | 启用该入站的 REALITY。为 true 时,普通 TLS 握手被 REALITY 握手取代。 |
handshake | InboundRealityHandshakeOptions | (required) | InboundRealityHandshakeOptions | 伪装目标。未授权连接(SNI / short_id 错误)会被透明代理到该地址,使观察者看到流量流向合法站点。 |
private_key | string | (required) | <base64 X25519 private key> | 服务端 X25519 私钥。可用 `sing-box generate reality-keypair` 生成。 |
short_id | badoption.Listable[string] | [] | [<hex string>] | 允许的 short ID 列表(hex,偶长度,≤ 16 字符)。含 `""` 项允许不声明 short ID 的客户端。 |
max_time_difference | badoption.Duration | 0 (no limit) | <duration> | 客户端与服务端之间容忍的最大时钟偏移。超出该窗口的连接被拒绝。0 关闭检查。 |
源码: option/tls.go:193-199 · 锚定版本 v1.13.11 (553cfa1)
handshake
handshake 子块为伪装上游内嵌 ServerOptions(server、 server_port)与 DialerOptions:
json
"handshake": {
"server": "www.cloudflare.com",
"server_port": 443,
"bind_interface": "eth0"
}出站 reality
位于出站内嵌 tls: 块的 tls.reality。
| 字段 | 类型 | 默认值 | 允许值 | 描述 |
|---|---|---|---|---|
enabled | bool | false | true | false | 启用该出站的 REALITY。 |
public_key | string | (required) | <base64 X25519 public key> | 服务端 X25519 公钥。与服务端 `private_key` 配套。 |
short_id | string | "" | <hex string> | 向服务端声明的 short ID。必须命中服务端 `short_id` 之一(或服务端允许时为空)。 |
源码: option/tls.go:234-238 · 锚定版本 v1.13.11 (553cfa1)
生成密钥
sh
$ sing-box generate reality-keypair
PrivateKey: <base64-private-key>
PublicKey: <base64-public-key>私钥用于服务端 private_key,公钥用于客户端 public_key。 short ID 是任意 hex 字符串,按客户端分配。
示例
服务端 —— 冒充 www.cloudflare.com 的 VLESS over REALITY:
json
{
"inbounds": [{
"type": "vless",
"tag": "vless-reality",
"listen": "::",
"listen_port": 443,
"users": [{ "uuid": "...", "flow": "xtls-rprx-vision" }],
"tls": {
"enabled": true,
"server_name": "www.cloudflare.com",
"reality": {
"enabled": true,
"handshake": {
"server": "www.cloudflare.com",
"server_port": 443
},
"private_key": "<base64-private-key>",
"short_id": ["", "0123456789abcdef"]
}
}
}]
}客户端:
json
{
"outbounds": [{
"type": "vless",
"server": "your.server.example",
"server_port": 443,
"uuid": "...",
"flow": "xtls-rprx-vision",
"tls": {
"enabled": true,
"server_name": "www.cloudflare.com",
"utls": { "enabled": true, "fingerprint": "chrome" },
"reality": {
"enabled": true,
"public_key": "<base64-public-key>",
"short_id": "0123456789abcdef"
}
}
}]
}说明
handshake.server与handshake.server_port共同构成 伪装目 标 —— Xray 中称为dest。未授权连接被透明代理到此地址,使线 路上的形态看起来是与该站点通信。- 出站故意保持最小 —— 只有
public_key与short_id。其余 (SNI、ALPN、指纹)由外层tls块提供。 - 出站使用
tls.reality时几乎总要同时设置tls.utls。没有 uTLS 时 ClientHello 会带有 Go 标准库指纹,与浏览器不匹配,破坏伪装。 max_time_difference是时钟偏移拒绝窗口。服务端与客户端不共享 NTP 时建议设宽(如 1 分钟)。
跨内核说明
- Xray-core 把 REALITY 字段全放在单一
REALITYConfig结构体里 (服务端与客户端字段同处一处)。伪装目标即dest字段。参见 REALITY — Xray-core。 - mihomo 把结构按入站 / 出站拆分,并使用 kebab-case 字段名 (
public-key、private-key、short-id、server-names)。参见 REALITY — mihomo。
源码: option/tls.go:193-238 · v1.13.11 (553cfa1)
