跳到主要内容

Xray JSON – Advanced

概述

在 Remnawave 中,XRAY_JSON 类型的订阅模板提供了 Remnawave 指令 — 这些是您添加到 JSON 模板中的特殊指令。面板在生成订阅时会处理它们,并从最终配置中删除 — 客户端永远不会看到它们。

目前可用的指令是 injectHosts,允许将主机的 outbound 配置动态注入到模板中。当您需要构建带有负载均衡器、自定义路由或多个 outbound 的复杂 Xray 配置时,这非常有用,而连接数据(地址、端口、密钥)将从面板自动注入。

提示

以下呈现的配置是用于演示注入机制的示例。请根据您的需求进行调整。

注意

需要 Remnawave 版本 2.6.3 或更高版本。

工作条件

  • 虚拟主机(分配了注入模板的主机)必须已启用未隐藏
  • 注入主机(通过 selector 选择的主机)必须已启用。默认情况下,只选择隐藏的主机(可通过 selectFrom 更改此行为)。
  • 所有主机 — 包括虚拟主机和注入主机 — 必须对最终用户可访问:它们绑定的 inbound 必须在用户的小队中启用。
  • 备注 (remark) 和服务器描述 (Server Description,如果设置了) 将从虚拟主机传入最终配置。

remnawave 结构

remnawave 对象添加到 JSON 模板的根级别。它支持以下字段:

字段描述
injectHosts注入组数组。每个组包含用于选择主机的选择器和标签形成参数。
addVirtualHostAsOutbound如果为 true — 虚拟主机将作为带有 proxy 标签的 outbound 添加到 outbounds 数组的开头。默认为 false。参见 addVirtualHostAsOutbound

injectHosts 字段是注入组的数组。每个组包含用于选择主机的选择器和自己的 tagPrefix

"remnawave": {
"injectHosts": [
{
"selector": { "type": "uuids", "values": ["uuid-хоста-1", "uuid-хоста-2"] },
"tagPrefix": "proxy"
},
{
"selector": { "type": "remarkRegex", "pattern": "^RU-" },
"tagPrefix": "backup"
}
]
},

injectHosts 数组的每个元素:

字段描述
selector定义将选择哪些主机的对象。必填字段。
selectFrom从哪个主机池中选择:"HIDDEN"(默认)、"NOT_HIDDEN""ALL"
tagPrefix创建的 outbound 的标签前缀。参见标签形成规则
useHostRemarkAsTag如果为 true — outbound 的标签将是主机的备注 (remark)。
useHostTagAsTag如果为 true — outbound 的标签将是主机标签(如果标签未设置,则使用备注)。
注意

必须指定三个字段中的恰好一个tagPrefixuseHostRemarkAsTaguseHostTagAsTag

可以有任意数量的组 — 每个组形成带有自己前缀的独立 outbound 集合。例如,这允许为每组服务器设置单独的负载均衡器。

选择器类型

uuids

通过 UUID 列表选择主机。UUID 的顺序决定 outbound 的顺序。

"selector": {
"type": "uuids",
"values": [
"8478b271-95d3-4312-85ae-ecf63fb53d1d",
"d31d6161-1315-4c1e-9a4b-141ab1c022f6"
]
}

remarkRegex

选择备注 (remark) 与正则表达式匹配的主机。语法为 JavaScript RegExp。

"selector": {
"type": "remarkRegex",
"pattern": "^Балансер"
}

上面的示例将选择所有备注以 "Балансер" 开头的隐藏主机(例如 "Балансер #1"、"Балансер RU")。

tagRegex

选择主机标签(主机设置中的 tag 字段)与正则表达式匹配的主机。

"selector": {
"type": "tagRegex",
"pattern": "^balancer-"
}

上面的示例将选择所有标签以 balancer- 开头的隐藏主机。

sameTagAsRecipient

选择所有主机标签与虚拟主机标签匹配的隐藏主机。不需要额外参数。

"selector": {
"type": "sameTagAsRecipient"
}

当您想自动对主机进行分组时很方便:只需为虚拟主机和注入主机分配相同的标签即可。

提示

默认情况下,所有选择器只处理隐藏的主机。要更改此行为,请使用 selectFrom 字段:"NOT_HIDDEN" 值只选择可见的主机(已启用但未隐藏),"ALL" — 所有主机(已启用,已隐藏)。

标签形成规则

outbound 的标签由注入组中指定的三个字段中的哪一个决定:

tagPrefix — 第一个主机获得等于 tagPrefix 的标签。此后每个主机获得 {tagPrefix}-{N},从 2 开始。

三个主机使用 tagPrefix: "proxy" 的示例:

顺序outbound 标签
第1个proxy
第2个proxy-2
第3个proxy-3

useHostRemarkAsTag — 每个 outbound 获得等于主机备注 (remark) 的标签。

{
"selector": { "type": "tagRegex", "pattern": "^ru-" },
"useHostRemarkAsTag": true
}

如果主机的备注是"莫斯科"、"圣彼得堡"、"喀山" — outbound 将获得 莫斯科圣彼得堡喀山 标签。

useHostTagAsTag — 每个 outbound 获得等于主机标签的标签。如果主机标签未设置,则使用其备注。

{
"selector": { "type": "tagRegex", "pattern": "^ru-" },
"useHostTagAsTag": true
}

Xray 中的前缀匹配

Xray 中的 selector(在 routing.balancers 中)和 subjectSelector(在 burstObservatory 中)字段作为前缀匹配器工作 — 它们与 outbound 标签的开头匹配,而不是与其精确值匹配。

例如,如果配置中有标签为 proxyproxy-2proxy-3direct 的 outbound:

selector / subjectSelector 值将选择哪些 outbound
["proxy"]proxyproxy-2proxy-3
["proxy-"]proxy-2proxy-3
  • "selector": ["proxy"] — 将捕获所有注入的 outbound,包括第一个。
  • "selector": ["proxy-"] — 将捕获除第一个之外的所有(仅 proxy-2proxy-3、...)。
通过第一个主机的回退

第一个选择的主机始终获得没有 -{N} 后缀的标签(简单地为 proxy)。这允许将其用作负载均衡器中的 fallbackTag:如果 selector 中的所有 outbound 都不可用,流量将转到第一个主机。为此,设置 "selector": ["proxy-"](仅 proxy-2proxy-3、...)和 "fallbackTag": "proxy"

addVirtualHostAsOutbound

默认情况下,使用 remnawave 指令时,最终配置中出现注入的主机。虚拟主机本身(recipient)仅用作 remarksserverDescription 的来源。

如果您需要虚拟主机也成为带有 proxy 标签的 outbound,请在 remnawave 对象级别添加字段 addVirtualHostAsOutbound: true

"remnawave": {
"addVirtualHostAsOutbound": true,
"injectHosts": [
{
"selector": { "type": "uuids", "values": ["uuid-хоста-1", "uuid-хоста-2"] },
"tagPrefix": "proxy"
},
{
"selector": { "type": "remarkRegex", "pattern": "^RU-" },
"tagPrefix": "backup"
}
]
}

在这种情况下,最终 outbounds 数组将如下所示:

  1. 带有 proxy 标签的虚拟主机 Outbound
  2. 注入的 outbound(来自 injectHosts)。
  3. 模板中的静态 outbounddirectblock 等)。

当路由规则使用 "outboundTag": "proxy" 通过主要主机路由流量,而注入的主机处理单独的流量组(例如,通过负载均衡器)时,这很有用。

提示

addVirtualHostAsOutbound 可以与 injectHosts 一起使用,也可以不使用。如果 injectHosts 未指定或为空,则只有虚拟主机的 outbound 会被添加到配置中。

分步示例:带三个主机的负载均衡器

在此示例中,我们将创建一个配置,其中三个 outbound 使用 leastLoad 策略组合成负载均衡器,并由 Observatory 监控。

步骤 1. 创建主机

在面板中创建将参与注入的主机。在我们的示例中:

  • Virtual Host — 将分配注入模板的虚拟主机。未隐藏,最终用户通过它获取配置。
  • Balancer #1Balancer #2Balancer #3 — outbound 将注入模板的主机。
主机列表

步骤 2. 隐藏注入主机

打开每个负载均衡器主机的卡片(Balancer #1、#2、#3),进入 Advanced 部分并启用 Hide Host 开关。

隐藏的主机不会出现在普通订阅中 — 它们只能通过注入机制访问。

隐藏负载均衡器主机

步骤 3. 创建订阅模板

创建 XRAY_JSON 类型的订阅模板。在其中描述完整配置:dnsroutinginboundsoutboundsburstObservatory 及其他所需部分。

outbounds 数组中只放静态 outbound(directblock)— 注入主机的 outbound 将自动添加。

在 JSON 根级别添加带有隐藏主机选择器的 remnawave 对象。

模板示例

{
"remnawave": {
"injectHosts": [
{
"selector": {
"type": "uuids",
"values": [
"8478b271-95d3-4312-85ae-ecf63fb53d1d",
"d31d6161-1315-4c1e-9a4b-141ab1c022f6",
"5749f69e-cd1b-4012-9407-450434085196"
]
},
"tagPrefix": "proxy"
}
]
},
"burstObservatory": {
"pingConfig": {
"timeout": "3s",
"interval": "1m",
"sampling": 1,
"destination": "http://www.gstatic.com/generate_204",
"connectivity": ""
},
"subjectSelector": ["proxy"]
},
"dns": {
"servers": ["1.1.1.1", "1.0.0.1"],
"queryStrategy": "UseIP"
},
"routing": {
"balancers": [
{
"tag": "Super_Balancer",
"selector": ["proxy"],
"strategy": {
"type": "leastLoad",
"settings": {
"maxRTT": "1s",
"expected": 2,
"baselines": ["1s"],
"tolerance": 0.01
}
},
"fallbackTag": "direct"
}
],
"rules": [
{
"protocol": ["bittorrent"],
"outboundTag": "direct"
},
{
"network": "tcp,udp",
"balancerTag": "Super_Balancer"
}
],
"domainMatcher": "hybrid",
"domainStrategy": "IPIfNonMatch"
},
"inbounds": [
{
"tag": "socks",
"port": 10808,
"listen": "127.0.0.1",
"protocol": "socks",
"settings": {
"udp": true,
"auth": "noauth"
},
"sniffing": {
"enabled": true,
"routeOnly": false,
"destOverride": ["http", "tls", "quic"]
}
},
{
"tag": "http",
"port": 10809,
"listen": "127.0.0.1",
"protocol": "http",
"settings": {
"allowTransparent": false
},
"sniffing": {
"enabled": true,
"routeOnly": false,
"destOverride": ["http", "tls", "quic"]
}
}
],
"outbounds": [
{
"tag": "direct",
"protocol": "freedom"
},
{
"tag": "block",
"protocol": "blackhole"
}
]
}

请注意:

  • "subjectSelector": ["proxy"] — Observatory 将监控标签以 proxy 开头的所有 outbound(即 proxyproxy-2proxy-3)。
  • "selector": ["proxy"]Super_Balancer 负载均衡器将在相同的 outbound 之间分配流量。
  • 模板的 outbounds 中只指定了 directblock — 主机的 outbound 将自动添加在它们之前。

步骤 4. 将模板分配给虚拟主机

打开虚拟主机 (Virtual Host) 的卡片,进入 Advanced 部分,在 Xray JSON Template 字段中选择创建的模板。

确保虚拟主机的 Hide Host 开关已关闭 — 它应该在订阅中可见。

将模板分配给虚拟主机

步骤 5. 结果

请求订阅时,面板会自动:

  1. 获取分配给虚拟主机的模板。
  2. 从中删除 remnawave 对象。
  3. 对于 injectHosts 中的每个组,通过 selector 选择隐藏主机并组装其 outbound。
  4. 将 outbound 插入到 outbounds 数组的开头
  5. 从虚拟主机的备注设置 remarks

客户端收到的最终配置

[
{
"dns": {
"servers": ["1.1.1.1", "1.0.0.1"],
"queryStrategy": "UseIP"
},
"routing": {
"rules": [
{
"protocol": ["bittorrent"],
"outboundTag": "direct"
},
{
"network": "tcp,udp",
"balancerTag": "Super_Balancer"
}
],
"balancers": [
{
"tag": "Super_Balancer",
"selector": ["proxy"],
"strategy": {
"type": "leastLoad",
"settings": {
"maxRTT": "1s",
"expected": 2,
"baselines": ["1s"],
"tolerance": 0.01
}
},
"fallbackTag": "direct"
}
],
"domainMatcher": "hybrid",
"domainStrategy": "IPIfNonMatch"
},
"inbounds": [
{
"tag": "socks",
...omitted...
},
{
"tag": "http",
...omitted...
}
],
"outbounds": [
{
"tag": "proxy",
"protocol": "vless",
"settings": {...omitted...},
"streamSettings": {...omitted...}
},
{
"tag": "proxy-2",
"protocol": "vless",
"settings": {...omitted...},
"streamSettings": {...omitted...}
},
{
"tag": "proxy-3",
"protocol": "vless",
"settings": {...omitted...},
"streamSettings": {...omitted...}
},
{
"tag": "direct",
"protocol": "freedom"
},
{
"tag": "block",
"protocol": "blackhole"
}
],
"burstObservatory": {
"pingConfig": {
"timeout": "3s",
"interval": "1m",
"sampling": 1,
"destination": "http://www.gstatic.com/generate_204",
"connectivity": ""
},
"subjectSelector": ["proxy"]
},
"remarks": "Virtual Host"
}
]

发生了什么:

  • remnawave 对象已从最终配置中删除。
  • 三个 outbound(proxyproxy-2proxy-3)被插入到 outbounds 数组的开头,在 directblock 之前。
  • 负载均衡器中的 "selector": ["proxy"] 自动捕获了所有三个 outbound,因为它们的标签以 proxy 开头(前缀匹配)。
  • Observatory 中的 "subjectSelector": ["proxy"] 同样捕获了所有三个 outbound 进行监控。
  • "remarks": "Virtual Host" — 取自虚拟主机的备注。
备注

虚拟主机地址和实际 inbound

在此场景中,虚拟主机作为模板和元数据(备注、服务器描述)的"包装器",而不是实际的连接点。 在其设置中可以指定任何地址(例如 balancer.host.com)— 它不参与用户的实际连接。 实际入口点是注入主机的特定 inbound。重要的是,请求订阅的用户通过小队可以访问此 inbound,否则虚拟主机根本不会出现在他们的订阅中。 实际连接参数(地址、端口、密钥等)取自注入主机,其 outbound 配置被注入到客户端的最终配置中。

重要说明

  • 虚拟主机必须已启用且未隐藏。 正是它决定将使用哪个模板,remarksdescription 都取自它。
  • 注入主机必须已启用。 默认只选择隐藏主机(selectFrom: "HIDDEN")。此行为可以更改为 "NOT_HIDDEN""ALL"。如果主机被禁用或未通过选择器找到 — 将被跳过。
  • 所有参与主机必须对最终用户可访问 — 它们绑定的 inbound 必须在用户的小队中启用。
  • remnawave 对象将被删除 — 客户端不会看到它。
  • Outbound 添加到开头 outbounds 数组。如果启用了 addVirtualHostAsOutbound,带有 proxy 标签的虚拟主机 outbound 排在第一位,然后是注入的,然后是模板中的静态 outbound(directblock)。
  • 主机顺序决定 outbound 的顺序和分配给它们的标签。对于 uuids 选择器 — values 数组中的 UUID 顺序。可以使用 useHostRemarkAsTaguseHostTagAsTag 代替 tagPrefix,以便从主机属性生成标签。
  • 模板选择和主机隐藏在主机卡片的 Advanced 部分。