--- sidebar_position: 3 title: 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](#addVirtualHostAsOutbound). | Поле `injectHosts` — это **массив** групп инжекта. Каждая группа содержит **селектор** для выбора хостов и собственный `tagPrefix`: ```json "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'ов. ```json "selector": { "type": "uuids", "values": [ "8478b271-95d3-4312-85ae-ecf63fb53d1d", "d31d6161-1315-4c1e-9a4b-141ab1c022f6" ] } ``` #### remarkRegex Выбирает хосты, у которых **примечание** (remark) совпадает с регулярным выражением. Синтаксис — JavaScript RegExp. ```json "selector": { "type": "remarkRegex", "pattern": "^Балансер" } ``` Пример выше выберет все скрытые хосты, чьё примечание начинается с «Балансер» (например, «Балансер #1», «Балансер RU»). #### tagRegex Выбирает хосты, у которых **тег хоста** (поле tag в настройках хоста) совпадает с регулярным выражением. ```json "selector": { "type": "tagRegex", "pattern": "^balancer-" } ``` Пример выше выберет все скрытые хосты с тегом, начинающимся на `balancer-`. #### sameTagAsRecipient Выбирает все скрытые хосты, у которых **тег хоста** совпадает с тегом виртуального хоста. Не требует дополнительных параметров. ```json "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) хоста. ```json { "selector": { "type": "tagRegex", "pattern": "^ru-" }, "useHostRemarkAsTag": true } ``` Если хосты имеют примечания «Москва», «Питер», «Казань» — outbound'ы получат теги `Москва`, `Питер`, `Казань`. **`useHostTagAsTag`** — каждый outbound получает тег, равный **тегу хоста**. Если тег хоста не задан, используется его примечание. ```json { "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`: ```json "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` будет выглядеть так: 1. **Outbound виртуального хоста** с тегом `proxy`. 2. **Инжектированные outbound'ы** (из `injectHosts`). 3. **Статические outbound'ы** из шаблона (`direct`, `block` и т. д.). Это полезно, когда в routing-правилах используется `"outboundTag": "proxy"` для направления трафика через основной хост, а инжектированные хосты обслуживают отдельные группы трафика (например, через балансировщики). :::tip `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` с селектором скрытых хостов. #### Пример шаблона ```json { //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** выберите созданный шаблон. Убедитесь, что переключатель **Скрыть хост** для виртуального хоста **выключен** — он должен быть видим в подписке. Назначение шаблона виртуальному хосту ### Шаг 5. Результат При запросе подписки панель автоматически: 1. Возьмёт шаблон, назначенный виртуальному хосту. 2. Удалит из него объект `remnawave`. 3. Для каждой группы в `injectHosts` выберет скрытые хосты по `selector` и соберёт их outbound'ы. 4. Подставит outbound'ы **в начало** массива `outbounds`. 5. Установит `remarks` из примечания виртуального хоста. #### Итоговый конфиг, который получит клиент ```json [ { "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`, чтобы теги формировались из свойств хостов. - **Выбор шаблона и скрытие хоста** находятся в разделе **Расширенные** в карточке хоста.