Xray JSON – Advanced
Обзор
Для шаблонов подписки типа XRAY_JSON в Remnawave предусмотрены Remnawave-директивы — специальные инструкции, которые вы добавляете в JSON-шаблон. Панель обрабатывает их при генерации подписки и удаляет из итогового конфига — клиент их никогда не увидит.
На данный момент доступна директива injectHosts, позволяющая динамически подставлять outbound-конфигурации хостов в шаблон. Это полезно, когда вам нужно собрать сложную конфигурацию Xray с балансировщиками, кастомным роутингом или несколькими outbound'ами, при этом данные подключения (адрес, порт, ключи) подставятся автоматически из панели.
Представленные ниже конфигурации являются примерами для демонстрации механизма инжекта. Адаптируйте их под свои нужды.
Требуется 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). |
Необходимо указать ровно одно из трёх полей: 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"
}
Удобно, когда вы хотите автоматически группировать хосты: достаточно присвоить одинаковый тег виртуальному и инжектируемым хостам.
По умолчанию все селекторы работают только со скрытыми хостами. Чтобы изменить это поведение, используйте поле 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, ...).
Первый выбранный хост всегда получает тег без суффикса -{N} (просто proxy). Это позволяет использовать его как fallbackTag в балансировщике: если все outbound'ы из selector окажутся недоступны, трафик уйдёт на первый хост. Для этого задайте "selector": ["proxy-"] (только proxy-2, proxy-3, ...) и "fallbackTag": "proxy".
addVirtualHostAsOutbound
По умолчанию при использовании remnawave-директивы в итоговый конфиг попадают только инжектированные хосты. Сам виртуальный хост (recipient) используется только как источник remarks и serverDescription.
Если вам нужно, чтобы виртуальный хост также стал outbound'ом с тегом proxy, добавьте поле addVirtualHostAsOutbound: true на уровне объекта remnawave:
"remnawave": {
"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" для направления трафика через основной хост, а инжектированные хосты обслуживают отдельные группы трафика (например, через балансировщики).
addVirtualHostAsOutbound можно использовать совместно с injectHosts или без них. Если injectHosts не указан или пуст, в конфиг будет добавлен только outbound виртуального хоста.
Пошаговый пример: балансировщик с тремя хостами
В этом примере мы создадим конфигурацию, в которой три outbound'а объединены в балансировщик со стратегией leastLoad и мониторятся обсерваторией.
Шаг 1. Создайте хосты
Создайте в панели хосты, которые будут участвовать в инжекте. В нашем примере это:
- Virtual Host — виртуальный хост, которому будет назначен шаблон с инжектом. Он не скрыт и именно через него конечный пользователь получит конфиг.
- Balancer #1, Balancer #2, Balancer #3 — хосты, outbound'ы которых будут подставлены в шаблон.
Шаг 2. Скройте инжектируемые хосты
Откройте карточку каждого хоста-балансировщика (Balancer #1, #2, #3), перейдите в раздел Расширенные и включите переключатель Скрыть хост.
Скрытые хосты не попадают в обычную подписку — они доступны только через механизм инжекта.
Шаг 3. Создайте шаблон подписки
Создайте шаблон подписки типа XRAY_JSON. В нём опишите полную конфигурацию: dns, routing, inbounds, outbounds, burstObservatory и другие нужные секции.
В массив outbounds поместите только статические outbound'ы (direct, block) — 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": [
{
"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 выберите созданный шаблон.
Убедитесь, что переключатель Скрыть хост для виртуального хоста выключен — он должен быть видим в подписке.
Шаг 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": [
{
"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'а (
proxy,proxy-2,proxy-3) подставлены в начало массиваoutbounds, передdirectиblock. "selector": ["proxy"]в балансировщике автоматически захватил все три outbound'а, поскольку их теги начинаются сproxy(префиксное сопоставление)."subjectSelector": ["proxy"]в обсерватории аналогично подхватил все три outbound'а для мониторинга."remarks": "Virtual Host"— взято из примечания виртуального хоста.
Адрес виртуального хоста и реальный инбаунд
Виртуальный хост в этом сценарии служит «обёрткой» для шаблона и метаданных (примечание, описание сервера), а не реальной точкой подключения.
В его настройках можно указать любой адрес (например, 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, чтобы теги формировались из свойств хостов. - Выбор шаблона и скрытие хоста находятся в разделе Расширенные в карточке хоста.