본문으로 건너뛰기

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삽입 그룹의 배열. 각 그룹에는 호스트를 선택하는 셀렉터와 태그 형성 파라미터가 포함됩니다.
addVirtualHostAsOutboundtrue인 경우 — 가상 호스트가 outbounds 배열 시작 부분에 proxy 태그가 있는 outbound로 추가됩니다. 기본값은 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의 태그 접두사. 태그 형성 규칙 참조.
useHostRemarkAsTagtrue인 경우 — outbound의 태그는 호스트의 비고 (remark)가 됩니다.
useHostTagAsTagtrue인 경우 — 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와 동일한 태그를 받습니다. 그 이후 각 호스트는 2번부터 시작하여 {tagPrefix}-{N} 태그를 받습니다.

tagPrefix: "proxy"로 세 개의 호스트가 있을 때의 예:

순서outbound 태그
1번째proxy
2번째proxy-2
3번째proxy-3

useHostRemarkAsTag — 각 outbound는 호스트의 비고 (remark)와 동일한 태그를 받습니다.

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

호스트의 비고가 "Moscow", "Petersburg", "Kazan"이면 — outbound는 Moscow, Petersburg, Kazan 태그를 받습니다.

useHostTagAsTag — 각 outbound는 호스트 태그와 동일한 태그를 받습니다. 호스트 태그가 설정되지 않은 경우, 비고가 사용됩니다.

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

Xray에서의 접두사 매칭

Xray의 selector (in routing.balancers) 및 subjectSelector (in burstObservatory) 필드는 접두사 매처로 작동합니다 — outbound 태그의 정확한 값이 아닌 시작 부분과 일치시킵니다.

예를 들어, 설정에 proxy, proxy-2, proxy-3, direct 태그를 가진 outbound가 있는 경우:

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로 사용할 수 있습니다: selector의 모든 outbound를 사용할 수 없는 경우, 트래픽이 첫 번째 호스트로 이동합니다. 이를 위해 "selector": ["proxy-"] (오직 proxy-2, proxy-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. 가상 호스트의 Outboundproxy 태그.
  2. 삽입된 outbound (injectHosts에서).
  3. 템플릿의 정적 outbound (direct, block 등).

이는 라우팅 규칙에서 메인 호스트를 통해 트래픽을 라우팅하기 위해 "outboundTag": "proxy"를 사용하고, 삽입된 호스트가 별도의 트래픽 그룹 (예: 밸런서를 통해)을 처리하는 경우에 유용합니다.

addVirtualHostAsOutboundinjectHosts와 함께 또는 없이 사용할 수 있습니다. injectHosts가 지정되지 않거나 비어 있으면 가상 호스트의 outbound만 설정에 추가됩니다.

단계별 예시: 세 호스트가 있는 밸런서

이 예시에서는 세 개의 outbound가 leastLoad 전략으로 밸런서로 결합되고 Observatory에 의해 모니터링되는 설정을 만들어보겠습니다.

1단계. 호스트 생성

삽입에 참여할 호스트를 패널에서 생성합니다. 우리의 예시에는:

  • Virtual Host — 삽입 템플릿이 할당될 가상 호스트. 숨겨져 있지 않으며 최종 사용자가 이를 통해 설정을 받습니다.
  • Balancer #1, Balancer #2, Balancer #3 — outbound가 템플릿에 삽입될 호스트들.
호스트 목록

2단계. 삽입 호스트 숨기기

각 밸런서 호스트 카드 (Balancer #1, #2, #3)를 열고 Advanced 섹션으로 이동하여 Hide Host 토글을 활성화합니다.

숨겨진 호스트는 일반 구독에 포함되지 않으며 삽입 메커니즘을 통해서만 접근 가능합니다.

밸런서 호스트 숨기기

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": [
{
"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를 모니터링합니다 (즉, proxy, proxy-2, proxy-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 (proxy, proxy-2, proxy-3)가 directblock 앞에 outbounds 배열 시작 부분에 삽입되었습니다.
  • 밸런서의 "selector": ["proxy"]가 태그가 proxy로 시작하므로 (접두사 매칭) 세 outbound를 모두 자동으로 선택했습니다.
  • Observatory의 "subjectSelector": ["proxy"]도 마찬가지로 모니터링을 위해 세 outbound를 모두 선택했습니다.
  • "remarks": "Virtual Host" — 가상 호스트의 비고에서 가져왔습니다.
노트

가상 호스트 주소와 실제 inbound

이 시나리오에서 가상 호스트는 템플릿과 메타데이터 (비고, 서버 설명)의 "래퍼" 역할을 하며, 실제 연결 지점이 아닙니다. 설정에서 임의의 주소를 지정할 수 있습니다 (예: balancer.host.com) — 이는 실제 사용자 연결에 참여하지 않습니다. 실제 진입 지점은 삽입 호스트의 특정 inbound입니다. 구독을 요청하는 사용자가 스쿼드를 통해 이 inbound에 접근할 수 있어야 합니다. 그렇지 않으면 가상 호스트가 구독에 전혀 표시되지 않습니다. 실제 연결 파라미터 (주소, 포트, 키 등)는 클라이언트 측 최종 설정에 삽입된 outbound 설정이 있는 삽입 호스트에서 가져옵니다.

중요 사항

  • 가상 호스트는 활성화되어 있고 숨겨지지 않아야 합니다. 어떤 템플릿이 사용될지 결정하며, remarksdescription은 여기서 가져옵니다.
  • 삽입 호스트는 활성화되어 있어야 합니다. 기본적으로 숨겨진 호스트만 선택됩니다 (selectFrom: "HIDDEN"). 이 동작은 "NOT_HIDDEN" 또는 "ALL"로 변경할 수 있습니다. 호스트가 비활성화되었거나 셀렉터로 찾을 수 없으면 건너뜁니다.
  • 모든 참여 호스트는 최종 사용자에게 접근 가능해야 합니다 — 그들이 바인딩된 inbound가 사용자의 스쿼드에 활성화되어 있어야 합니다.
  • remnawave 객체는 제거됩니다 — 클라이언트는 볼 수 없습니다.
  • Outbound는 시작 부분에 추가됩니다. addVirtualHostAsOutbound가 활성화된 경우, proxy 태그가 있는 가상 호스트의 outbound가 먼저 오고, 그 다음 삽입된 것들, 그 다음 템플릿의 정적 outbound (direct, block)가 옵니다.
  • 호스트의 순서는 outbound의 순서와 할당된 태그를 결정합니다. uuids 셀렉터의 경우 — values 배열의 UUID 순서. tagPrefix 대신 useHostRemarkAsTag 또는 useHostTagAsTag를 사용하여 호스트 속성에서 태그를 형성할 수 있습니다.
  • 템플릿 선택 및 호스트 숨기기는 호스트 카드의 Advanced 섹션에 있습니다.