Предыдущие статьи серии:

«Современные технологии обхода блокировок: V2Ray, XRay, XTLS, Hysteria и все‑все‑все»

«Программы‑клиенты для протоколов недетектируемого обхода блокировок сайтов: V2Ray/XRay, Clash, Sing‑Box, и другие»

С протоколами разобрались, с клиентами разобрались, теперь наконец‑то настало время рассказать о том, как же настроить свой личный прокси‑сервер с современными протоколами для обхода блокировок. Мы будем настраивать сервер на базе XRay (который является форком известного V2Ray, и еще я немного упомяну Sing‑Box) с протоколами Shadowsocks-2022 и VLESS с транспортом XTLS‑Vision и фейковым веб‑сайтом для защиты от выявления. И в качестве запасного варианта на том же сервере мы настроим fallback на VLESS+Websockets, чтобы была возможность работать через CDN типа Cloudflare, если вдруг IP-адрес вашего сервера попадет под блокировку. В конце я приведу настройки десктопных и мобильных клиентов для подключения ко всему этому.

Поехали.

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

Настройку буду описывать под Debian или Ubuntu Linux. Если у вас на VPS стоит другой дистрибутив, то там будет примерно все то же самое, хотя некоторые команды и названия пакетов могут отличаться.

Итак, допустим у нас уже есть VPS-сервер с Debian или Ubuntu в какой-нибудь заморской юрисдикции, у него есть IP-адрес, на нем настроен SSH и вообще все пока что неплохо. И еще у вас должен быть какой-нибудь домен, не обязательно платный (хотя сейчас по акциям можно зарегистрировать домен в какой-нибудь не очень популярной зоне всего за доллар-два в год), подойдет даже DynDNS. Если чего-то из вышеописанного у вас нет, советую ознакомиться этой и этой статьей, там в начале описывается базовая установка и настройка VPS-сервера с Linux и регистрация бесплатного домена через DynDNS. Ну а мы идем дальше.

Первый вариант настройки я приведу для "пустого сервера" - это если на вашем сервере нет никаких других сервисов (но потом можно будет добавить еще и веб-сайт, да). Во второй половине статьи я расскажу, как настроить XRay когда у вас на машине уже крутится веб-сервер и вы не хотите лишний раз трогать его конфигурацию. Третий вариант будет с Docker для самых маленьких.

Вариант первый, полный, подробный

Разработчики XRay подготовили скрипт, который автоматически скачивает XRay под используемую систему и создает systemd-юнит (спасибо @alegz81 что напомнил): https://github.com/XTLS/Xray-install

Устанавливается одной длинной командой

$ bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install

Единственное различие от описанного в статье при конфигурации будет в том, что конфиги XRay будут лежать не в /opt/xray, а в /usr/local/etc/xray/.

Либо же можем установить все ручками.

Идем вот сюда: https://github.com/XTLS/Xray-core/releases и скачиваем самый свежий билд XRay-core:

$ wget https://github.com/XTLS/Xray-core/releases/download/v1.8.0/Xray-linux-64.zip

Cоздаем директорию, распаковываем и делаем файл исполняемым (он поставляется в .zip-архиве, поэтому разрешения при упаковке-распаковке теряются):

$ mkdir /opt/xray
$ unzip ./Xray-linux-64.zip -d /opt/xray
$ chmod +x /opt/xray/xray

Далее создадим systemd-юнит и вставим туда следущий текст (я использую nano, вы, понятное дело, можете использовать vi и вообще все что угодно):

$ nano /usr/lib/systemd/system/xray.service
[Unit]
Description=XRay

[Service]
Type=simple
Restart=on-failure
RestartSec=30
WorkingDirectory=/opt/xray
ExecStart=/opt/xray/xray run -c /opt/xray/config.json

[Install]
WantedBy=multi-user.target
$ systemctl daemon-reload
$ systemctl enable xray

Обратите внимание - в данном случае xray запустится от пользователя root. Это не очень хорошо в плане безопасности, я сделал это так в примере для упрощения мануала, но по-хорошему нужно создать для xray отдельного пользователя, запускать его от него, не забыть выставить ему права для чтения на директории и файлы от certbot/letsencrypt (об этом чуть дальше), и чтобы была возможность повесить сервер на порт 443 или другие <1000, выставить специальную опцию на бинарник/процесс.

На этом установка XRay закончена, дальнейшие действия будут одинаковы и при ручной настройке, и при использовании скрипта.

Нам будут нужны TLS-сертификаты.

Устанавливаем certbot и запрашиваем сертификат для нашего домена (например, example.com):

$ apt install certbot
$ certbot certonly --standalone --preferred-challenges http -d example.com

Если вам нужно иметь два домена или домен и поддомен (например, один будет доступен напрямую, другой через CDN), то можно указать ещё один аргумент -d в этой команде и у вас будет сертификат сразу для двух доменов. А ещё оно поддерживает wildcards.

Certbot спросит ваш емайл на всякий случай, спросит согласны ли вы с правилами, запросит сертификат от LetsEncrypt, положит его в папочку /etc/letsencrypt и создаст правило, чтобы он обновлялся каждые 3 месяца. При каждом обновлении сертификата нужно перезапускать XRay-сервер, давайте попросим certbot делать это автоматически:

$ nano /etc/letsencrypt/renewal/example.com.conf

и там в конец добавим строку

renew_hook = systemctl reload xray

Теперь переходим к самому интересному. Создаем и редактируем конфиг:

$ nano /opt/xray/config.json # или в /usr/local/etc/xray/ в случае использования скрипта
{
  "log": {
    "loglevel": "info"
  },
  "routing": {
    "rules": [],
    "domainStrategy": "AsIs"
  },
  "inbounds": [
    {
      "port": 23,
      "tag": "ss",
      "protocol": "shadowsocks",
      "settings": {
        "method": "2022-blake3-aes-128-gcm",
        "password": "aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb",
        "network": "tcp,udp"
      }
    },
    {
      "port": 443,
      "protocol": "vless",
      "tag": "vless_tls",
      "settings": {
        "clients": [
          {
            "id": "7957c33c-d9ca-11ed-afa1-0242ac120002",
            "email": "user1@myserver",
            "flow": "xtls-rprx-vision"
          }
        ],
        "decryption": "none",
        "fallbacks": [
          {
            "path": "/myverysecretpath",
            "dest": "@vless-ws"
          },
          {
            "dest": "8080"
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "security": "tls",
        "tlsSettings": {
          "alpn": [
            "http/1.1",
            "h2"
          ],
          "certificates": [
            {
              "certificateFile": "/etc/letsencrypt/live/example.com/fullchain.pem",
              "keyFile": "/etc/letsencrypt/live/example.com/privkey.pem"
            }
          ]
        }
      },
      "sniffing": {
        "enabled": true,
        "destOverride": [
          "http",
          "tls"
        ]
      }
    },
    {
      "listen": "@vless-ws",
      "protocol": "vless",
      "tag": "vless_ws",
      "settings": {
        "clients": [
          {
            "id": "7957c33c-d9ca-11ed-afa1-0242ac120002",
            "email": "user2@myserver"
          }
        ],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "ws",
        "security": "none",
        "wsSettings": {
          "path": "/myverysecretpath"
        }
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "tag": "direct"
    },
    {
      "protocol": "blackhole",
      "tag": "block"
    }
  ]
}

На что обратить внимание. В inbounds мы задаем правила обработки входящих подключений - первым идет Shadowsocks-2022 на 23 порту (можете использовать любой другой порт, само собой). О том, что эта версия протокола именно 2022 говорит method "2022-blake3-aes-128-gcm". Ключ - любой в шестнадцатеричной форме, его длина зависит от типа шифра, в примере 128-битный шифр, если используете 256-битный, то ключ, соответственно, должен быть в два раза длиннее.

Дальше идет VLESS через TLS, стандартный порт 443. В секции "clients" задаются пользователи-клиенты, в примере он только один. ID клиента можно сгенерировать любым онлайновым UUID-генератором. Также для юзера задается опция "flow" со значением "xtls-rprx-vision", означающая что этот пользователь будет подключаться с использованием XTLS-Vision. В настройках "streamSettings" вы можете увидеть пути к сертификатам, которые мы запросили у LetsEncrypt, в пути должен быть файл соответствующий вашему домену. В "fallbacks" задаются правила о том, что делать, если юзер был не опознан, либо подключение производится не через чистый VLESS-протокол: если мы видим HTTP-запрос с URI /myverysecretpath, то передаем подключение на обработчик vless-ws, для всего остального - на порт 8080, где у нас будет висеть веб-сервер с фейковым (или даже настоящим) веб-сайтом.

И наконец, третим идет вариант VLESS через Websocket, на том же 443 порту. Таким образом, например, можно подключаться к серверу не напрямую, а через CDN, что поможет если ваш сервер вдруг заблокировали цензоры или если вы подклюяаетесь через строгий корпоративный прокси. Настройка его почти аналогична предыдущему пункту, UUID пользователя там указан тот же самый, единственные различие - нет опции "xtls-rprx-vision", потому что через CDN она работать не будет, и есть секция "wsSettings", где указан тот же секретный путь на сервере /myverysecretpath что и в fallbacks.

В комментариях к предыдущей статье упоминали, что websocket-транспорт не всегда работает надежно и эффективно, а еще при очень больших объемах передаваемого трафика Cloudflare может обидиться и начать просить перейти на платный тариф. Поэтому вместо websocket некоторые советуют использовать gRPC-транспорт. Я пробовал, и у меня не получилось нормально настроить fallback на gRPC. В комментарии к статье хабраюзер @s7eepz приложил пример настройки fallback'а на gRPC через Nginx - но важный момент, Nginx должен быть собран с gRPC-модулем. В Debian/Ubuntu и в официальных репозиториях от разработчиков он собран без него.

И как вы могли заметить, в конфиге упомянут порт 8080 для fallback. На нем у нас должен слушать веб-сервер с сайтом для маскировки. Самый просто вариант это сделать - поставить позади него nginx:

$ apt install nginx
$ nano /etc/nginx/sites-enabled/default
$ systemctl restart nginx

Где /etc/nginx/sites-enabled/default в самом простом случае будет представлять собой что-то типа такого:

server {
        listen 127.0.0.1:8080 default_server;
        listen [::1]:8080 default_server;

        root /var/www/html;
        index index.html index.htm index.nginx-debian.html;

        server_name _;

        location / {
                try_files $uri $uri/ =404;
        }
}

(главное изменение по сравнению с дефолтной конфигурацией - сервер слушает не на всех адресах, а только на localhost, и не на 80, а на 8080 порту).

После этого при попытке подключиться к вашему IP-адресу обычным браузером (то, что могут автоматически делать цензоры, пытаясь выявить прокси-сервера), отвечать будет Nginx, отдавая страницы лежащие в /var/www/html. По умолчанию там лежит заглушка, можно закинуть туда какие-нибудь странички и видео с котятками.

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

location / {
                auth_basic "Administrator’s Area";
                auth_basic_user_file /etc/htpasswd;
        }

Сам файл /etc/htpasswd может вообще даже не существовать - нам не нужно проверять правильность пароля, мы будем отклонять все подряд, делая вид что пароль не подошел. Nginx все равно запустится даже без этого файла.

В браузере это будет выглядеть вот так
В браузере это будет выглядеть вот так

Третий вариант - переадресовывать подключения на какой-нибудь другой сайт. XRay не умеет перекидывать подключения на внешние сервера, только на локальные, поэтому тут нам опять поможет Nginx:

server {
    listen 127.0.0.1:8080 default_server;
    listen [::1]:8080 default_server;

    server_name _;

    location / {
         proxy_pass http://lib.ru;
    }
}

В результате при попытке открытия адреса прокси браузером загрузится зеркало lib.ru - замените его на какой-нибудь другой сайт. Использовать для этого какие-либо популярные или навороченный сайты явно не стоит, а вот какую-нибудь богом забытую хомпагу эпохи Web 1.0 или безымянную webftp-файлосвалку - уже можно. А чтобы некоторые тупые боты или пауки поисковых систем не нагнали вам трафика, можно добавить опции ratelimit-модуля в Nginx и ограничить скорость передачи данных с "переадресованного" сайта, например, до 1 мегабита.

Перезапускаем еще раз xray:

$ systemctl restart xray

Проверяем что все нормально запустилось:

$ journalctl -u xray

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

Я буду показывать на примере Nekoray/Nekobox, но абсолютно то же самое можно сделать и в другом клиенте, настройки будут одинаковые. Скачиваем Nekoray, выбираем в настройках core Sing-box (и Nekoray волшебным образом становится Nekobox).

Идем в server -> new profile, и далее заполняем поля следующим образом.

Для прямого VLESS + XTLS-Vision:

Для VLESS-over-Websockets:

Для Shadowsocks:

Выбираем нужное подключение в списке на главном окне, кликаем правой кнопкой мыши -> Current Select -> URL test, и видим в логе и в окошке, что пинг успешен:

Все. Теперь достаточно нажать сверху галочку System proxy или VPN mode, и вы попадаете в интернет через ваш новый прокси.

Чтобы настроить в других клиентах или на других устройствах (например, на смартфоне, или поделиться сервером с друзьями), кликаем на сервер правой кнопкой мыши, выбираем Share -> QR code and Link, и получаем ссылку, которую можно отправить кому-нибудь например через Telegram и QR-код, который можно отсканировать камерой во многих клиентах:

Соответственно, потом на мобильном устройстве в Nekobox, или в v2rayNG, или в Wings X, или в любом другом клиенте, нажимаем что-то типа "Add server" -> "Scan QR" - и все, новый сервер у вас в списке, можно подключаться.

Важно: некоторые клиенты при добавлении сервера по ссылке или QR теряют настройку uTLS, поэтому перепроверяйте все ли на месте после добавления нового сервера.

Лайфхак #1: а еще можно упороться и добавить в Nekobox еще и SSH в качестве подключения, пример конфигурации есть вот здесь (сначала надо будет подключиться родным системным ssh-клиентом, сгенерить клиентский ssh-ключи и сделать ssh-copy-id, в Windows это тоже работает).

Лайфхак #2: Чтобы не забывать ставить uTLS fingerprint для каждого подключения отдельно, его можно задать дефолтное значение в общих настройках Nekobox:

Вариант второй, полуготовый

А теперь представим, что у вас на VPS уже установлен веб-сервер с каким-нибудь сайтом, уже настроены TLS-сертификаты, и все остальное, и нужно просто аккуратно добавить прокси, желательно не ломая конфиг сервера.

Вариант раз: заиметь еще один поддомен, и разруливать TLS-подключения еще на этапе хэндшейка по SNI с помощью, например, HAProxy или ssl_preread модуля в Nginx. Тогда настройка XRay будет полностью аналогична описанному в предыдущем пункте, разве что только надо будет перевесить его с 443 на другой порт.

Вариант два: TLS-сессия будет терминироваться веб-сервером, и в случае обращения к определенному URL он будет передавать подключение на прокси. Этот вариант проще, единственное ограничение - никакого XTLS (ни Vision, ни Reality) уже не получится, и производительность будет немного ниже.

Итак, допустим, у вас настроен Nginx (или любой другой веб-сервер с каким-нибудь сайтом). Нужно средствами веб-сервера настроить переадресацию обращений к определенному урлу на прокси. Варианта два - использовать websockets (и надо не забыть передать специфичные для них хедеры), или использовать gRPC (если ваш сервер умеет его проксировать). В Nginx это будет выглядеть примерно так, для веб-сокетов:

location /myverysecretpath {
         proxy_pass http://127.0.0.1:8888;
         proxy_http_version 1.1;
         proxy_set_header Upgrade $http_upgrade;
         proxy_set_header Connection "Upgrade";
         proxy_set_header Host $host;
       }

А конфиг XRay будет таким:

{
  "log": {
    "loglevel": "info"
  },
  "routing": {
    "rules": [],
    "domainStrategy": "AsIs"
  },
  "inbounds": [
    {
      "port": 23,
      "tag": "ss",
      "protocol": "shadowsocks",
      "settings": {
        "method": "2022-blake3-aes-128-gcm",
        "password": "aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb",
        "network": "tcp,udp"
      }
    },
    {
      "listen": "localhost",
      "port": 8888,
      "protocol": "vless",
      "tag": "vless_ws",
      "settings": {
        "clients": [
          {
            "id": "7957c33c-d9ca-11ed-afa1-0242ac120002",
            "email": "user1@myserver"
          }
        ],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "ws",
        "security": "none",
        "wsSettings": {
          "path": "/myverysecretpath"
        }
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "tag": "direct"
    },
    {
      "protocol": "blackhole",
      "tag": "block"
    }
  ]
}

Как видно, почти то же самое, что и в предыдущем варианте, только нет inbound для "прямого" TLS-подключения, и вообще нет ничего про TLS - сервер слушает 8888 порт и сразу обрабатывает его как веб-сокет. /myverysecretpath, понятное дело, должен совпадать в конфиге веб-сервера и в конфиге прокси.

Настройки клиентов для этого варианта будут полностью аналогичны настройкам клиентов для Shadowsocks и VLESS+Websocket из прошлого пункта.

Вариант с gRPC по примеру из официальной репы с примерами у меня так и не заработал (чует мое сердце, там есть какой-то подвох с TLS и с переадресацией на него) - так что если у кого-то есть рабочие конфиги для XRay и Nginx с gRPC, делитесь в комментариях.

Вариант третий для самых ленивых (Websockets-only)

$ apt install docker.io docker-compose
$ mkdir /etc/xray/
$ nano /etc/xray/config.json
$ nano /etc/xray/Caddyfile
$ nano docker-compose.yml
/etc/xray/config.json:
{
    "log": {
        "loglevel": "info"
    },
    "routing": {
        "domainStrategy": "AsIs",
        "rules": [
            {
                "type": "field",
                "ip": [
                    "geoip:private"
                ],
                "outboundTag": "block"
            }
        ]
    },
    "inbounds": [
        {
            "listen": "0.0.0.0",
            "port": 5000,
            "tag": "vless_ws",
            "protocol": "vless",
            "settings": {
                "clients": [
                    {
                        "id": "7957c33c-d9ca-11ed-afa1-0242ac120002",
                        "email": "test@test.com"
                    }
                ],
                "decryption": "none"
            },
            "streamSettings": {
                "network": "ws",
                "security": "none"
            }
        }
    ],
    "outbounds": [
        {
            "protocol": "freedom",
            "tag": "direct"
        },
        {
            "protocol": "blackhole",
            "tag": "block"
        }
    ]
}

/etc/xray/Caddyfile
example.com {
  handle_path /myverysecretpath {
        reverse_proxy http://xray:5000
  }
  reverse_proxy  lib.ru:80 {
  }
}

или, если не нужна переадресация, а хватит просто 401 Unauthrized:

example.com {
  handle_path /myverysecretpath {
        reverse_proxy http://xray:5000
  }
  basicauth / {
        bob JDJhJDE0JElab2ZPM25zdU40bE5SSURlTHd3OHVBeVJvYTlMN3dMOEFMdFVCRzNYS1l5ODl6TlVyQllH
  }
}

docker-compose.yml
version: '2'

volumes:
  caddy_data:
  caddy_config:

services:
  xray:
    image: teddysun/xray
    volumes:
      - /etc/xray:/etc/xray
    ports:
      - 23:23

  caddy:
    image: caddy
    volumes:
      - caddy_data:/data
      - caddy_config:/config
      - /etc/xray/Caddyfile:/etc/caddy/Caddyfile
    depends_on:
      - xray
    links:
      - xray:xray

    ports:
      - 443:443
      - 80:80

$ docker-compose up -d

Тут в качестве веб-сервера используется Caddy, он же сам запрашивает и обновляет TLS-сертификаты (certbot не нужен). IPv6 не будет, но все остальное в принципе работает - опять же, только WS, и никакого XTLS. Lazydocker вам в помощь.

Нюансы и мудрости

На сегодняшний день связка VLESS+XTLS-Vision является самой проверенной и устойчивой к блокировкам. Однако нужно иметь в виду еще пару вещей:

  1. Обязательно используйте uTLS на клиентах, выставляя правильный TLS fingerprint. Клиенты, которые не умеют в uTLS лучше не использовать;

  2. Обязательно поднимите фейковый веб-сайт или настройте fallback-переадресацию на какой-нибудь левый адрес;

  3. С uTLS связан интересный баг: если при использовании XTLS-Vision вы почему-то не можете подключиться, в логах сервера видна ошибка типа "failed to use xtls-rprx-vision, found outer tls version 771", попробуйте сменить версию uTLS. У меня, например, при выборе "android" клиент не подключается, а при выборе "chrome" все окей;

  4. С XTLS лучше, чем без него;

  5. Во время отладки конфигурации в случае проблем с TLS может помочь опция "allowInsecure" на клиенте;

  6. Очень рекомендуется настраивать на клиентах правила маршрутизации, чтобы трафик до .ru-доменов и хостов с российскими IP шел напрямую, а не через прокси (в клиентах для такого поставляется GeoIP база данных). На сервере можно для надежности добавить правило типа

"routing": {
        "domainStrategy": "IPIfNonMatch",
        "rules": [
            {
                "type": "field",
                "domain": [
                  "geosite:ru"
                ],
                "ip": [
                    "geoip:ru"
                ],
                "outboundTag": "block"
            }
        ]
    },

и отправлять RU-подключения сразу нафиг.

Ещё можно иметь два сервера (low-end сервер в РФ можно арендовать рублей так за 60), и приняв подключение передавать его на следущий сервер, указав в outboundTag не freedom и не block, а тег соответствующего outbound'а (XRay может работать сразу и как сервер, и как клиент, не забываем).

  1. При проксировании на Nginx или любой другой сервер, так же хорошей практикой считается проксировать HTTP/1.1 и HTTP/2 отдельно.
    В конфиге Nginx для этого нужно что-то типа такого:

    listen 127.0.0.1:8888;
    listen 127.0.0.1:8889 http2;

А в конфиге XRay:

"fallbacks": [
                    {
                        "dest": "8888"
                    },
                    {
                        "alpn": "h2",
                        "dest": "8889"
                    }
		]

А что там с CDN?

Пока что известно две CDN, которые позволяют на бесплатных аккаунтах работать с подобным: Cloudflare позволяет проксировать Websocket и gRPC, GCore позволяет проксировать Websocket (насчет gRPC не знаю, не проверял). Про Cloudflare говорят, что при проксировании очень больших объемов через вебсокеты они могут попросить перейти на платный тариф, про gRPC такого не написано.

Для работы через CDN нужен будет уже полноценный домен (не DynDNS), который можно делегировать на NS CDN-сети и управлять им там. Дальше нужно включить проксирование для конкретного домена:

Лайфхак: если у вас дешевый IPv6-only сервер, вы можете указать для него только AAAA-запись (IPv6), и Cloudflare все равно позволит вам подключаться к нему по IPv4 через свою сеть. Смекалочка.

Ну и не забыть отдельно включить в настройках проксирование WS и gRPC:

А что с XTLS-Reality?

Технология многообещающая, уже даже во многих клиентах поддерживаемая, но с ней надо разбираться отдельно, и ее настройка - тоже разговор отдельный. Кто уже смог и осилил - пишите в комментарии, а лучше вообще еще одну статью. Источник вдохновения и примеры конфигов с XTLS-Reality можно найти здесь.

А что с Sing-box?

Sing-box - активно развивающийся и тоже многообещающий клиент и сервер, и он может использоваться вместо XRay, поскольку тоже поддерживает Shadowsocks-2022, VLESS, Trojan, XTLS-Vision и XTLS-Reality, а еще умеет работать с Hysteria, Naiveproxy, и всякое другое.

Официальный сайт
Github
Документация по настройке
Разработчики реорганизуют репу, поэтому переход по ссылкам в документации может выдавать 404 ошибку — без паники, смотрим название файла, и находим правильный путь в гите по названию, дальше никаких проблем.
Как и XRay, Sing‑box умеет делать fallback'и, только здесь в секции «listen» оно называется «detour», и значением этого параметра должен быть «tag» другого inbound'а.
Сайт со скриптами автоустановки и примерами конфигураций

На этом всё. Удачи вам в нелегком деле настройки всего этого дела, и да прибудет с вами сила.

Предыдущие статьи серии:

«Современные технологии обхода блокировок: V2Ray, XRay, XTLS, Hysteria и все‑все‑все»

«Программы‑клиенты для протоколов недетектируемого обхода блокировок сайтов: V2Ray/XRay, Clash, Sing‑Box, и другие»

Комментарии (37)


  1. s7eepz
    14.04.2023 10:00
    +4

      "inbounds": [
        {
          "listen": "/dev/shm/Xray-VLESS-gRPC.socket,0666",
          "protocol": "vless",
          "settings": {
            "clients": [
              {
                "id": "123"
              }
    	],
    	"decryption": "none"
          },
          "streamSettings": {
            "network": "grpc",
            "grpcSettings": {
              "serviceName": "1234"
          	}
          }
        },
      {
           "listen": "0.0.0.0",
           "port": 443,
           "protocol": "vless",
           "settings": {
        	   "clients": [
            	{
                    "id": "123",
                    "flow": "xtls-rprx-vision"
                     }
                ],
    		"decryption": "none",
    		"fallbacks": [
                        {
                            "dest": "8001",
                            "xver": 1
                        },
                        {
                            "alpn": "h2",
                            "dest": "8002",
                            "xver": 1
                        }
    		]
    	},
    	  "streamSettings": {
                    "network": "tcp",
                    "security": "tls",
                    "tlsSettings": {
                        "rejectUnknownSni": true,
                        "minVersion": "1.2",
                        "certificates": [
                            {
                                "ocspStapling": 3600,
                                "certificateFile": "/etc/letsencrypt/live/domain/fullchain.pem",
                                "keyFile": "/etc/letsencrypt/live/domain/privkey.pem"
                            }
                        ]
                    }
                },
                "sniffing": {
                    "enabled": true,
                    "destOverride": [
                        "http",
                        "tls"
                    ]
                }
    	}
    ],
    

    grpc плюс vision. nginx слушает только 80/8001/8002 порт, с 443 идет проброс с xray на nginx, если клиент не опознан. grpc работает через 443 с таким конфигом.

    nginx.conf из ключевого
    
    http
    {
    map $http_upgrade $connection_upgrade {
            default upgrade;
            ""      close;
        }
    
    map $proxy_protocol_addr $proxy_forwarded_elem {
            ~^[0-9.]+$        "for=$proxy_protocol_addr";
            ~^[0-9A-Fa-f:.]+$ "for=\"[$proxy_protocol_addr]\"";
            default           "for=unknown";
        }
    
    map $http_forwarded $proxy_add_forwarded {
            "~^(,[ \\t]*)*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*([ \\t]*,([ \\t]*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*)?)*$" "$http_forwarded, $proxy_forwarded_elem";
            default "$proxy_forwarded_elem";
        }
    
    server {
        listen 80;
        return 301 https://$host$request_uri;
         server_tokens off;
    }
    
    server {
        listen 127.0.0.1:8001 proxy_protocol;
        listen 127.0.0.1:8002 http2 proxy_protocol;
        set_real_ip_from 127.0.0.1;
    location / {
        sub_filter                         $proxy_host $host;
        sub_filter_once                    off;
        proxy_pass                         https://thisis.com;
        proxy_set_header Host              $proxy_host;
        proxy_http_version                 1.1;
        proxy_cache_bypass                 $http_upgrade;
        proxy_ssl_server_name              on;
        proxy_set_header Upgrade           $http_upgrade;
        proxy_set_header Connection        $connection_upgrade;
        proxy_set_header X-Real-IP         $proxy_protocol_addr;
        proxy_set_header Forwarded         $proxy_add_forwarded;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host  $host;
        proxy_set_header X-Forwarded-Port  $server_port;
        proxy_connect_timeout              60s;
        proxy_send_timeout                 60s;
        proxy_read_timeout                 60s;
        }
    
    location /1234 {
        if ($content_type !~ "application/grpc") {
        return 404;
        }
        client_max_body_size 0;
        client_body_buffer_size 512k;
        grpc_set_header X-Real-IP $remote_addr;
        client_body_timeout 52w;
        grpc_read_timeout 52w;
        grpc_pass unix:/dev/shm/Xray-VLESS-gRPC.socket;
        }
    }
    }


  1. alegz81
    14.04.2023 10:00
    +1

    Xray имеет скрипт для установки который скачает последнюю версию и создаст юниты systemd. Почему не использовали его?

    https://github.com/XTLS/Xray-install


    1. MiraclePtr Автор
      14.04.2023 10:00
      +4

      Потому что задачей было рассказать процесс детально и подробно - а скрипт может рано или поздно устареть или не сработать на каком-нибудь дистрибутиве, а может читатель вообще решит использовать упомянутый Sing-Box вместо XRay :)
      Ну и да, установка XRay и unit'а - это только первый шаг, все дальнейшие шаги одинаковы и при установке скриптом, и при установке руками.

      А если хочется совсем простой настройки - в статье описан вариант с Docker, он там еще автоматически TLS-сертификаты и фейковый веб-сайт настраивает.

      Но в любом случае большое спасибо что упомянули, для кого-то может оказаться полезным, я добавил ссылку в статью.


      1. aanovik42
        14.04.2023 10:00

        del


  1. aanovik42
    14.04.2023 10:00

    Спасибо за цикл статей.

    Другой вариант - переадресовывать подключения на какой-нибудь другой сайт.

    Я так и не понял этот момент до конца. Разве такой проброс на другой домен — не подозрительный вариант by default? Зачем-то же люди размещают свои собственные сайты в качестве fallback, если бы всё было так просто, то зачем бы они это делали.

    Ну или я не прав, в общем хотелось бы чуть подробнее раскрыть этот момент.


    1. MiraclePtr Автор
      14.04.2023 10:00
      +3

      Это не "переброс" (не http-редирект), а отдача контента с другого сайта под своим доменом. Да, использовать для этого какие-либо популярные или навороченный сайты явно не стоит, а вот какую-нибудь богом забытую хомпагу эпохи Web 1.0 или безымянную webftp-файлосвалку - уже можно.

      Но да, вариант со своим уникальным сайтом, конечно, надёжнее. Кстати, тут в голову пришла ещё мысль - если не хочется париться с контентом фейкового сайта, можно настроить веб-сервер чтобы он требовал HTTP Authentication (запрос пароля в браузере) и на любые введённые креды возвращал Unauthorized :) Завтра добавлю в статью.


      1. aanovik42
        14.04.2023 10:00
        +1

        Да, вы правы, это я лихо прокси с редиректом смешал.

        Ещё вот какой вопрос возник:

        Другой вариант - иметь два сервера (low-end сервер в РФ можно арендовать рублей так за 60), и в зависимости от точки назначения (RU или не-RU) или выпускать сразу с сервера наружу, или передавать на следущий сервер, указав в outboundTag не freedom и не block, а тег соответствующего outbound'а (XRay может работать сразу и как сервер, и как клиент, не забываем).

        А в чём плюс такого подхода с двумя серверами?


        1. MiraclePtr Автор
          14.04.2023 10:00
          +2

          Особого плюса прям такого нет. Есть, например, у некоторых людей теория, что когда настанет час X и начнутся массовые блокировки по протоколам, то на хосты внутри страных их применять не будут, например ради экономии ресурсов, а с сервера (на канале от хостера в забугор) блокировок может не быть вообще или они будут не такие жёсткие, и тогда подключение сначала к местному серверу, а потом уже по цепочке к заграничному будет более надёжным вариантом.


      1. MiraclePtr Автор
        14.04.2023 10:00
        +2

        Дополнил статью вариантом с 401 Unauthorized.


  1. mrwtf
    14.04.2023 10:00
    +1

    Большое спасибо за цикл статей.

    особенная благодарность за  Nekoray/Nekobox . Я когда изучал вопрос этот как-то проглядел это чудо)


  1. alexdora
    14.04.2023 10:00

    Статья интересная, так почитать что что-то там придумали. Но классический ShadowSocket избыточен на данный с chacha20. Китайцы ходят вроде на ура и без проблем. А на тему заглушек актуально наверное для тех кто предоставляет это как сервис. А когда ты и твои друзья условно пользуются одним сервером – вряд ли будут ползать искать

    Сейчас у меня две виртуалки, одна в РФ и там нет роскомнадзора, другая в США для нетфликса и прочих сервисов которые наши айпи забанили. Заплатил сразу за год, обе обошлись мне в 2800 в сумме. 230 рублей/мес это не такие уж большие деньги за комфорт.

    Поставил Shadowsocks одной командой, сделал sub для shadowrocket. Туда указал два сервера, и определил с помощь config какие ресурсы куда должны ходить. 4 месяца – идеально. Макбук и iphone ходит в зависимости от адреса через нужную проксю. Но большая часть в Direct.


    1. MiraclePtr Автор
      14.04.2023 10:00
      +1

      Китайцы ходят вроде на ура и без проблем.

      Как поведал живущий в Китае комментатор под предыдущим постом, агрессивность блокировок очень сильно зависит от региона и времени (происходящих событий). В оригинальном Shadowsocks те же китайские исследователи нашли достаточно уязвимостей, которые его демаскируют, и есть много сообщений что в конце прошлого года ss-подключения массово банили совершенно фантастическим образом.

      А когда ты и твои друзья условно пользуются одним сервером – вряд ли будут ползать искать

      Подобное обычно не не вручную лезут искать, а автоматически при DPI - видно новое HTTPS подключение к ещё не знакомому хосту - подслушиваем SNI и через некоторое время пытаемся сходить туда под видом обычного бразура и проверить, а есть ли там вообще сайт. Какие-нибудь SoftEther, SSTP и AnyConnect так выявляются вообще на раз-два.


      1. alexdora
        14.04.2023 10:00

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

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


        1. MiraclePtr Автор
          14.04.2023 10:00
          +2

          а как эти новые технологии по скорости доступа

          XRay: что Shadowsocks-2022 (aes-128), что VLESS-over-TLS (даже без XTLS-Vision) у меня между хостами в Москве и в Праге не напрягаясь выдает ~100 мегабит


  1. anonym0use
    14.04.2023 10:00
    +1

    Спасибо за ваш труд, вон оказывается сколько всяких интересных технологий существует, не TORом и VPNом единым, даже GUI красивый и функциональный сделали и не один)


  1. oneTimeUse
    14.04.2023 10:00

    хз что там по dyndns, но как написано в етой статье есть ещё какой-то сервис freemyip для тех кто ни хочет регесрировать домемные имена


    1. MiraclePtr Автор
      14.04.2023 10:00
      +1

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


    1. nidalee
      14.04.2023 10:00

      На Freenom в свое время можно было бесплатно доменное имя сделать. На этой неделе заходил — пишут, технические неполадки, новых не даем. Не знаю, оклемаются ли, но как альтернатива — домен sbs можно на год взять за 3 бакса. Без продления (оно уже 9).


      1. MiraclePtr Автор
        14.04.2023 10:00

        На Godaddy по акции можно взять вообще за 1-2 бакса на первый год (а потом не продлять, зарегать какой-нибудь новый).

        Единственная проблема - российские карты, я думаю, они не принимают.


        1. nidalee
          14.04.2023 10:00

          sbs себе заказывал пару дней назад за bitcoin, но не уверен, что стоит давать ссылку на регистратора.


      1. s7eepz
        14.04.2023 10:00

        Freenom через год попросил платить за домен. Есть еще no-ip.com. Третий уровень дают. Раз в месяц надо тыкнуть, что пользуешься.


        1. Aelliari
          14.04.2023 10:00

          По freenom - ты не успел тыкнуть на «продлить», там что-то около двух недель до окончания срока «аренды» нужно нажать, не успеешь - какое-то время домен вернуть можно только за деньги. Потом он возвращался в общий пул. Заранее продлить или сразу на больше чем на год - он не давал. А сейчас вообще ругается на технические неполадки


  1. the_shade
    14.04.2023 10:00

    низкий поклон. Пока все работает через sshuttle думаешь, что сойдет итак. А тут видимо надо готовиться уже заранее. Сам пробовал начинать смотреть и изучать, но информация настолько разрозненна, что бросал на пол пути даже SoftEther.


    1. kt97679
      14.04.2023 10:00

      Интересно мнение опытных людей насколько подозрительно выглядит sshuttle работающий через websocket соединение. В теории это просто https сессия, но возможно по паттернам траффика можно заподозрить что-то неладное?


      1. MiraclePtr Автор
        14.04.2023 10:00
        +1

        Паттерны могут выдать, но выявление по паттернам штука довольно сложная и дорогая, наш РКН до такого ещё долго расти будет.

        А вот TLS fingerprint подобного клиента неплохо было бы проверить, особенно если он на Go :)


        1. kt97679
          14.04.2023 10:00

          Вот клиент, которым пользуюсь лично я: https://github.com/vi/websocat. Можно вас попросить рассказать или дать линку на документацию как я могу проверить TLS fingerprint?


          1. MiraclePtr Автор
            14.04.2023 10:00
            +1

            Есть сайт https://tlsfingerprint.io/, где расписано что это и как оно работает, большая база фингерпринтов, и можно проверить, насколько популярен фингерпринт клиента - одно но, для этого нужно зайти клиентом на сайт как браузером, в вашем случае скорее всего не получится (разве что только если кода дописать специально для такого теста).

            Но в принципе, грубо прикинуть можно и без этого: в Китае в первую очередь резали подключения с fingerprint'ами TLS-библиотеки языка Go, ибо 90% всяких подобных клиентов написаны на нем. У вас клиент на Rust, поэтому уже не так плохо. В Иране, судя по сообщениям, банили вообще все подключения с fingerprints не похожими на стандартные браузеры, но это уже совсем терминальная стадия, думаю у нас до такого маразма в ближайшие годы не дойдет.

            Поэтому варианта два - или забить и не париться, продолжать пользоваться, или вместо wstunnel попробовать использовать Cloak который тут недавно советовали - снаружи выглядит тоже как настоящий HTTPS, применяются разные методы маскирования (в том числе и правильный фингерпринт от браузера), и ваш sshtunnel по нему должен бегать без проблем.


            1. kt97679
              14.04.2023 10:00

              Все понятно, спасибо!


  1. s7eepz
    14.04.2023 10:00
    +4

      "inbounds": [
        {
          "listen": "/dev/shm/Xray-VLESS-gRPC.socket,0666",
          "protocol": "vless",
          "settings": {
            "clients": [
              {
                "id": "123"
              }
    	],
    	"decryption": "none"
          },
          "streamSettings": {
            "network": "grpc",
            "grpcSettings": {
              "serviceName": "1234"
          	}
          }
        },
      {
           "listen": "0.0.0.0",
           "port": 443,
           "protocol": "vless",
           "settings": {
        	   "clients": [
            	{
                    "id": "123",
                    "flow": "xtls-rprx-vision"
                     }
                ],
    		"decryption": "none",
    		"fallbacks": [
                        {
                            "dest": "8001",
                            "xver": 1
                        },
                        {
                            "alpn": "h2",
                            "dest": "8002",
                            "xver": 1
                        }
    		]
    	},
    	  "streamSettings": {
                    "network": "tcp",
                    "security": "tls",
                    "tlsSettings": {
                        "rejectUnknownSni": true,
                        "minVersion": "1.2",
                        "certificates": [
                            {
                                "ocspStapling": 3600,
                                "certificateFile": "/etc/letsencrypt/live/domain/fullchain.pem",
                                "keyFile": "/etc/letsencrypt/live/domain/privkey.pem"
                            }
                        ]
                    }
                },
                "sniffing": {
                    "enabled": true,
                    "destOverride": [
                        "http",
                        "tls"
                    ]
                }
    	}
    ],
    

    grpc плюс vision. nginx слушает только 80/8001/8002 порт, с 443 идет проброс с xray на nginx, если клиент не опознан. grpc работает через 443 с таким конфигом.

    nginx.conf из ключевого
    
    http
    {
    map $http_upgrade $connection_upgrade {
            default upgrade;
            ""      close;
        }
    
    map $proxy_protocol_addr $proxy_forwarded_elem {
            ~^[0-9.]+$        "for=$proxy_protocol_addr";
            ~^[0-9A-Fa-f:.]+$ "for=\"[$proxy_protocol_addr]\"";
            default           "for=unknown";
        }
    
    map $http_forwarded $proxy_add_forwarded {
            "~^(,[ \\t]*)*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*([ \\t]*,([ \\t]*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*)?)*$" "$http_forwarded, $proxy_forwarded_elem";
            default "$proxy_forwarded_elem";
        }
    
    server {
        listen 80;
        return 301 https://$host$request_uri;
         server_tokens off;
    }
    
    server {
        listen 127.0.0.1:8001 proxy_protocol;
        listen 127.0.0.1:8002 http2 proxy_protocol;
        set_real_ip_from 127.0.0.1;
    location / {
        sub_filter                         $proxy_host $host;
        sub_filter_once                    off;
        proxy_pass                         https://thisis.com;
        proxy_set_header Host              $proxy_host;
        proxy_http_version                 1.1;
        proxy_cache_bypass                 $http_upgrade;
        proxy_ssl_server_name              on;
        proxy_set_header Upgrade           $http_upgrade;
        proxy_set_header Connection        $connection_upgrade;
        proxy_set_header X-Real-IP         $proxy_protocol_addr;
        proxy_set_header Forwarded         $proxy_add_forwarded;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host  $host;
        proxy_set_header X-Forwarded-Port  $server_port;
        proxy_connect_timeout              60s;
        proxy_send_timeout                 60s;
        proxy_read_timeout                 60s;
        }
    
    location /1234 {
        if ($content_type !~ "application/grpc") {
        return 404;
        }
        client_max_body_size 0;
        client_body_buffer_size 512k;
        grpc_set_header X-Real-IP $remote_addr;
        client_body_timeout 52w;
        grpc_read_timeout 52w;
        grpc_pass unix:/dev/shm/Xray-VLESS-gRPC.socket;
        }
    }
    }


    1. MiraclePtr Автор
      14.04.2023 10:00
      +1

      То есть я правильно понял, XRay ждет VLESS+XTLS, если нет - то передает запрос на Nginx, а уже Nginx смотрит, что там URL для gRPC и передает обратно на XRay? Запутанно, но должно сработать наверное.

      Кстати, еще вопрос, вдруг в курсе: часто вижу разделение правил fallback'ов для разных ALPN и со стороны XRay, и со стороны Nginx. Зачем? Без него оно работает вполне окей, Nginx в принципе должен сам разобраться, что за запрос пришел внутри подключения, или нет? Или это для случая, когда TLS ALPN указан как http/1.1, а после подключения клиент пытается слать запросы как h2, и если оно сработает, то это уже подозрительно?


      1. s7eepz
        14.04.2023 10:00

        То есть я правильно понял, XRay ждет VLESS+XTLS, если нет - то передает запрос на Nginx, а уже Nginx смотрит, что там URL для gRPC и передает обратно на XRay? Запутанно, но должно сработать наверное.

        по логике так. работает

        Или это для случая, когда TLS ALPN указан как http/1.1

        мне кажется именно для этого, http1, http2 детектить.


      1. MiraclePtr Автор
        14.04.2023 10:00
        +1

        До меня в итоге дошло, почему у меня gRPC не работал через Nginx. В Ubuntu, что в официальных репах дистрибутива, что в официальных репах от разработчиков Nginx он собран без gRPC-модуля. Труба.


  1. s7eepz
    14.04.2023 10:00

    не получилось только kcp настроить, либо shadowrocket что-то не так умеет

    	{
                "protocol": "vless",
                "port": 443,
                "settings": {
                    "decryption":"none",
                    "clients": [
                        {"id": "123"}
                    ]
                },
                "streamSettings": {
                    "network": "kcp",
                    "mtu": 1360,
                    "uplinkCapacity":90,
                    "downlinkCapacity":90,
                    "congestion":false,
                    "header":{
                        "type":"dtls"
                    },
                    "kcpSettings": {
                        "seed": ""
                    }
                }
            },
    

    в логе xray

    [Info] transport/internet/kcp: discarding invalid payload


    1. MiraclePtr Автор
      14.04.2023 10:00
      +1

      Из того что видно, можно попробовать или вообще убрать упоминание seed из конфига, либо наборот задать какое-нибудь значение. Ну и да, seed и header должны быть одинаковые на сервере и на клиенте (для отладки можно еще попробовать без header).


      1. s7eepz
        14.04.2023 10:00

        Точно. Без указания header в shadowrocket работает. С любым совпадающим header не работает. Видимо не поддерживается обсфукация в shadowrocket.


  1. schebotar
    14.04.2023 10:00

    Криво (используется host-network потому что иначе не работает IPv6, а с ней оно слушает 48800 порт не только на localhost, а в открытую, ибо через 127.0.0.1 не работает - знающие люди, подскажите, как пофиксить)

    Caddy запускаем во внешней сетке, скажем reverse-proxy. Ее естественно перед этим создаем

    docker network create reverse-proxy
    services:
      caddy:
        container_name: caddy
        image: caddy:latest
        restart: unless-stopped
        networks:
          - reverse-proxy
        ports:
          - "80:80"
          - "443:443"
          - "443:443/udp"
        volumes:
          - $PWD/Caddyfile:/etc/caddy/Caddyfile
          - ./data:/data
          - ./config:/config
    networks:
      reverse-proxy:
        external: true

    В этой же сетке запускаем контейнер xray.

    version: '3'
    services:
      xray:
        image: teddysun/xray
        container_name: xray
        volumes:
          - .:/etc/xray
        networks:
          - reverse-proxy
    
    networks:
      reverse-proxy:
        external: true

    В Caddyfile тогда будет

    example.com {
      handle_path /myverysecretpath {
        reverse_proxy xray:48800      
      }
    }

    К слову можно у себя запускать прокси тоже в докере

    config.json

    {
        "log": {
            "loglevel": "warning"
        },
        "inbounds": [
            {
                "port": 10800,
                "listen": "0.0.0.0",
                "protocol": "socks",
                "settings": {
                    "udp": true
                }
            }
        ],
        "outbounds": [
            {
                "protocol": "vless",
                "settings": {
                    "vnext": [
                        {
                            "address": "example.com", 
                            "port": 443,
                            "users": [
                                {
                                    "id": "UUID",
                                    "encryption": "none"
                                }
                            ]
                        }
                    ]
                },
                "streamSettings": {
                    "network": "ws",
                    "security": "tls",
                    "tlsSettings": {
                        "serverName": "example.com" 
                    },
                    "wsSettings": {
                        "path": "/myverysecretpath" 
                    }
                }
            }
        ]
    }

    docker-compose.yml

    version: '3'
    services:
      xray-client:
        image: teddysun/xray
        container_name: xray-client
        ports:
          - "10800:10800"
        volumes:
          - .:/etc/xray


    1. MiraclePtr Автор
      14.04.2023 10:00

      Это то понятно, в самом первом варианте я так и делал - и xray и caddy в одной докеровской сетке. Проблема в том, что если запускать прокси в докеровской сетке, то он не имеет IPv6-связности наружу. Поэтому решением было использовать host-network, но там есть проблема с тем чтобы слушать только localhost и при этом быть доступным для контейнера Caddy.


      1. schebotar
        14.04.2023 10:00

        Я ipv6 не использую. Поэтому такой вариант работает.

        Возможно стоит попробовать включить поддержу ipv6 в самом docker

        https://docs.docker.com/config/daemon/ipv6/