Hysteria 2 节点
Hysteria 2 节点在一个 UDP 端口上通过 QUIC 承载代理流量。当节点的 [node.api].node_type 为 Hysteria2(也接受 hysteria、hy2,不区分大小写)时,katana 就会提供这种节点。和其他节点一样,用户仍由面板下发,流量也仍上报给面板。但监听器本身需要几项任何面板都不会下发的设置,所以它们放在节点的 [node.hysteria] 表中。
本页介绍运行这种节点的两种方式:由面板描述节点,或由 katana 根据配置文件描述节点。本页列出 [node.hysteria] 下的所有键、面板必须下发的内容、官方 Hysteria 2 客户端如何认证,以及可能遇到的错误。关于 etemenanki-app 所提供的协议本身,见 etemenanki-app 中的 Hysteria 2。
Hysteria 2 节点有何不同
Section titled “Hysteria 2 节点有何不同”- 它监听 UDP。 katana 把
listen_ip:port绑定为 UDP 套接字,因此 Hysteria 2 节点可以与同一地址上的 TCP 节点共用端口号,例如 TCP 443 上的 Trojan 节点和 UDP 443 上的 Hysteria 2 节点。 - 它总是使用 TLS。 TLS 握手是 QUIC 的一部分,因此没有明文模式。无论面板对 TLS 怎么设置,每个 Hysteria 2 节点都需要
mode = "file"的[node.controller.cert]。 - 它的用户表原地替换。 面板的用户列表变化时,katana 只替换认证器。UDP 套接字保持绑定,其余用户保持连接。
--test可以检查它。[node.hysteria]下的设置都是本地的,所以katana --test会检查它们,并读取证书和私钥。对于其他节点类型,--test无法检查面板将要下发的内容。
描述节点的两种方式
Section titled “描述节点的两种方式”键 [node.hysteria].port 决定节点的端口和混淆设置从哪里来:
flowchart LR
A["node_type = Hysteria2"] --> B{"[node.hysteria] port"}
B -->|"0"| C["面板节点配置:port、obfs"]
B -->|"非零"| D["[node.hysteria]:port、obfs"]
C --> L["QUIC 监听器"]
D --> L
E["[node.hysteria]:credential、udp、masquerade"] --> L
F["[node.controller.cert]"] --> L
G["面板用户列表"] --> L
面板描述(port = 0) |
本地描述(port 非零) |
|
|---|---|---|
| 监听端口 | 面板的端口 | [node.hysteria].port |
| 混淆 | 面板的 obfs 和 obfs-password |
[node.hysteria].obfs 和 obfs_password |
节点配置请求(/UniProxy/config、/mod_mu/nodes/…/info) |
每次轮询都发送 | 从不发送 |
| 用户列表和流量上报 | 从面板获取,上报到面板 | 从面板获取,上报到面板 |
SSPanel 的节点级 node_speedlimit |
生效 | 不读取。[node.api].speed_limit 仍然生效 |
来自节点 routes 的 Xboard 审计规则 |
加载 | 不加载;只有 rule_list_path 生效 |
credential、udp、udp_idle_timeout、伪装响应 |
[node.hysteria] |
[node.hysteria] |
当面板无法描述节点时,就在本地描述它,例如在 SSPanel 上你不想使用 custom_config,或者你希望把端口和混淆设置保存在配置文件里。面板仍然必须知道这个节点:katana 会向面板请求 node_id 的用户,并以该 ID 上报他们的流量。在 Xboard 上,查找还会用到节点类型,所以该节点在面板中必须以 Hysteria 节点的形式存在。
本地描述的节点
Section titled “本地描述的节点”下面的文件在 UDP 端口 443 上提供一个 Hysteria 2 节点,启用 Salamander 混淆和 UDP 中继。用户和流量上报都经由一个 Xboard 面板。
# katana Hysteria 2 node described locally: the port and the obfuscation# come from [node.hysteria], so katana never asks the panel for the node's# config. The panel still supplies the user list and receives the traffic# reports for node_id 1.
[log]level = "info"
[[node]]panel_type = "NewV2board"
[node.api]host = "https://panel.example.com"node_id = 1key = "replace-with-the-panel-key"# Sent to the panel as node_type=hysteria2, which Xboard maps to its# "hysteria" node type.node_type = "Hysteria2"
[node.controller]listen_ip = "0.0.0.0"update_periodic = 60
# Every Hysteria 2 node needs a certificate file. Clients that verify it# also need a subjectAltName for the name they use as SNI.[node.controller.cert]mode = "file"cert_file = "/etc/katana/cert/fullchain.pem"key_file = "/etc/katana/cert/privkey.pem"
[node.hysteria]# Non-zero: this node is described here. Clients connect to UDP port 443.port = 443# The client's auth string is the user's UUID (the default).credential = "uuid"# Relay UDP as well as TCP; a quiet UDP session ends after 60 seconds.udp = trueudp_idle_timeout = 60# Salamander obfuscation. Clients need the same password.obfs = "salamander"obfs_password = "replace-with-a-long-random-password"
# What anyone without a valid credential sees.[node.hysteria.masquerade]status = 404body = "<html><body><h1>404 Not Found</h1></body></html>\n"content_type = "text/html; charset=utf-8"-
检查文件。
--test会读取证书和私钥,并检查[node.hysteria]下的每个键。它不会联系面板。终端窗口 katana --test -c /etc/katana/config.toml文件有效时打印
Configuration OK并以状态0退出。文件无效时把原因打印到 stderr,并以状态1退出:configuration error: node 1: udp_idle_timeout must be between 2 and 600 seconds -
启动 katana。 面板返回至少一个用户后,节点绑定套接字并记录:
INFO katana::manager::node: node 1: listening on 0.0.0.0:443如果用户列表为空,katana 不绑定任何端口,在出现用户之前不为任何人提供服务。如果节点无法启动,例如面板无法访问或端口已被占用,katana 会记录原因并重试。常见错误列出了这些消息。
-
放行端口。 在主机防火墙和云安全组中放行入站 UDP 443。TCP 443 的规则不覆盖它。
-
连接客户端。 把用户的 UUID 作为认证字符串交给客户端,同时提供混淆密码和证书上的服务器名称。客户端认证中有完整的客户端配置。
如果要改由面板描述同一个节点,把 port 设为 0(或省略),并删除 obfs 和 obfs_password:
[node.hysteria]port = 0udp = true[node.hysteria]
Section titled “[node.hysteria]”只有当 node_type 是 Hysteria 2 的取值时,katana 才会读取这个表;对其他节点则忽略它。未知键会被拒绝,因此像 obsf 这样的拼写错误会让 --test 报 unknown field `obsf`, expected one of `port`, `credential`, `udp`, `udp_idle_timeout`, `obfs`, `obfs_password`, `masquerade` 并停止。
| 键 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
port | u16 | 否 | 0 | 监听的 UDP 端口。非零值表示这是一个本地描述的节点:katana 用本表构造节点,从不调用面板的节点配置接口。0 表示像其他节点类型一样,向面板获取端口和混淆设置。 |
credential | string (enum) | 否 | "" | 客户端的认证字符串如何标识用户。"" 或 uuid:整个字符串就是用户的 UUID。user_pass:字符串为 label:uuid,按第一个冒号拆分;在 Xboard/V2board 上 label 是 <uuid>@v2board.user,在 SSPanel 上是数字用户 ID;label 比较时不区分大小写。本键的值区分大小写,其他取值会报 unknown hysteria credential kind。 |
udp | bool | 否 | false | 除 TCP 外也中继 UDP。默认关闭,这一点与上游服务端不同:此时监听器会告诉客户端 UDP 已禁用,只中继 TCP。 |
udp_idle_timeout | u64 | 否 | 60 | UDP 会话在没有任何数据报的情况下最多保持多少秒,超时后 katana 将其关闭。取值范围 2 到 600。默认值 60 只在 udp = true 时生效:本键只能在开启 UDP 时设置,udp 关闭时设置会报 udp_idle_timeout is set but udp is not enabled。 |
obfs | string (enum) | 否 | — | 本地描述节点的数据包混淆。唯一接受的值是小写的 salamander。port = 0 时使用面板的混淆设置,运行时忽略本键,但 --test 仍会检查它。 |
obfs_password | string | 视情况 | — | 所有客户端共用的 Salamander 密钥,至少 4 字节。设置了 obfs 时必填。只设它而不设 obfs 会被拒绝,这样拼写错误就不会悄悄关闭混淆。与 obfs 一样只对本地描述的节点生效。 |
masquerade | table | 否 | {} | 对所有未携带有效凭据的请求返回的 HTTP/3 响应,写作 [node.hysteria.masquerade]。本地描述和面板描述的节点都适用。 |
[node.hysteria.masquerade]
Section titled “[node.hysteria.masquerade]”Hysteria 2 服务端看起来像一个 HTTP/3 Web 服务器。凭据错误或缺失的请求,或者指向任何其他路径的请求,都会得到这个固定响应,因此密码错误看起来和 URL 错误完全一样。省略这个表,或只设置其中部分键时,缺失的键取下面的默认值。这些默认值与未配置的上游服务端的回复一致。
| 键 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
status | u16 | 否 | 404 | HTTP 状态码。可以是 100 到 999 之间除 233 以外的任何值:233 是 Hysteria 2 认证成功的状态码,用它会告诉探测者认证已通过。超出范围的值会报 is not an HTTP status code。 |
body | string | 否 | "404 page not found\n" | 响应正文,原样发送,并带有相应的 Content-Length。 |
content_type | string | 否 | "text/plain; charset=utf-8" | Content-Type 头的值。katana 不检查它。如果它不是合法的 HTTP 头部值(例如含有换行符),伪装响应会变成不带任何头部的 200 响应,因此请只使用可打印字符。 |
katana 只在 QUIC 内部提供伪装响应,不会为它打开 TCP 监听器。启用 Salamander 时,没有混淆密码的客户端无法完成 QUIC 握手,所以根本到不了伪装响应。
证书来自节点的 [node.controller.cert],从不来自面板。对于 Hysteria 2 节点:
| 键 | 要求 |
|---|---|
mode |
必须为 "file"。默认值 "none" 以及 ACME 模式 "dns"、"http" 和 "tls" 都会报 hysteria2 node requires cert.mode = "file"。 |
cert_file |
PEM 证书链,叶子证书在前。两个路径都必须设置,否则节点报 TLS node requires cert.cert_file and cert.key_file。DER 文件会报 hysteria2: the certificate file contains no certificates。 |
key_file |
与证书匹配的 PEM 私钥,格式为 PKCS#8、PKCS#1 或 SEC1。 |
reject_unknown_sni |
必须保持 false。设为 true 会报 node requests kernel-unsupported feature: cert.reject_unknown_sni。 |
监听器只使用 TLS 1.3,并提供 ALPN h3。
katana 在构建监听器时读取这些文件。续期后的证书即使保存到相同路径,也要等监听器重建后才会生效,所以每次续期后都要重启 katana。其他做法见 热重载。
面板描述的节点
Section titled “面板描述的节点”port = 0 时,katana 每次轮询都从面板获取端口和混淆设置。面板下发的其他节点信息 katana 都不使用:TLS 设置、服务器名称、带宽数值(up_mbps、down_mbps)和端口跳跃设置对监听器都没有影响。不过,katana 仍会在两次轮询之间比较其中一些未使用的值:Xboard 上节点的 host 或 server_name 变化,或 SSPanel 上 host、path 等 custom_config 字段变化,都会在下一次轮询时重建监听器并断开所有连接。
在面板中创建一个类型为 Hysteria 的节点,并把协议版本设为 2。katana 从 GET /api/v1/server/UniProxy/config 读取以下字段:
| 字段 | 面板设置 | katana 的用途 |
|---|---|---|
server_port |
节点的监听端口(不是展示给用户的端口) | UDP 端口。0 会报 newV2board: server port must be > 0。 |
obfs |
开启混淆时为 salamander,关闭时为 null |
混淆类型 |
obfs-password |
混淆密码 | Salamander 密钥 |
在 katana 配置中设置 panel_type = "NewV2board",以及 node_type = "Hysteria2" 或 "hysteria"。katana 会把 node_type 转为小写后发给面板,Xboard 会把 hysteria2 映射为它的 hysteria 类型。Xboard 不认识 hy2,所以按这种方式配置的节点发出的每个请求都会被拒绝。
Xboard 的订阅链接把用户的 UUID 作为 Hysteria 2 密码,这与 katana 默认的 credential 一致。
SSPanel 没有自己的 Hysteria 2 节点类型,所以 katana 从节点的 custom_config JSON 中读取节点信息。面板版本必须为 2021.11 或更高,并且 [node.api].disable_custom_config 必须保持 false。否则 katana 会使用旧式的 server 字符串,而它无法描述 Hysteria 2 节点,节点会报 sspanel: a hysteria2 node needs custom_config; the legacy server string cannot describe one。
{ "offset_port_node": "443", "obfs": "salamander", "obfs-password": "replace-with-a-long-random-password"}| 字段 | 含义 |
|---|---|
offset_port_node |
UDP 端口,写成 JSON 字符串。写成 JSON 数字会报 parse sspanel custom_config,不是端口号的字符串会报 invalid offset_port_node。 |
obfs |
"salamander";不需要混淆时省略 |
obfs-password |
Salamander 密钥,按 UniProxy 面板的拼写使用连字符 |
custom_config 缺失或为 null 时报 custom_config is empty, disable custom config。对于 Hysteria 2 节点,katana 不使用 custom_config 中的其他字段。节点的 node_speedlimit 作为节点限速生效,除非设置了 [node.api].speed_limit,它会替换前者。
katana 在构建监听器时检查面板下发的值,规则与本地键相同:salamander 以外的任何类型报 unknown obfs,密钥过短报 obfs_password must be at least 4 bytes for salamander。本地的 obfs 和 obfs_password 键对面板描述的节点不起作用,但 --test 仍会检查它们。
Hysteria 2 客户端发送一个认证字符串。[node.hysteria].credential 决定 katana 如何从中找出用户:
credential |
Xboard / V2board 上的认证字符串 | SSPanel 上的认证字符串 |
|---|---|---|
"" 或 "uuid"(默认) |
<uuid> |
<uuid> |
"user_pass" |
<uuid>@v2board.user:<uuid> |
<user id>:<uuid> |
- 在
uuid模式下,整个字符串会与面板列出的每个用户的 UUID 逐字节比较,因此大小写有影响。除非你的客户端是按上游的userpass认证方式配置的,否则使用这种模式。 - 在
user_pass模式下,katana 在第一个冒号处拆分字符串。冒号之前是用户的标签,比较时不区分大小写:在 UniProxy 面板上是<uuid>@v2board.user,在 SSPanel 上是数字用户 ID。冒号之后是 UUID。这正是上游服务端的userpass格式,所以为上游服务端写的客户端配置无需改动即可使用。 - 两种模式下,katana 的流量计费都用同一个标签来标识用户。
认证字符串错误时会得到伪装响应。官方客户端随后会报告伪装响应的状态码,例如 authentication error, HTTP status code: 404。
server: proxy.example.com:443auth: 11111111-2222-3333-4444-555555555555obfs: type: salamander salamander: password: replace-with-a-long-random-passwordtls: sni: proxy.example.comsocks5: listen: 127.0.0.1:1080server: proxy.example.com:443auth: 11111111-2222-3333-4444-555555555555@v2board.user:11111111-2222-3333-4444-555555555555obfs: type: salamander salamander: password: replace-with-a-long-random-passwordtls: sni: proxy.example.comsocks5: listen: 127.0.0.1:1080server: proxy.example.com:443auth: 42:11111111-2222-3333-4444-555555555555obfs: type: salamander salamander: password: replace-with-a-long-random-passwordtls: sni: proxy.example.comsocks5: listen: 127.0.0.1:1080这里的 42 是该用户在 SSPanel 中的 ID。
节点没有混淆时,省略 obfs 块。混淆设置与节点不一致的客户端永远无法完成 QUIC 握手,它报告的是超时,而不是认证错误。
UDP 中继
Section titled “UDP 中继”udp 默认关闭。此时 katana 会在认证回复中告诉每个客户端 UDP 不可用,并且只中继 TCP。上游服务端默认启用 UDP,所以如果你的用户需要 UDP,请设置 udp = true。开启 UDP 后,如果某个 UDP 会话在 udp_idle_timeout 秒内没有任何数据报,katana 会将其关闭;这项检查每秒进行一次。
带宽与拥塞控制
Section titled “带宽与拥塞控制”katana 没有实现 Hysteria 的 Brutal 拥塞控制。它对每个客户端都回复 Hysteria-CC-RX: auto,并忽略客户端声明的带宽以及面板的 up_mbps 和 down_mbps。Hysteria 2 用户受到的唯一限速是 katana 的每用户令牌桶,见 限速。
固定的连接上限
Section titled “固定的连接上限”以下上限适用于每个 Hysteria 2 节点,且无法修改:
| 上限 | 值 | 达到上限时 |
|---|---|---|
| 每节点 QUIC 连接数 | 4,096 | 新连接一到达就被拒绝。 |
| 每节点活跃的代理流和 UDP 会话数 | 65,536 | 新的流或会话被拒绝,客户端的其他流不受影响。 |
| 每个 QUIC 连接的并发流数 | 1,024 | 客户端要等到有空闲的流,才能再打开新的流。 |
| QUIC 空闲超时 | 30 s | 连接在 30 秒内(如果客户端自己的空闲超时更短,则按客户端的)没有收到任何数据包时被关闭。客户端的 keep-alive 也算作数据包。 |
这些上限都不是按用户计算的。拒绝会以 debug 级别记录,例如 hysteria2: refusing a connection; the listener is full。
嗅探、路由与审计
Section titled “嗅探、路由与审计”Hysteria 2 的流与其他节点上的流走相同的路径:准入、路由、审计和计量。[node.controller].disable_sniffing = true 同样会关闭 Hysteria 2 流的嗅探。Xboard 上本地描述的节点从不拉取节点配置,所以没有来自面板 routes 的审计规则;只有 rule_list_path 指定的文件生效。
修改运行中节点的设置
Section titled “修改运行中节点的设置”| 修改 | 何时生效 |
|---|---|
| 面板的用户列表 | 下一次轮询时生效,无需重新绑定。其余用户保持连接。 |
面板的端口或混淆设置(port = 0) |
下一次轮询时生效,表现为重建监听器并断开所有连接 |
文件中 [node.hysteria] 下的任何键:port、credential、udp、udp_idle_timeout、obfs、obfs_password 或伪装响应 |
立即生效,表现为重建监听器并断开所有连接。设置或清除 port 会在本地描述和面板描述之间切换。 |
文件中的 [node.controller].listen_ip、[node.controller].disable_sniffing 或 [node.controller.cert] |
立即生效,表现为重建监听器 |
文件中的 [node.api].speed_limit |
立即生效,无需重新绑定。已打开的连接保持不变;修改之后打开的流按新速率运行。 |
重建监听器时会先关闭旧监听器,再构建新监听器。配置重载会拒绝 [node.hysteria] 下的未知键,但 katana 只在构建监听器时才检查它们的取值,所以保存了一个无法构建的修改(例如 udp_idle_timeout = 1)后,节点会没有监听器:katana 记录 node 1: rebuild failed: …,并在每次轮询时重试,直到你修正文件。保存之前先运行 katana --test。
完整的重载过程见 热重载。
--test 和正在启动的节点报出的错误都带有节点前缀,例如 configuration error: node 1: …。
| 消息 | 原因 | 解决办法 |
|---|---|---|
hysteria2 node requires cert.mode = "file" |
没有配置证书,或设置了 ACME 模式。 | 设置 [node.controller.cert],包括 mode = "file"、cert_file 和 key_file。 |
No such file or directory (os error 2) |
证书或私钥路径不存在。 | 检查两个路径;使用绝对路径。 |
hysteria2: the certificate file contains no certificates |
cert_file 不是 PEM 证书。 |
让它指向 PEM 证书链,而不是 DER 文件或私钥。 |
hysteria2: the key file contains no private key |
key_file 中没有 PEM 私钥,例如它指向了证书。 |
让它指向 PEM 私钥。 |
hysteria2: certificate and key do not match: … |
key_file 属于另一张证书。 |
使用与这张证书一起签发的私钥。 |
obfs_password is set but obfs is not; did you mean obfs = "salamander"? |
设置了密码但没有设置类型,可能在本地,也可能来自关闭了混淆的 Xboard。 | 添加 obfs = "salamander",或删除密码。 |
unknown obfs "Salamander" (expected "salamander") |
拼写或大小写错误,或者 Xboard 节点仍是协议版本 1。 | 用小写写 salamander;把 Xboard 节点设为版本 2。 |
obfs_password must be at least 4 bytes for salamander |
密钥短于 4 字节或缺失。 | 使用较长的随机值,例如由 openssl rand -base64 24 生成。 |
udp_idle_timeout is set but udp is not enabled |
设置了 udp_idle_timeout 但没有 udp = true。 |
设置 udp = true,或删除该超时。 |
udp_idle_timeout must be between 2 and 600 seconds |
取值不在 2 到 600 之间。 |
选择范围内的值。 |
unknown hysteria credential kind "UUID" (expected "uuid" or "user_pass") |
取值不是可接受的字符串之一。它区分大小写。 | 使用 uuid 或 user_pass。 |
hysteria2: 233 is the authentication success status and cannot be used for the masquerade |
伪装响应的状态码为 233。 |
使用其他状态码,例如 404。 |
hysteria2: 1000 is not an HTTP status code |
伪装响应的状态码不在 100 到 999 之间。 |
使用真实的 HTTP 状态码。 |
newV2board: server port must be > 0 或 panel returned port 0 |
面板下发的端口为 0:Xboard 的 server_port,或 SSPanel 的 offset_port_node。 |
在面板中设置节点端口,或在本地描述节点。 |
Address already in use (os error 98) |
其他进程或节点已占用 listen_ip 上的该 UDP 端口。 |
释放该端口或换一个端口。 |
节点无法启动时,katana 会记录原因并重试。node 1: node_info failed: …; retrying in <N>s 表示无法拉取或解析面板的节点配置,node 1: user_list failed: …; retrying in <N>s 表示用户列表出现同样的问题,node 1: initial start failed: …; retrying in <N>s 表示无法构建监听器。第一次重试在 1 秒后进行,之后每次失败等待时间翻倍,最长 60 秒;如果 update_periodic 更短,则最长为 update_periodic。每次尝试都会重新向面板请求用户列表,对于面板描述的节点,还会重新请求节点配置。保存对该节点 [[node]] 条目的修改会立即触发重试,所以你可以直接在文件中排除原因,无需重启 katana。之后的重建如果失败(例如面板修改了混淆设置之后),会记录 node 1: rebuild failed: …,katana 会在下一次轮询时重试。
客户端一侧:
| 客户端现象 | 原因 |
|---|---|
authentication error, HTTP status code: 404(或你设置的伪装状态码) |
认证字符串与 credential 模式不匹配,或者该用户不在面板为此节点下发的列表中。 |
| 超时,没有认证错误 | 混淆设置或密码与节点不一致、UDP 端口被阻断,或者节点没有在监听。 |
x509: certificate relies on legacy Common Name field, use SANs instead |
证书没有 subjectAltName。 |