habrahabr

XRay (с VLESS/XTLS): проброс портов, реверс-прокси, и псевдо-VPN

  • вторник, 21 ноября 2023 г. в 00:00:12
https://habr.com/ru/articles/774838/

Я уже написал тут много статей на тему установки и настройки прокси-серверов XRay с недетектируемыми протоколами Shadowsocks-2022, VLESS (с XTLS), и т.п. И один из очень часто поднимаемых в комментариях вопросов звучит так: можно ли с использованием XRay-прокси как-то организовать проброс портов или получать доступ к внутренностям корпоративной сети? Можно, и сейчас я расскажу как.

XRay поддерживает механизм под названием "reverse proxy", что в купе с богатыми возможностями настройки правил маршрутизации позволяет сделать довольно много интересных схем. Механизм в документации упомянут как "...in the testing phase" и "may have some issues", но я попробовал, и все работает.

Традиционная нейрокартинка для отвлечения внимания
Традиционная нейрокартинка для отвлечения внимания

Итак, что же можно сделать с помощью реверс-проксирования?

  1. Можно получать доступ к каким-либо сервисам на хосте за NAT'ом или строгим фаерволом, и даже более того - можно получать доступ к сервисам на других устройствах в локальной сети, к которой имеет доступ этот самый хост за NAT'ом или файерволом.

  2. Можно маршрутизировать весь (или некоторый в зависимости от настроенных правил) трафик на хост за NAT'ом или фаерволом и выпускать его оттуда в Интернет.
    Например, вы проживаете за границей, хотите оплачивать счета за ЖКХ вашей недвижимости оставшейся России, но сервис оплаты не пускает вас с забугорных IP и не пускает вас с IP-адресов даже российских VPS-хостеров. Тогда можно поставить у кого-нибудь из друзей или родственников в РФ преднастроенный роутер или одноплатник типа Raspberry Pi, который подключится к вашему прокси-серверу, а вы, в свою очередь, через прокси-сервер сможете достучаться до этого роутера/р-пишки и выйти через него во внешний интернет как обычный пользователь, находящийся в России - и всем ресурсам будет виден IP-адрес российского домашнего интернет-провайдера.

  3. Можно обманывать тупые DPI, фильтрующие подключения по "белым спискам" в одну сторону. Например, от вас к серверу X подключиться нельзя (потому что его IP-адрес забанен), а с сервера X к вам - можно (когда у вас есть белый IP-адрес, пусть даже не статический, а с DynDNS). Тогда сделаем так, чтобы сервер X подключался к вам, а вы выходили через него в свободный интернет.

  4. Можно выборочно пробрасывать порты, например, все подключения на 80 порт прокси-сервера будут переадресовываться на 80 (или любой другой) порт "изолированного" хоста или еще куда-то дальше.

  5. Можно даже теоретически попробовать соорудить псевдо-VPN, чтобы подключенные клиенты прокси-сервера могли коммуницировать друг с другом.

Итак, поехали. Суть механизма reverse-proxy проста. Есть у нас прокси-сервер на каком-нибудь VPS, доступный для всего интернета. Нам через него надо попасть на какой-нибудь хост, который не доступен из всего интернета - например, он находится за NAT'ом без своего белого IP.

Решение простое: нужно сделать так, чтобы не сервер пытался достучаться до хоста (это невозможно), а хост первым подключился к серверу, а потом по уже установленному подключению запросы будут бегать в противоположном направлении. Это и есть reverse proxy.

Далее нужно разобраться с терминологией, которая используется в документации и конфигах XRay, потому что там все немного не очевидно и можно запутаться. Нужно знать и понимать три слова:

"client" - это клиент (логично, да), которому надо получить доступ куда-то или выйти в Интернет;
"portal" - это ваш прокси-сервер на VPS, связующее звено между client'ом и bridge'м;
"bridge" - это другой xray-клиент, изолированный за NAT'ом и фаерволом, к которому надо получить доступ.

-
-


Слово "bridge" может запутать, но скорее всего такое название разработчики решили использовать, потому что bridge является точкой соприкосновения внешнего интернета и ресурсов внутри сети - то есть финальной точкой назначения до которой мы хотим достучаться, может быть не сам bridge, а какие-то хосты в его локалке.

А теперь давайте разбираться, как это все настроить. Конфигурация reverse proxy описана в документации XRay: https://xtls.github.io/en/config/reverse.html, но описание довольно куцее. Еще есть гит-репа с примерами настройки реверс-прокси: https://github.com/XTLS/Xray-examples/blob/main/ReverseProxy/README.ENG.md

В качестве транспортного протокола может использоваться любой, который поддерживается XRay'ем. Я буду использовать в примерах для простоты обычный Shadowsocks, но ровно то же самое можно сделать и с VLESS, и с VLESS с Reality, и с VLESS через вебсокеты и CDN, и вообще как угодно - см. мои предыдущие статьи.

Начнем с настройки сервера ("portal")

{
    "reverse": {
      "portals": [
        {
          "tag": "portal",
          "domain": "reverse.hellohabr.com"
        }
      ]
    },
    "inbounds": [
      {
        "port": 5555,
        "tag": "incoming",
        "protocol": "shadowsocks",
        "settings": {
          "method": "2022-blake3-aes-128-gcm",
          "password": "...",
          "network": "tcp,udp"
         }
      }],
     "outbounds": [
        {
            "protocol": "freedom",
            "tag": "direct"
        },
        {
            "protocol": "blackhole",
            "tag": "block"
        }],
     "routing": {
        "rules": [
            {
              "type": "field",
              "inboundTag": ["incoming"],
              "outboundTag": "portal"
            }]
     }
  }

И давайте разбираться, что все это значит. В inbound у нас все как обычно - описание параметров для входящих соединений, простой shadowsocks. А вот дальше начинается интересное.

В начале конфига в секции "reverse" и объявляем один объект в массиве "portals", назначая ему тег "portal" и виртуальный домен "reverse.hellohabr.com". Этот домен именно виртуальный - он может не существовать, а правильнее сказать, он не должен быть каким-то из реально существующих доменов. Он используется только для того, чтобы XRay понял, что конкретно вот этот входящий запрос (с таким доменом) - не что-то, что нужно обработать как обычно и выпустить в интернет, а специальный запрос на установление соединения от bridge'а для того чтобы поднять reverse proxy.

Самое важное тут в routing - rules. Правило маршрутизации говорит о том, что все запросы, которые приходят от клиентов, нужно переадресовывать на reverse-proxy (тег "portal").

Естественно, если у нас другие пожелания, можно это правило маршрутизации немножко усложнить - например, переадресовывать на portal только запросы к определенным IP-адресам (диапазонам IP-адресов) или хостнеймам, а все остальное отправлять сразу в интернет (outbound "freedom"), или не пускать никуда (outbound "block"). Про правила маршрутизации можно почитать в документации XRay, плюс я еще немного касался этой темы в своем недавнем FAQ.

Дальше настраиваем "изолированного клиента" ("bridge")

{
  "reverse": {
    "bridges": [
      {
        "tag": "bridge",
        "domain": "reverse.hellohabr.com"
      }]
    },
    "outbounds": [
    {
      "protocol": "shadowsocks",
      "settings": {
        "servers": [{
          "address": "....",
          "port": 5555,
          "method": "2022-blake3-aes-128-gcm",
          "password": "...",
         }]
      },
      "tag": "outgoing"
    },
    {
      "protocol": "freedom",
      "tag": "direct"
    },
    {
      "protocol": "blackhole",
      "tag": "block"
    }],
    "routing": {
      "rules": [
      {
        "type": "field",
        "inboundTag": ["bridge"],
        "domain": ["full:reverse.hellohabr.com"],
        "outboundTag": "outgoing"
      },
      {
        "type": "field",
        "inboundTag": ["bridge"],
        "outboundTag": "direct"
      }]
    }
  }

В outbounds у нас тоже все просто - настройки подключения к серверу на VPS, который мы настроили в предыдущем шаге. Тоже есть секция "reverse", но на этот раз там объявлен объект "bridge" с тегом "bridge", и тем же виртуальным служебным хостнеймом, что мы задали для portal'а на предыдущем шаге - они должны совпадать.

А вот в routing-rules у нас все чуть-чуть сложнее. Первое правило определяет, как именно мы должны подключаться к нашему прокси на VPS (порталу) - мы говорим, что все подключения от bridge с виртуальным служебным адресом должны уходить на прокси через outbound, который мы описали чуть выше в этом же конфиге.

А второе правило определяет, что мы должны сделать с запросами, которые пришли к нам с прокси-сервера (с портала). В данном случае мы сразу выпускаем их всех наружу. То есть если пунктом назначения в запросе будет IP-адрес какого-либо хоста в локальной сети, к которому подключен bridge, подключение будет установлено к этому хосту в локальной сети. Если пунктом назначения в запросе будет какой-нибудь ресурс в публичности интернете - бридж установит соединение с ним. И т.д. Также как и в прошлом пункте, эти правила можно кастомизировать под себя - например, выпускать на outbound "direct" только запросы к определенным IP-адресам или хостнеймам, а все остальное отправлять в block, если мы не хотим, чтобы через нас лазили в интернет, а могли достучаться только до туда, до куда мы разрешили.

Готово!

В принципе, на этом настройка закончена. Когда вы запустите Xray на bridge, благодаря объявленному объекту "bridge" в конфиге, он инициирует и будет поддерживать специальное служебное подключение к прокси-серверу.

Прокси-сервер на вашем VPS, благодаря объекту "bridge" в конфиге, поймет, что это подключение - не обычно, а специальное служебное для reverse-проксирования, и обработает его как надо.

Когда вы своим обычным клиентом (например, с мобильного телефона или десктопа) подключитесь к прокси-серверу и попробуете подключиться через него к какому-нибудь ресурсу, прокси (portal) следуя правилам, переадресует этот запрос на подключение на bridge, а bridge, в свою очередь, следуя своим правилам, выпустит этот вопрос в свою локальную сеть или в интернет.

Как я уже сказал ранее, вы можете настроить правила так, как надо вам - например, на вашем прокси на VPS отправлять на bridge только запросы к определенным IP-адресам или доменам, а все остальное выпускать сразу в интернет и блокировать. Можно настроить несколько разных inbound's или несколько разных пользовательских ID в рамках одного inbound'а, и применять разные правила для разных пользователей. На самом bridge (хосте за NAT'ом) тоже можно применять разные правила - например, пропускать только запросы на определенные IP, а все остальное блокировать.

Про правила маршрутизации можно почитать в документации XRay, плюс я еще немного касался этой темы в своем недавнем FAQ.

Из того, что еще может оказаться полезным: в настройках outbound'а с протоколом "freedom" можно указывать конкретный сетевой интерфейс или IP-адрес для исходящих подключений, и даже routing mark в Linux, чтобы разруливать доступ к локалке так, как нужно вам, см. документацию Xray для подробностей.

А если нужен проброс портов?

На базе описанных выше конфигов можно строить разные схемы - например, пробрасывать порты.

Допустим, нам нужно, чтобы все подключения к 80 порту portal'а переадресовывались на 80 порт bridge'а.

Добавим на portal'е специальный inbound и правило для него:

"inbounds":
[
  ....,
  {
    "tag": "web_server_forward",
    "port": 80,
    "protocol": "dokodemo-door",
    "settings": {
      "address": "127.0.0.1",
      "port": 80,
      "network": "tcp"
    }
  }
]
....
"routing": {
  "rules": [
    ....,
  
    {
      "type": "field",
      "inboundTag": ["web_server_forward"],
      "outboundTag": "portal"
    }
  ]
}

а и добавим соответствующие директивы на bridge:

"outbounds":
[
  ...,
  {
    "tag": "web_server_local",
    "protocol": "freedom",
    "settings": {
      "redirect": "127.0.0.1:80"
    }
  }
]
...
"routing": {
  "rules": [
    ...,
    {
      "type": "field",
      "inboundTag": ["bridge"],
      "outboundTag": "web_server_local"
    }
  ]
}

После этого все запросы, пришедшие на 80 порт portal'а, будут переадресованы на 80 порт bridge'а через reverse-прокси подключение.

Если ничего не работает

Если вы пытаетесь получить доступ к локальной сети через reverse proxy, но ничего не работает, и даже на сервере в логах не видно запросов, проверьте настройки клиента. Во многих клиентах по умолчанию запросы на "geoip:private" (то есть как минимум локальные IP-адреса классов A, B и C) отправляются в block.

Так же не забываем, что есть основания предполагать, что в случае чего РКН будет блокировать вообще все неопознанные протоколы (и судя по всему, они уже это делали). Поэтому вместо Shadowsocks лучше использовать VLESS с TLS, см. предыдущие статьи.

Еще, из того, с чем столкнулся я: после настройки на bridge XRay запускался, но почему-то не поднимал подключение к portal'у, и в логах была тишина.
На portal же, при попытке сходить куда-то через bridge, в логах были сообщение

v2ray.com/core/app/reverse: failed to process reverse connection > v2ray.com/core/app/reverse: empty worker list

означавшее, что portal-то и не против сходить куда-нибудь через bridge, вот только ни одного подходящего bridge'а к нему не подключилось. Я так и не понял, в чем было дело, в какой-то момент я просто снес конфиг и переписал его заново более внимательно и все заработало.

При правильной настройке при попытке подключиться, на стороне portal в логах будет что-то типа такого:


xray[1856216]: 11.11.11.11:10457 accepted 22.22.22.22:443 [incoming -> portal]
xray[1856216]: proxy/shadowsocks_2022: tunnelling request to tcp:reverse.hellohabr.com:0
xray[1856216]: app/dispatcher: taking detour [portal] for [tcp:reverse.hellohabr.com:0]
xray[1856216]: common/mux: dispatching request to udp:reverse.internal.v2fly.org:0
xray[1856216]: [33.33.33.33]:63786 accepted reverse.hellohabr.com:0 [incoming -> portal]

а на bridge:

xray[648852]: [Info] app/dispatcher: taking detour [outgoing] for [tcp:reverse.hellohabr.com:0]
xray[648852]: [Info] proxy/shadowsocks_2022: tunneling request to tcp:reverse.hellohabr.com:0 via 11.11.11.11:5555
xray[648852]: [Info] transport/internet/tcp: dialing TCP to tcp:[11.11.11.11]:5555
xray[648852]: [Info] common/mux: received request for udp:reverse.internal.v2fly.org:0

Псевдо-VPN?

Сообразительный читатель наверное догадается, что в теории таким образом можно даже сделать какое-то подобие VPN. Если на каждом клиенте настроить и outbound до прокси и bridge, а на прокси для каждого клиента создать portal и соответствующие правила (например, выдумав набор виртуальных IP-адресов и routing rules для них, типа подключения к 10.0.0.1 отправляй на этот портал, а к 10.0.0.2 на другой), то можно соорудить схему, когда клиенты смогут подключаться через прокси друг к другу. Скажу честно - я не пробовал. Может заработает, а может и нет. Если кто-то попробует и получится - расскажите. Если не получится - тоже расскажите. Из явных минусов: конфиг будет страшный, и отлаживать все это дело будет непросто. Плюс на мобильных клиентах (да и в простых десктопных) bridge-функционал просто так не настроить.

Поэтому в случае необходимости подобного, я бы предложил все-таки использовать классические VPN-протоколы, например, AmneziaWG, или OpenVPN завернутый в Cloak, или что-то из TLS VPN, о которых я расскажу в следующей статье.