* docs(xray-json): clarify virtual host role in inject flow - document that the virtual host acts as a wrapper for the template and metadata - explain that its address and profile can be arbitrary and are not used for the actual user connection - clarify that real connection parameters are taken from injected hosts' outbounds * Update xray-json-advanced.md
27 KiB
sidebar_position, title
| sidebar_position | title |
|---|---|
| 3 | Xray JSON – Advanced |
Обзор
Для шаблонов подписки типа XRAY_JSON в Remnawave предусмотрены Remnawave-директивы — специальные инструкции, которые вы добавляете в JSON-шаблон. Панель обрабатывает их при генерации подписки и удаляет из итогового конфига — клиент их никогда не увидит.
На данный момент доступна директива injectHosts, позволяющая динамически подставлять outbound-конфигурации хостов в шаблон. Это полезно, когда вам нужно собрать сложную конфигурацию Xray с балансировщиками, кастомным роутингом или несколькими outbound'ами, при этом данные подключения (адрес, порт, ключи) подставятся автоматически из панели.
:::tip Совет Представленные ниже конфигурации являются примерами для демонстрации механизма инжекта. Адаптируйте их под свои нужды. :::
:::warning Внимание Требуется Remnawave версии 2.6.3 или новее. :::
Условия работы
- Виртуальный хост (хост, которому назначен шаблон с инжектом) должен быть включён и не скрыт.
- Инжектируемые хосты (выбранные через
selector) должны быть включены. По умолчанию выбираются только скрытые хосты (поведение можно изменить черезselectFrom). - Все хосты — и виртуальный, и инжектируемые — должны быть доступны конечному пользователю: инбаунд, к которому они привязаны, должен быть включён в сквад пользователя.
- Из виртуального хоста в итоговый конфиг попадают примечание (remark) и описание сервера (Server Description, если задано).
Структура remnawave
Объект remnawave добавляется на корневой уровень JSON-шаблона. Он поддерживает следующие поля:
| Поле | Описание |
|---|---|
injectHosts |
Массив групп инжекта. Каждая группа содержит селектор для выбора хостов и параметры формирования тегов. |
addVirtualHostAsOutbound |
Если true — виртуальный хост будет добавлен как outbound с тегом proxy в начало массива 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'а будет тег хоста (если тег не задан, используется remark). |
:::warning
Необходимо указать ровно одно из трёх полей: tagPrefix, useHostRemarkAsTag или useHostTagAsTag.
:::
Групп может быть сколько угодно — каждая формирует свой независимый набор 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"
}
Удобно, когда вы хотите автоматически группировать хосты: достаточно присвоить одинаковый тег виртуальному и инжектируемым хостам.
:::tip
По умолчанию все селекторы работают только со скрытыми хостами. Чтобы изменить это поведение, используйте поле 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
Поля selector (в routing.balancers) и subjectSelector (в burstObservatory) в Xray работают как префиксные матчеры — они сопоставляются с началом тега outbound'а, а не с его точным значением.
Например, если в конфиге есть outbound'ы с тегами proxy, proxy-2, proxy-3, direct:
| Значение selector / subjectSelector | Какие outbound'ы будут выбраны |
|---|---|
["proxy"] |
proxy, proxy-2, proxy-3 |
["proxy-"] |
proxy-2, proxy-3 |
"selector": ["proxy"]— подхватит все инжектированные outbound'ы, включая первый."selector": ["proxy-"]— подхватит все кроме первого (толькоproxy-2,proxy-3, ...).
:::note Fallback через первый хост
Первый выбранный хост всегда получает тег без суффикса -{N} (просто proxy). Это позволяет использовать его как fallbackTag в балансировщике: если все outbound'ы из selector окажутся недоступны, трафик уйдёт на первый хост. Для этого задайте "selector": ["proxy-"] (только proxy-2, proxy-3, ...) и "fallbackTag": "proxy".
:::
addVirtualHostAsOutbound
По умолчанию при использовании remnawave-директивы в итоговый конфиг попадают только инжектированные хосты. Сам виртуальный хост (recipient) используется только как источник remarks и serverDescription.
Если вам нужно, чтобы виртуальный хост также стал outbound'ом с тегом proxy, добавьте поле addVirtualHostAsOutbound: true на уровне объекта remnawave:
"remnawave": {
//highlight-next-line-green
"addVirtualHostAsOutbound": true,
"injectHosts": [
{
"selector": { "type": "uuids", "values": ["uuid-хоста-1", "uuid-хоста-2"] },
"tagPrefix": "proxy"
},
{
"selector": { "type": "remarkRegex", "pattern": "^RU-" },
"tagPrefix": "backup"
}
]
}
В этом случае итоговый массив outbounds будет выглядеть так:
- Outbound виртуального хоста с тегом
proxy. - Инжектированные outbound'ы (из
injectHosts). - Статические outbound'ы из шаблона (
direct,blockи т. д.).
Это полезно, когда в routing-правилах используется "outboundTag": "proxy" для направления трафика через основной хост, а инжектированные хосты обслуживают отдельные группы трафика (например, через балансировщики).
:::tip
addVirtualHostAsOutbound можно использовать совместно с injectHosts или без них. Если injectHosts не указан или пуст, в конфиг будет добавлен только outbound виртуального хоста.
:::
Пошаговый пример: балансировщик с тремя хостами
В этом примере мы создадим конфигурацию, в которой три outbound'а объединены в балансировщик со стратегией leastLoad и мониторятся обсерваторией.
Шаг 1. Создайте хосты
Создайте в панели хосты, которые будут участвовать в инжекте. В нашем примере это:
- Virtual Host — виртуальный хост, которому будет назначен шаблон с инжектом. Он не скрыт и именно через него конечный пользователь получит конфиг.
- Balancer #1, Balancer #2, Balancer #3 — хосты, outbound'ы которых будут подставлены в шаблон.
<img src={require('./images/xray-json-advanced/1.webp').default} width="100%" style={{borderRadius: '8px'}} alt="Список хостов" />
Шаг 2. Скройте инжектируемые хосты
Откройте карточку каждого хоста-балансировщика (Balancer #1, #2, #3), перейдите в раздел Расширенные и включите переключатель Скрыть хост.
Скрытые хосты не попадают в обычную подписку — они доступны только через механизм инжекта.
<img src={require('./images/xray-json-advanced/3.webp').default} width="100%" style={{borderRadius: '8px'}} alt="Скрытие хостов-балансировщиков" />
Шаг 3. Создайте шаблон подписки
Создайте шаблон подписки типа XRAY_JSON. В нём опишите полную конфигурацию: dns, routing, inbounds, outbounds, burstObservatory и другие нужные секции.
В массив outbounds поместите только статические outbound'ы (direct, block) — outbound'ы инжектируемых хостов будут добавлены автоматически.
На корневом уровне JSON добавьте объект remnawave с селектором скрытых хостов.
Пример шаблона
{
//highlight-next-line-green
"remnawave": {
//highlight-next-line-green
"injectHosts": [
//highlight-next-line-green
{
//highlight-next-line-green
"selector": {
//highlight-next-line-green
"type": "uuids",
//highlight-next-line-green
"values": [
//highlight-next-line-green
"8478b271-95d3-4312-85ae-ecf63fb53d1d",
//highlight-next-line-green
"d31d6161-1315-4c1e-9a4b-141ab1c022f6",
//highlight-next-line-green
"5749f69e-cd1b-4012-9407-450434085196"
//highlight-next-line-green
]
//highlight-next-line-green
},
//highlight-next-line-green
"tagPrefix": "proxy"
//highlight-next-line-green
}
//highlight-next-line-green
]
},
"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": [
{
"type": "field",
"protocol": ["bittorrent"],
"outboundTag": "direct"
},
{
"type": "field",
"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"]— обсерватория будет мониторить все outbound'ы, тег которых начинается сproxy(т. е.proxy,proxy-2,proxy-3)."selector": ["proxy"]— балансировщикSuper_Balancerбудет распределять трафик между теми же outbound'ами.- В
outboundsшаблона указаны толькоdirectиblock— outbound'ы хостов добавятся автоматически перед ними.
Шаг 4. Назначьте шаблон виртуальному хосту
Откройте карточку виртуального хоста (Virtual Host), перейдите в раздел Расширенные и в поле Шаблон Xray JSON выберите созданный шаблон.
Убедитесь, что переключатель Скрыть хост для виртуального хоста выключен — он должен быть видим в подписке.
<img src={require('./images/xray-json-advanced/2.webp').default} width="100%" style={{borderRadius: '8px'}} alt="Назначение шаблона виртуальному хосту" />
Шаг 5. Результат
При запросе подписки панель автоматически:
- Возьмёт шаблон, назначенный виртуальному хосту.
- Удалит из него объект
remnawave. - Для каждой группы в
injectHostsвыберет скрытые хосты поselectorи соберёт их outbound'ы. - Подставит outbound'ы в начало массива
outbounds. - Установит
remarksиз примечания виртуального хоста.
Итоговый конфиг, который получит клиент
[
{
"dns": {
"servers": ["1.1.1.1", "1.0.0.1"],
"queryStrategy": "UseIP"
},
"routing": {
"rules": [
{
"type": "field",
"protocol": ["bittorrent"],
"outboundTag": "direct"
},
{
"type": "field",
"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": [
{
//highlight-next-line-green
"tag": "proxy",
"protocol": "vless",
"settings": {...omitted...},
"streamSettings": {...omitted...}
},
{
//highlight-next-line-green
"tag": "proxy-2",
"protocol": "vless",
"settings": {...omitted...},
"streamSettings": {...omitted...}
},
{
//highlight-next-line-green
"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'а (
proxy,proxy-2,proxy-3) подставлены в начало массиваoutbounds, передdirectиblock. "selector": ["proxy"]в балансировщике автоматически захватил все три outbound'а, поскольку их теги начинаются сproxy(префиксное сопоставление)."subjectSelector": ["proxy"]в обсерватории аналогично подхватил все три outbound'а для мониторинга."remarks": "Virtual Host"— взято из примечания виртуального хоста.
::::note Адрес виртуального хоста и реальный инбаунд
Виртуальный хост в этом сценарии служит «обёрткой» для шаблона и метаданных (примечание, описание сервера), а не реальной точкой подключения.
В его настройках можно указать любой адрес (например, balancer.host.com) - он не участвует в реальном подключении пользователя.
Фактическая точка входа - это конкретный инбаунд инжектируемых хостов. Важно, чтобы у пользователя, запрашивающего подписку, был доступ к этому инбаунду через сквады, иначе виртуальный хост в подписке у него вообще не появится.
Фактические параметры подключения (адреса, порты, ключи и т. д.) берутся из инжектируемых хостов, чьи outbound-конфигурации подставляются в итоговый конфиг на стороне клиента.
::::
Важные замечания
- Виртуальный хост должен быть включён и не скрыт. Именно он определяет, какой шаблон будет использован, и от него берутся
remarksиdescription. - Инжектируемые хосты должны быть включены. По умолчанию выбираются только скрытые хосты (
selectFrom: "HIDDEN"). Это поведение можно изменить на"NOT_HIDDEN"или"ALL". Если хост выключен или не найден по селектору — он будет пропущен. - Все участвующие хосты должны быть доступны конечному пользователю — инбаунд, к которому они привязаны, должен быть включён в сквад пользователя.
- Объект
remnawaveудаляется из итогового конфига — клиент его не увидит. - Outbound'ы добавляются в начало массива
outbounds. Если включёнaddVirtualHostAsOutbound, outbound виртуального хоста с тегомproxyидёт первым, затем — инжектированные, затем — статические outbound'ы из шаблона (direct,block). - Порядок хостов определяет порядок outbound'ов и присваиваемые им теги. Для селектора
uuids— порядок UUID в массивеvalues. ВместоtagPrefixможно использоватьuseHostRemarkAsTagилиuseHostTagAsTag, чтобы теги формировались из свойств хостов. - Выбор шаблона и скрытие хоста находятся в разделе Расширенные в карточке хоста.