Разделение исходящего и входящего трафика по разным IP-адресам в XRAY

от автора

В наше непростое время… Хотя, к чему все это? Все в курсе. Переходим к технической части.

Многие знают, что XRAY умеет получать запрос от клиента на один ip адрес, а отправлять ответ с другого ip адреса. Документации на этот счет в интернете крайне немного (ну или я не умею искать). Есть образцы базовых настроек в примерах на GitHub проекта XTLS и несколько упоминаний о существовании такой фишки в статьях на Хабре без подробностей реализации. Я же попробую описать здесь теорию и настройку разделения входящего и исходящего трафика подробно и с примерами.

Зачем все это нужно?

  1. Теоретически, это усложняет поведенческий анализ трафика со стороны DPI. Паттерны ломаются и, в теории, это помогает обойти блокировки. Но это не точно.

  2. Если такая фича существует, ее нужно запилить и посмотреть как все это работает на практике. Чисто из научного интереса.

На чем будем настраивать?

На сервере стоит Angie (версия 1.11.6) в качестве реверсивного прокси, за ним прячется панель 3x-ui (версия 3.4.0), которая управляет XRAY (версия 26.6.22). Будем исходить из этой конфигурации.

Можно ли реализовать разделение трафика без реверсивного прокси?

Все упирается в расшифровку двух разных потоков SSL в рамках одного входящего подключения в XRAY. Умеет он такое или нет, я информации не нашел. В настройках подключения есть возможность указать несколько SSL сертификатов, но нет возможности указать несколько SNI, так что возможно, это будет работать только для ip без доменных имен. Скажу честно, я не пробовал.

Важная деталь: у Вашего сервера обязательно должно быть два внешних ip адреса. Желательно из разных подсетей, хотя различие подсетей не играет никакой роли. Большинство хостеров раздает несколько ip на один VDS. У меня эта опция стоит 5 руб в день за каждый дополнительный адрес. Можно делать настройки напрямую с ip адресами, но, для красоты и наглядности, предлагаю к каждому ip привязать свой поддомен. У нас это будут upload.domain.com и download.domain.com. Привязка делается в настройках DNS зоны у регистратора доменов. Интересное наблюдение. Несмотря на то, что оба моих ip адреса на одном и том же VDS, выданы одним и тем же хостером и находятся физически в одном и том же датацентре, 2ip.ru показывает, что один ip адрес расположен в Москве, а второй в Новосибирске, что забавно.

Можно использовать разные VDS для разделения трафика. Физически это могут быть разные машины в разных дата-центрах в разных странах на разных континентах. Для этого нужно с проксировать весь входящий трафик с download сервера на upload сервер с помощью любого реверсивного прокси. На download сервере даже XRAY не нужен. О том как запилить такое проксирование есть ролик на замедленном Youtube на примере Caddy. Но тут есть несколько моментов.

  1. Если оба сервера находятся внутри локальной сети в одном дата-центре, то это ничем не отличается от варианта два ip на одном VDS, а если нет разницы, зачем платить больше?

  2. Если оба сервера находятся в разных дата-центрах, мы получаем дополнительные накладные расходы в виде задержек трафика между серверами без видимой целесообразности, ибо DPI ломается от различия ip адресов, а не от их географического расположения. Опять же, два сервера минимум в два раза дороже, чем один сервер с двумя ip адресами.

Поэтому из экономии будем рассматривать вариант один сервер — два ip.

Итак, погнали. Настраиваем Angie.

Создаем файл xray.conf в папке /etc/angie/http.d. Имя файла может быть любым. Расширение строго conf

xray.conf
server {    listen x.x.x.1:443 ssl;    server_name upload.domain.com;    http2 on;    ssl_certificate /etc/letsencrypt/live/upload.domain.com/fullchain.pem;    ssl_certificate_key /etc/letsencrypt/live/upload.domain.com/privkey.pem;    ssl_protocols TLSv1.2 TLSv1.3;    ssl_ciphers TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305;    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;    resolver 1.1.1.1 8.8.8.8 valid=300s;    resolver_timeout 5s;    ssl_session_cache shared:SSL:10m;    ssl_session_timeout 1d;    ssl_session_tickets on;     client_header_timeout 5m;    keepalive_timeout 5m;   location /api/v1/data/ {        client_max_body_size 0;                proxy_pass http://unix:/dev/shm/xrxhd.socket;                proxy_set_header Host $host;        proxy_set_header X-Real-IP $remote_addr;        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;        proxy_set_header X-Forwarded-Proto $scheme;                proxy_http_version 1.1;        proxy_set_header Connection "";        proxy_buffering off;        proxy_cache off;                proxy_connect_timeout 60s;        proxy_read_timeout 315s;        proxy_send_timeout 5m;   }}server {    listen x.x.x.2:443 ssl;    server_name download.domain.com;    http2 on;    ssl_certificate /etc/letsencrypt/live/download.domain.com/fullchain.pem;    ssl_certificate_key /etc/letsencrypt/live/download.domain.com/privkey.pem;    ssl_protocols TLSv1.2 TLSv1.3;    ssl_ciphers TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305;    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;    resolver 1.1.1.1 8.8.8.8 valid=300s;    resolver_timeout 5s;    ssl_session_cache shared:SSL:10m;    ssl_session_timeout 1d;    ssl_session_tickets on;     client_header_timeout 5m;    keepalive_timeout 5m;   location /api/v1/data/ {        client_max_body_size 0;                proxy_pass http://unix:/dev/shm/xrxhd.socket;                proxy_set_header Host $host;        proxy_set_header X-Real-IP $remote_addr;        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;        proxy_set_header X-Forwarded-Proto $scheme;                proxy_http_version 1.1;        proxy_set_header Connection "";        proxy_buffering off;        proxy_cache off;                proxy_connect_timeout 60s;        proxy_read_timeout 315s;        proxy_send_timeout 5m;   }}

Пояснения к настройкам:

  1. Для NGINX настройки аналогичные.

  2. Вместо x.x.x.1 и x.x.x.2 пишем внешние ip адреса вашего сервера. Если вы не привязывали доменные имена, поле server_name опускаете (вообще не пишете).

  3. Сертификаты для доменов или ip адресов предварительно нужно получить самостоятельно у Let’s Encript или кого угодно еще.

  4. ssl_ciphers и add_header — не догма, пишите как считаете нужным.

  5. Разделы location должны быть аналогичные для обоих ip адресов, включая путь /api/v1/data/ (любой, но одинаковый. И не забудьте слеш в конце) и файл сокета.

  6. Если не хотите использовать сокет, для общения с XRAY, в proxy_pass можно прописать адрес и порт: 127.0.0.1:4443. Порт может быть любым свободным, он дальше будет использоваться при настройке XRAY.

Создаем входящее подключение для XRAY

Через панель 3x-ui: Входящие — Создать подключение.

Вкладка Основное:

Примечание: все что угодно. Это название подключения.

Протокол: vless

Адрес: /dev/shm/xrxhd.socket,0666. Здесь внимательно. Адрес сокета должен совпадать с адресом сокета в настройках реверсивного прокси. После запятой идут права доступа к файлу сокета, их лучше оставить как в примере. Если в настройках реверс прокси вы указывали 127.0.0.1:4443, то здесь пишем 127.0.0.1

Порт. Если используете сокет, ставите 0 или 1, если 127.0.0.1:4443, ставите 4443 (или какой указали в настройках реверсивного прокси).

Вкладка Поток:

Транспорт — XHTTP.

Путь: /api/v1/data — берем из секции location настроек реверсивного прокси. В этом случае без слеша в конце.

Режим: auto — это важно.

Остальное оставляем как есть.

Вкладка Безопасность: выбираем Нет. За расшифровку SSL у нас отвечает реверсивный прокси.

Вкладка Сниффинг: Включить, выбрать все что есть HTTP, TLS, QUIC, FAKEDNS.

Если выше не упомянуты какие-то поля настроек — оставляем их без изменений.

Дальше добавьте клиента в получившееся входящее подключение (раздел панели Клиенты — Добавить клиентов — Привязанные входящие).

В итоге получаем вот такой шаблон входящего подключения:

{  "listen": "/dev/shm/xrxhd.socket,0666",  "port": 1,  "protocol": "vless",  "tag": "in-1-tcp",  "settings": {    "clients": [      {        "id": "xxxxxxxxxxxxxxxx",        "email": "xxxxx",        "flow": "",        "limitIp": 0,        "totalGB": 0,        "expiryTime": 0,        "enable": true,        "tgId": 0,        "subId": "xxxxxx",        "comment": "",        "reset": 0,        "created_at": 1756652179000,        "updated_at": 1782274393000      }    ],    "decryption": "none",    "encryption": "none"  },  "sniffing": {    "enabled": true,    "destOverride": [      "http",      "tls",      "quic",      "fakedns"    ]  },  "streamSettings": {    "network": "xhttp",    "xhttpSettings": {      "path": "/api/v1/data",      "host": "",      "mode": "auto",      "xPaddingBytes": "100-1000",      "scMaxEachPostBytes": "1000000",      "scMaxBufferedPosts": 30,      "scStreamUpServerSecs": "20-80"    },    "security": "none"  }}

На этом с настройками сервера все. Настройка фаервола, маршрутизации в XRAY и прочие настройки на сервере не относятся к делу, а значит выходят за рамки и без того скучной статьи. ИИ в помощь.

Для чистоты эксперимента проверять делится наш трафик или не делится будем на чистом XRAY без графических клиентов. К сторонним клиентам мы еще вернемся. Как известно, бинарник XRAY одновременно является и клиентом и сервером, поэтому качаем последнюю версию под свою платформу (Windows или Linux роли не играет) прямо с GitHub разработчика. Создаем файл конфигурации для клиента config.json.

Примерно такой
{  "log": {    "access": "access.log",    "error": "error.log",    "loglevel": "debug",    "dnsLog": true  },  "inbounds": [    {      "listen": "127.0.0.1",      "port": 2080,      "protocol": "socks",      "settings": {        "udp": true      },      "tag": "socks-in"    },    {      "listen": "127.0.0.1",      "port": 2081,      "protocol": "http",      "tag": "http-in"    }  ],  "outbounds": [    {      "protocol": "vless",      "settings": {        "vnext": [          {            "address": "upload.domain.com",            "port": 443,            "users": [              {                "id": "xxxxxxxxxxxxx",                "security": "auto",                "encryption": "none"              }            ]          }        ]      },      "tag": "vless-out",      "streamSettings": {        "network": "xhttp",        "security": "tls",        "tlsSettings": {          "allowInsecure": false,          "serverName": "upload.domain.com",          "alpn": [            "h2",            "http/1.1"          ],          "fingerprint": "firefox"        },        "xhttpSettings": {          "path": "/api/v1/data",          "host": "upload.domain.com",          "mode": "auto",          "xPaddingBytes": "100-1000",          "downloadSettings": {            "address": "download.domain.com",            "port": 443,            "network": "xhttp",            "security": "tls",            "tlsSettings": {              "serverName": "download.domain.com",              "alpn": [                "h2",                "http/1.1"              ],              "fingerprint": "firefox"            },            "xhttpSettings": {              "path": "/api/v1/data",              "host": "download.domain.com",              "mode": "auto"            }          }        }      }    }  ]}

Что важно пояснить в конфигурации.

Во всех секциях, вне “downloadSettings” указываем домен upload.domain.com или ip адрес x.x.x.1 из настроек реверсивного прокси на сервере. Внутри “downloadSettings” соответственно download.domain.com или ip адрес x.x.x.2.

“mode”: “auto” — во всех случаях. Можно в разделе “xhttpSettings”: для исходящего домена upload.domain.com поэкспериментировать с “mode”: “stream-up” или “mode”: “packet-up”, но auto работает точно. “mode”: “stream-one” в нашем случае гарантированно работать не будет. Такова жизнь.

“loglevel”: “debug” — важно указать именно debug, так как нам нужно проверить разделение трафика, а не только ошибки.

Получившийся файл config.json кладем рядом с бинарником XRAY и запускаем из командной строки. Не забудьте сначала в командной строке перейти в папку, где лежит XRAY (cd … и вот это вот все).

xray --config config.json

Дальше идет описание проверки из под Windows.

Заходим в настройки Windows -> Сеть и Интернет -> Прокси-сервер -> Настройка прокси вручную -> Настройка. Использовать прокси-сервер -> Вкл. IP адрес прокси-сервера 127.0.0.1. Порт 2081 берем из настроек файла клиента (раздел с “protocol”: “http”). Сохраняем, запускаем браузер, открываем 2ip.ru видим ip адрес сервера. Ну или не видим, но чаще видим. Если не видим, проверяем, что сделали не так.

Теперь открываем файл error.log. Он должен появиться в той же папке, что и бинарник XRAY. В этом файле важно найти строки:

transport/internet/splithttp: XHTTP is dialing to tcp:upload.domain.com:443, mode packet-up, HTTP version 2, host upload.domain.comtransport/internet/splithttp: XHTTP is downloading from tcp:download.domain.com:443, mode stream-down, HTTP version 2, host download.domain.com

Должны быть обе строки. Могут идти не подряд, но обязательно должны быть обе. Если в логе есть только первая строка (XHTTP is dialing to tcp …) — ничего не получилось и весь трафик идет через один ip. Идем в начало статьи, смотрим где накосячили с настройками.

Для тех кто в Linux можно проверить работоспособность нашей поделки через curl. XRAY предварительно должен быть запущен в качестве сервиса или в другом терминале. Номер порта берем из настроек файла клиента (раздел с “protocol”: “socks”).

curl --socks5 127.0.0.1:2080 https://google.com

Дальше, по аналогии с Windows, бежим в логи и ищем две заветные строчки.

Как можно проверить еще, ну вот чтобы вообще на 100%? На примере Windows.

Качаем и запускаем TCPView прямо с сайта Microsoft. В это время у нас уже запущен из командной строки XRAY и включен системный прокси в настройках Windows. В интерфейсе TCPView вверху в поле Search пишем xray, чтобы оставить отображение трафика только одного процеса. Открываем или обновляем в браузере любую страницу. В таблице ниже мы должны увидеть три ip адреса: 127.0.0.1, x.x.x.1 и x.x.x.2. Если видим только два: 127.0.0.1 и x.x.x.1, ничего не работает и день прожит зря. Но, надеюсь, это не про нас.

Что касается графических клиентов для XRAY. Финт с разделением потоков удалось провернуть только на клиенте V2RayN под Windows. Пробовал еще Exclave на Андроиде и Passwall2 на openWRT. В двух последних случаях настройки “downloadSettings” игнорировались и все шло в один поток. Во всех клиентах блок “downloadSettings” я добавлял в раздел «Дополнительные параметры XHTTP (Extra)» в настройках подключения. Просто вставлял блок в соответствующее поле:

"downloadSettings": {            "address": "download.domain.com",            "port": 443,            "network": "xhttp",            "security": "tls",            "tlsSettings": {              "serverName": "download.domain.com",              "alpn": [                "h2",                "http/1.1"              ],              "fingerprint": "firefox"            },            "xhttpSettings": {              "path": "/api/v1/data",              "host": "download.domain.com",              "mode": "auto"            }          }

В случае V2RayN это сработало, Exclave и Passwall2 просто проигнорировали эти настройки, хотя само соединение поднималось. Если у кото-то получится разделить трафик на Андроиде или openWRT, будет очень интересно почитать.

В заключении добавлю, что вся информация предоставлена исключительно в образовательных и научных целях. Ни в коем случае не используйте полученные знания для нарушения закона и доступа к богомерзким запрещенным ресурсам в сети интернет. Да прибудет с Вами сила. Всем бобра.

ссылка на оригинал статьи https://habr.com/ru/articles/1051380/