В заметке опишу как организовать доступ к серверу в локальной сети через Xray. Про базовую настройку Xray можно посмотреть тут. Схема

В терминологии xray схема с реверс-прокси состоит из двух участников: portal и bridge.

  • Portal - это сервер который доступен из интернет и который выполняет роль реверс-прокси.
  • Bridge - сервер/клиент который находится в приватной сети и через который осуществляется доступ к ресурсу в приватной сети.

Допустим у нас в локальной сети есть веб-сервер без публичного IP и мы хотим опубликовать его через xray portal. Клиенты из интернет будут подключаться к публичному серверу А и через служебный канал (vless, shadowsocks…) между A и B получать доступ к приватному серверу.

Настройка Portal

На портале описываем, что данный сервер выступает в роли portal для домена reverse.proxy (любой несуществующий “виртуальный” домен)

"reverse": {
      "portals": [
        {
          "tag": "portal",
          "domain": "reverse.proxy"
        }
      ]
    },

Добавляем входящее подключение, которое будет проксировать трафик через туннель

"inbounds": [
    {
        "tag": "web_server",
        "port": 80, // порт на реверс-прокси (портале)
        "protocol": "dokodemo-door",
        "settings": {
            "network": "tcp",
            "address": "192.168.1.10", //адрес сервера на который хотим перенаправлять запросы
            "port": 80  
        },
	},
    ...// подключения для клиентов (vless, vvmess, shadowsocks и т.д.)
]

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

Вообще каждый раз настраивая xray испытываю дикое раздражение от странной логики происходящего, еще горения добавляет пустые сообщения в логах, качество документации и китайский иероглифы в issues на github 🤬️

Далее маршрутизация, тут два правила:

    "routing": {
        "rules": [
        {
            "type": "field",
            "inboundTag": ["web_server"],
            "outboundTag": "portal"
        },
        {
            "type": "field",
            "domain": ["full:reverse.proxy"],
            "outboundTag": "portal"
        },
        ]
    }
  1. Роутим входящий трафик с тэгом web_server в портал.
  2. Тут я еще раз должен сказать, что документация у проекта - говно. Второе правило написал после нескольких дней попыток всё это настроить. Без этого правила, при наличии outbound секции, конфиг не работал, в логах было, что xray не может разрешить имя reverse.proxy. Если удалить секцию outbound, т.е. ваш xray-сервер служит только как реверс-прокси, - второе правило можно не писать.

Настройка Bridge

Указываем, что второй хост выступает как бридж для того же домена. Домен должен совпадать с тем, что указан в конфиге портала. При подключении к порталу бридж будет пытаться зарегистрироваться на портале как мост для указанного домена.

"reverse": {
    "bridges": [
      {
        "tag": "bridge",
        "domain": "reverse.proxy"
      }
    ]
  },

В исходящих соединениях описываем подключение до портала и до нашего веб-сервера:

"outbounds": [
    {
      "tag": "tunnel"
      "protocol": "vless",
      "settings": {
        ...
      }
    },
    {
      "tag": "web_server_local",
      "protocol": "freedom",
      "settings": {
        "redirect": "192.168.1.10:80" // адрес вашего локального сервера
      }
    }
  ],

Маршрутизация

"routing": {
    "rules": [
        {
        "type": "field",
        "inboundTag": ["bridge"],
        "domain": ["full:reverse.proxy"],
        "outboundTag": "tunnel"
      },
      {
        "type": "field",
        "inboundTag": ["bridge"],
        "outboundTag": "web_server_local"
      },     
    ]
  }
  1. Служебный трафик для домена reverse.proxy маршрутизируем в туннель
  2. Входящий трафик пришедший из бриджа отправляем на локальный веб-сервер.

Всё. После запуска бриджа и портала, порт сервера в локалке должен быть доступен по публичному адресу портала.