Meow
2026-05-15 04:09:53 +08:00
parent 0700e722e0
commit 0fe890e9c7
12 changed files with 928 additions and 7 deletions
+299
View File
@@ -433,3 +433,302 @@ sequenceDiagram
- Подключение пользователя к публичному серверу с последующим возвратом домой через обратный туннель.
Оба сценария используют один и тот же механизм обратного соединения. Основное различие состоит в том, как публичная сторона маршрутизирует трафик и как внутренняя сторона продолжает его обрабатывать. Поняв это, можно свободно расширять схему между "пробросом порта" и "удаленным возвращением домой" под свои задачи.
## Продвинутые Приемы: Расширенная Балансировка Нагрузки
Если на публичной стороне несколько входящих подключений используют один и тот же reverse tag, в итоге все равно будет создан только один outbound. Это можно понимать как несколько линий, подвешенных к одному общему пулу доступности, где при каждом использовании случайно выбирается одна из живых линий.
Такой подход позволяет строить более гибкие конфигурации many-to-many. Если какая-то линия временно недоступна, например потому что соответствующее внутреннее устройство еще не подключилось, она просто не попадет в текущий пул доступности. Последующий трафик будет автоматически перенаправляться на другие линии, которые все еще в сети, без ручного переключения.
Иначе говоря, у `reverse.tag` с обеих сторон одна и та же семантика: "одинаковый tag объединяется в один пул соединений":
- Несколько клиентов на публичной стороне, использующих один и тот же `reverse-out`, сходятся в один пул outbound;
- Несколько VLESS outbound на внутренней стороне, использующих один и тот же `reverse-in`, сходятся в один пул inbound.
Поэтому схема естественно масштабируется до N-к-N. Более типичный реальный сценарий выглядит так: снаружи есть только один сервисный домен, например `www.example.com`, а GeoDNS отправляет посетителей на ближайший публичный сервер в Лос-Анджелесе или Токио. На внутренней стороне при этом устанавливаются соединения назад к `us-reverse.example.com` и `jp-reverse.example.com`. Затем разворачиваются два внутренних устройства: дома и в офисе. И дом, и офис подключаются и к Лос-Анджелесу, и к Токио, поэтому каждый публичный узел поддерживает пул доступности вида "дом + офис". При реальной переадресации случайно выбирается одна доступная линия из уже установленных соединений. Если одна из точек уходит офлайн, она автоматически исчезает из пула.
Ниже приведен минимальный фрагмент, в котором оставлены только части, связанные с reverse, и по-прежнему используется модель "публичный входной порт отображается на внутренний сервис".
### Публичный Сервер В Лос-Анджелесе
```json
{
"inbounds": [
{
"port": 8443,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "us-home-uuid",
"reverse": {
"tag": "reverse-out"
}
},
{
"id": "us-office-uuid",
"reverse": {
"tag": "reverse-out"
}
}
]
}
},
{
"port": 443,
"protocol": "tunnel",
"tag": "portal"
}
],
"routing": {
"rules": [
{
"inboundTag": ["portal"],
"outboundTag": "reverse-out"
}
]
},
"outbounds": [
{
"protocol": "freedom"
}
]
}
```
### Публичный Сервер В Токио
Полностью та же идея, только UUID меняются на `jp-home-uuid` и `jp-office-uuid`.
### Домашнее Внутреннее Устройство
```json
{
"routing": {
"rules": [
{
"inboundTag": ["reverse-in"],
"outboundTag": "reverse-direct"
}
]
},
"outbounds": [
{
"protocol": "freedom",
"tag": "reverse-direct",
"settings": {
"redirect": "192.168.1.123:80",
"finalRules": [
{
"action": "allow",
"network": "tcp",
"ip": "192.168.1.123",
"port": "80"
}
]
}
},
{
"protocol": "vless",
"settings": {
"address": "us-reverse.example.com",
"port": 8443,
"id": "us-home-uuid",
"reverse": {
"tag": "reverse-in"
}
}
},
{
"protocol": "vless",
"settings": {
"address": "jp-reverse.example.com",
"port": 8443,
"id": "jp-home-uuid",
"reverse": {
"tag": "reverse-in"
}
}
}
]
}
```
### Офисное Внутреннее Устройство
Полностью аналогично домашнему внутреннему устройству, только UUID меняются на `us-office-uuid` и `jp-office-uuid`, а `redirect` нужно заменить на тот внутренний сервис, который должен публиковаться из офиса.
## Продвинутые Приемы: Передача Реального IP Посетителя
Если вы хотите, чтобы внутренний Web-сервис видел реальный IP посетителя, а не определял источник как IP машины с Xray на внутренней стороне, можно включить `proxyProtocol` у outbound `freedom` на внутренней стороне. Если он выключен, backend Web-сервер обычно видит адрес самой внутренней машины с Xray. Если он включен, Xray перед пересылкой соединения на backend, указанный в `redirect`, сначала отправляет заголовок PROXY protocol, чтобы вместе с ним передать Web-серверу и реальный исходный адрес.
Например:
```json
{
"outbounds": [
{
"protocol": "freedom",
"tag": "reverse-direct",
"settings": {
"redirect": "192.168.1.123:80",
"proxyProtocol": 1,
"finalRules": [
{
"action": "allow",
"network": "tcp",
"ip": "192.168.1.123",
"port": "80"
}
]
}
}
]
}
```
В этом случае backend, на который указывает `redirect`, не может быть просто обычным HTTP listener. На нем также нужно явно включить поддержку PROXY protocol. Предположим, что Web-сервер расположен по адресу `192.168.1.123`, а адрес внутреннего Xray — `192.168.1.10`. Для `nginx`, например, конфигурация может выглядеть так:
```nginx
server {
listen 80 proxy_protocol;
server_name _;
set_real_ip_from 192.168.1.10;
real_ip_header proxy_protocol;
location / {
root /srv/www/html;
index index.html;
}
}
```
Строка `set_real_ip_from 192.168.1.10;` выше означает, что доверять нужно только PROXY protocol header, пришедшему от внутреннего Xray. `192.168.1.10` здесь приведен лишь как пример; замените его на реальный IP вашей внутренней машины с Xray. `proxyProtocol` может быть равен `1` или `2`. Если backend это поддерживает, конфигурация на стороне `nginx` остается той же.
## Продвинутые Приемы: Точная Маршрутизация По Домену И IP Посетителя На Внутренней Стороне
Если вы хотите, чтобы извне были доступны сразу несколько внутренних сайтов, и эти сайты находятся не на одной машине, можно включить `sniffing` прямо на входе обратного прокси, извлечь доменное имя из HTTP Host или TLS SNI, а затем по домену отправлять трафик в разные outbound `freedom`. Так публичная сторона сохранит только одну точку входа, а внутренняя сторона все равно сможет разводить трафик по разным хостам в зависимости от сайта.
При этом обратный прокси VLESS сохраняет реальный source IP посетителя. Когда трафик попадает во внутреннюю систему маршрутизации, можно продолжить использовать `sourceIP` для более точного контроля, например отправлять некоторые источники сразу в `blackhole` или направлять определенные источники в другую группу backend-серверов. Ниже приведен комбинированный пример:
```json
{
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"inboundTag": ["reverse-in"],
"sourceIP": ["!geoip:cn"],
"outboundTag": "reverse-block"
},
{
"inboundTag": ["reverse-in"],
"domain": ["full:admin.example.com"],
"outboundTag": "reverse-admin"
},
{
"inboundTag": ["reverse-in"],
"domain": ["domain:blog.example.com"],
"outboundTag": "reverse-blog"
},
{
"inboundTag": ["reverse-in"],
"outboundTag": "reverse-default"
}
]
},
"outbounds": [
{
"protocol": "freedom"
},
{
"protocol": "blackhole",
"tag": "reverse-block"
},
{
"protocol": "freedom",
"tag": "reverse-admin",
"settings": {
"redirect": "192.168.1.10:8443",
"proxyProtocol": 1,
"finalRules": [
{
"action": "allow",
"network": "tcp",
"ip": "192.168.1.10",
"port": "8443"
}
]
}
},
{
"protocol": "freedom",
"tag": "reverse-blog",
"settings": {
"redirect": "192.168.1.20:8080",
"proxyProtocol": 1,
"finalRules": [
{
"action": "allow",
"network": "tcp",
"ip": "192.168.1.20",
"port": "8080"
}
]
}
},
{
"protocol": "freedom",
"tag": "reverse-default",
"settings": {
"redirect": "192.168.1.30:80",
"proxyProtocol": 1,
"finalRules": [
{
"action": "allow",
"network": "tcp",
"ip": "192.168.1.30",
"port": "80"
}
]
}
},
{
"protocol": "vless",
"settings": {
"address": "yourserver.com",
"port": 8443,
"id": "ac04551d-6ebf-4685-86e2-17c12491f7f4",
"flow": "xtls-rprx-vision",
"reverse": {
"tag": "reverse-in",
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"]
}
}
}
}
]
}
```
Ключевые моменты этого примера:
- `reverse.sniffing` включается внутри `reverse` у внутреннего VLESS outbound, то есть sniffing выполняется для соединений, пришедших через вход обратного прокси;
- Правила маршрутизации проверяются по порядку, поэтому ограничительные правила вроде `sourceIP -> blackhole` нужно ставить раньше;
- `proxyProtocol` нужен только для того, чтобы передавать реальный IP посетителя дальше в backend-приложение. Если вам нужна только маршрутизация внутри Xray по `sourceIP`, его можно не включать.
С такой конфигурацией одна публичная точка входа может обслуживать сразу несколько внутренних сайтов, а внутренняя сторона при этом сохраняет возможность более гибко отбрасывать, маршрутизировать и аудировать трафик по адресу источника посетителя.
## Замечания По Безопасности
Следующие пункты больше относятся к безопасному развертыванию и контролю границ. Рекомендуется внимательно прочитать их перед использованием этой схемы в продакшене:
- UUID, используемый для обратного проксирования, нельзя разделять с обычными клиентами прямого прокси. Его нужно создавать отдельно. Кроме того, UUID для обратного прокси нужно бережно хранить: если конфигурация клиента утечет, злоумышленник может попытаться перехватить ваш reverse tunnel.
- Для соединения, используемого в сценарии внутреннего проникновения, даже при включенном `XTLS Vision` практический выигрыш сейчас в основном ограничивается такими вещами, как `padding`. Это не то же самое, что часто обсуждаемый эффект "прямого оголенного канала". Нужно ли также включать XTLS на соединении, обращенном к конечным пользователям, зависит от вашей реальной топологии и модели угроз.
- Внутренний outbound `freedom`, который принимает трафик обратного прокси, то есть привычный `direct`, желательно настраивать по принципу минимально необходимых привилегий. Сделайте outbound по умолчанию равным `blackhole`, явно маршрутизируйте только разрешенные цели в выделенный `freedom`, а затем через `finalRules` открывайте только действительно нужные адреса и порты.
- Если вы используете сервис проникновения, предоставленный кем-то другим, для удаленного доступа домой, или если вы не полностью доверяете публичному VPS, лучше не направлять трафик обратного прокси напрямую на реальные внутренние сервисы. Вместо этого можно развернуть на внутренней стороне еще один сервер с включенным `VLESS Encryption`, специально для приема такого трафика, и уже через него пересылать трафик к настоящему сервису. Это добавляет аутентификацию и защиту данных; иначе любой, кто имеет достаточный доступ к публичному серверу, потенциально сможет перемещаться по вашей внутренней сети.
- Когда трафик доставляется на внутреннюю сторону через входящие протоколы вроде `VLESS`, протокол, который routing system показывает у `Source` или `Local`, не обязательно совпадает с итоговым `Target`. При использовании условий вроде `source`, `local` или `network` ориентируйтесь на реальную форму трафика, а не на предположение, что они эквивалентны.
- HTTP-ориентированные inbounds, такие как `XHTTP` и `WebSocket`, сейчас по умолчанию читают `X-Forwarded-For`. Если перед ними нет HTTP reverse proxy, которому вы доверяете, этот заголовок может быть подделан клиентом. Поэтому не используйте его напрямую для строгих решений безопасности, например для IP whitelist, blacklist или аудиторской атрибуции.