惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
Webroot Blog
Webroot Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
T
Threat Research - Cisco Blogs
V2EX - 技术
V2EX - 技术
L
LINUX DO - 热门话题
Google DeepMind News
Google DeepMind News
Recorded Future
Recorded Future
S
Schneier on Security
I
InfoQ
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The GitHub Blog
The GitHub Blog
S
Security @ Cisco Blogs
O
OpenAI News
W
WeLiveSecurity
Vercel News
Vercel News
阮一峰的网络日志
阮一峰的网络日志
Simon Willison's Weblog
Simon Willison's Weblog
人人都是产品经理
人人都是产品经理
Cloudbric
Cloudbric
The Last Watchdog
The Last Watchdog
The Hacker News
The Hacker News
Google Online Security Blog
Google Online Security Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
GbyAI
GbyAI
NISL@THU
NISL@THU
T
Tailwind CSS Blog
V
Visual Studio Blog
PCI Perspectives
PCI Perspectives
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Jina AI
Jina AI
D
DataBreaches.Net
B
Blog RSS Feed
N
News and Events Feed by Topic
N
News and Events Feed by Topic
H
Heimdal Security Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
腾讯CDC
Latest news
Latest news
V
Vulnerabilities – Threatpost
Hacker News: Ask HN
Hacker News: Ask HN
WordPress大学
WordPress大学
V
V2EX
aimingoo的专栏
aimingoo的专栏
博客园 - 司徒正美
Apple Machine Learning Research
Apple Machine Learning Research
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The Register - Security
The Register - Security
Help Net Security
Help Net Security

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Простой гайд как на одном и том же сервере иметь и панель 3X-UI за NGINX, и свой сервис
JustMe_001 · 2026-04-30 · via Все публикации подряд на Хабре

Время на прочтение10 мин

Охват и читатели65

Туториал

Давно читаю Хабр, и всегда хотелось что-нибудь да написать. И вот, тема появилась. Надо было на одном сервере запустить и VPN-сервер (который уже работал), и легитимный сервис.

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

Как мы уже все знаем, без VPN в современном Интернете не выжить. Да даже эту статью вы наверняка читаете со способом обхода. Но с каждым днём РКН выкашивает всё активнее и всё больше VPN-серверов. И самая главная проблема "раскрытия" серверов - простукивание по портам, а также слепки. Если вы маскируетесь, например, под apple.com, то вас рано или поздно "по ойпи вычеслят", особенно если вы не закрыли панель. А мы тут не под кого не маскируемся - сервис легитимный и реально ваш. Просто его часть немного "скрыта" от ненужных глаз :). Кто его знает, может в database.mydomain.site крутится база данных.

Нам понадобится NGINX, и панель типа 3x-ui, но это не обязательно (если вы мазохист то можно и голый XRAY), однако желательно, потому что панель тоже будет закрыта за щитом NGINX, и рисков с ней нет :).

Простая схема как это выглядит

Для начала, у NGINX будет stream-block с ssl preread. Тоесть перед тем как мы подключимся, мы смотрим: "так, а на какой домен стучатся?". Если стучатся на IP, просто режем TLS-рукопожатие, выдавая SSL_ERROR_UNRECOGNIZED_NAME_ALERT ("у нас нет сертификата на такой домен, простите"). Если стучатся прямо на домен, пропускаем и разбираемся в HTTP-блоке NGINXа, либо же сразу прокидываем на наш inbound.

Примерная схема как это будет работать

Примерная схема как это будет работать

Подготовка

Для начала, нужно установить NGINX с модулем stream.

Создаём APT-репозиторий для NGINX и устанавливаем

Так как в этом туториале будут директивы, которые поддерживаются только с версии 1.25 (Например, listen 443 ssl; вместо listen 443; ssl on;), лучше добавить APT-репозиторий непосредственно от NGINX, вместо дефолтных (Сейчас вроде как "из коробки" идёт 1.18). Инструкция будет для Debian/Ubuntu, для RHEL/CentOS и др. читайте здесь: https://nginx.org/en/linux_packages.html.

Устанавливаем зависимости:
Debian:
sudo apt install curl gnupg2 ca-certificates lsb-release debian-archive-keyring -y
Ubuntu:
sudo apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring

Скачиваем GPG-ключ:
curl https://nginx.org/keys/nginx\_signing.key | gpg --dearmor \ | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null

Создаём APT-репозиторий со стабильными релизами NGINX:
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \ https://nginx.org/packages/debian lsb_release -cs nginx" \ | sudo tee /etc/apt/sources.list.d/nginx.list

Ставим новый репозиторий выше системного:
echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \ | sudo tee /etc/apt/preferences.d/99nginx

Устанавливаем:
sudo apt update && sudo apt install nginx

Получаем SSL-сертификаты

Вам понадобится SSL-сертификат. Если у вас есть домен (субдомены тоже подойдут). Панель предложит создать вам сертификат на 90 дней, однако (как я покажу ниже) можно получить куда более долгоживущий от Cloudflare. Если домена нет, то панель сама запросит и выдаст короткоживущие сертификаты на IP-адрес, которые будут обновляться автоматически. Плюс долгоживущего сертификата в том, что acme.sh (утилита для автообновления) необходим открытый порт 80, и без него она не работает, а это вредит маскировке. Конечно, можно настроить то чтобы (локальный) ACME listener слушал на другом порту, но внешний всё равно будет кидать трафик на порт 80. Так что придётся "вайтлистить" путь /.well-known/acme-challenge/*, что станет "ахилесовой пятой" нашей маскировки.

Получаем сертификат от Cloudflare

Для начала рекомендую иметь домен, т.к. сертификаты долгоживущие, особенно если они от "взрослых" CA типа GlobalSign. Панель предложит сертификат на 90 дней от Let's Encrypt. Такая же проблема с ACME.sh listener (и вайтлистом), но обновлять каждые 3 месяца куда проще, чем каждую неделю, так что всё не так плохо.

Устанавливаем 3x-ui и подключаем сертификаты

Выполняем скрипт для установки 3x-ui:
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
ОЧЕНЬ ВАЖНЫЙ СОВЕТ НА БУДУЩЕЕ: НЕ ВЫПОЛНЯЙТЕ НИКАКИЕ СКРИПТЫ ИЗ ИНТЕРНЕТА, ПЕРЕД ТЕМ КАК ВЫ ИХ ПРОЧИТАЕТЕ, ВСЁ НА ВАШ СТРАХ И РИСК. Скажете спасибо потом.

Нам предложат либо создать SSL-сертификаты с помощью Let's Encrypt либо на домен (1), либо на IP (2). Если у вас просто есть уже имеющийся сертификат, выбирайте опцию 3 и указываете путь. Как видите, у меня это /root/cert/mydomain_*.pem.

Настройка обфускации

Итак, когда панель и NGINX установлены, можно приступать к настройке. Чтобы куда-то пробрасывать траффик, нам нужно создать это куда-то. Для начала заходим в панель по внешней ссылке, и настравиваем inbound на XHTTP.

Как +- будет выглядеть ваш inbound

Как +- будет выглядеть ваш inbound


ВАЖНО: не слушайте на 0.0.0.0 или оставляйте Listen IP пустым, необходимо чтобы он был у вас на localhost-е (127.0.0.1)! Без этого на ваш inbound можно будет стучаться из интернета.

ВАЖНО 2: НЕ СТАВЬТЕ РЕЖИМ auto! Так как мы будем использовать grpc_pass, работают только stream-one и stream-up! packet-up просто отваливается!

ВАЖНО 3: security НА СЕРВЕРЕ ставьте none, так как этим будет заниматься NGINX, а не XRAY, после чего идёт уже передача ГОЛОГО gRPC. У КЛИЕНТА должен как раз таки стоять security TLS.
Кстати, именно поэтому нам придётся создать "перехватчик" подписок, который будет давать security=tls клиенту.

После этого сохраняйте инбаунд, переходите в настройки панели (Panel Settings) > Certificates > УБИРАЕТЕ пути до сертификатов (отключает HTTPS)
(Это нужно для того чтобы не было tls-in-tls, что детектируется.)

Итак, настроили inbound. Теперь можно и nginx. Переходим в директорию /etc/nginx/. Нам нужен nginx.conf. (Тем, кто собирал из исходников - /usr/local/bin/nginx/conf/nginx.conf). Для начала разберём блок stream:

# Тут ничего не трогайте
user  nginx;
worker_processes  auto;

error_log  logs/error.log;
error_log  logs/error.log  notice;
error_log  logs/error.log  info;

pid        logs/nginx.pid;


events {
    worker_connections  1024;
}
########################################################################

stream {
        map $ssl_preread_server_name $backend {
        mydomain.site                     webhook_backend;   # Ваш легитимный сервис
        service.mydomain.site             panel_backend; # Ваша панель
        database.mydomain.site            inbound_1; # Наш inbound
        default                           rejector;   # Сюда попадает всё остальное
    }

        upstream webhook_backend {
                server 127.0.0.1:<ПОРТ_ВАШЕГО_СЕРВИСА>;
                #Порт НЕ вебсервера nginx, а сразу вашего сервиса.
        }

        upstream panel_backend {
                server 127.0.0.1:<ПОРТ1>;
                #ВНИМАНИЕ: здесь не порт вашей панели, а порт вебсервера nginx! (будет ниже)
        }

        upstream inbound_1 {
                server 127.0.0.1:<ПОРТ2>; 
                #ВНИМАНИЕ: здесь не порт вашего инбаунда, а порт сервера nginx! (будет ниже)
        }

        upstream rejector {
                server 127.0.0.1:8011;
                #Порт вебсервера NGINX (будет ниже)
        }

        server {
                listen 443; 
                ssl_preread on;
                proxy_pass $backend;
        }

}

В блоке map мы просто присваиваем нужным доменам названия upstream-ов ($backend).
Каждый upstream просто "биндится" на определенный IP и порт.
В блоке server мы включаем ssl_preread - т.е. заранее смотрим, на какой домен/IP летит трафик, не расшифровывая его заранее. Далее просто делаем proxy_pass на нужный upstream в зависимости от этого

mydomain.site - прокидываем на легитимный сервис без посредников.
service.mydomain.site - прокидываем на веб-сервер nginx-а, который крутится на localhost (посредник; будет ниже), а потом прокдывает уже на панель. Также можно прикрутить сюда "перехватчик" подписок, который будет переписывать IP с 127.0.0.1 на database.mydomain.site, порт с нашего на 443, а также security будет делать TLS.
database.mydomain.site - прокидываем на веб-сервер nginx-а, который крутится на localhost (посредник; будет ниже), а потом прокидывает трафик на XHTTP-inbound с помощью grpc_pass.
default - всё остальное (в том числе и IP) - просто идёт на сервер, в котором будет terminate SSL-рукопожатия.

Все эти серверы будут в блоке http.

http {
    include       mime.types;
    default_type  application/octet-stream;
  
    #Логирование - не трогайте (и у вас может быть не так как у меня)
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  logs/access.log  main;
    #######################################################################

    #Просто присваиваем переменной connection_upgrade либо HTTP upgrade, 
    #либо close connection
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ""      close;
    }

    sendfile        on;

    keepalive_timeout  65;
    server_tokens off; #Не нужно говорить какая у нас версия NGINX и подобное

    #Переносим все подключения на HTTPS
    server {
            listen 80;
            server_name service.mydomain.site www.service.mydomain.site;
            # Redirect all traffic to HTTPS
            return 301 https://$host$request_uri;
    }

    server {
            listen <ПОРТ1> ssl;
            server_name service.mydomain.site;

            set_real_ip_from           127.0.0.1;
            ssl_certificate     /root/cert/mydomain.site/fullchain.pem;
            ssl_certificate_key /root/cert/mydomain.site/privkey.pem;
            ssl_protocols       TLSv1.2 TLSv1.3;

            
            location ~ ^/Ваша секретная ссылка на панель(.*)$ {
                                      #HTTP, не HTTPS, избегаем tls-in-tls
                proxy_pass             http://127.0.0.1:<ПОРТ НЕПОСРЕДСТВЕННО ВАШЕЙ ПАНЕЛИ>;
                proxy_set_header Host  $host;
                proxy_http_version     1.1;
                proxy_set_header Upgrade    $http_upgrade;
                proxy_set_header Connection $connection_upgrade;
                        proxy_ssl_verify off;
            }

            #По желанию - сервис-перехватчик подписок который даёт уже правильные данные
            #(т.к. 3x-ui выдаст что у нас security=none, и вообще надо на 127.0.0.1 подключаться)
            location ~ ^/Ваша секретная ссылка на сервис-исправлятель подписок/(.*)$ {
                proxy_pass             http://127.0.0.1:<ПОРТ ПЕРЕХВАТЧИКА>;
                proxy_set_header Host  $host;
                proxy_http_version     1.1;
                proxy_set_header Upgrade    $http_upgrade;
                proxy_set_header Connection $connection_upgrade;
                
            }

        
            #Притворяемся частью нашего легитимного сервиса, и выдаём Unauthorized, мол чего вы лезете во внутрянку?
            location = / {
                    return 401;
            }

            location / {
                    return 404;
            }
    }

    server {
            listen <ПОРТ2> ssl http2;
            server_name database.mydomain.site;
            ssl_certificate     /root/cert/mydomain.site/fullchain.pem;
            ssl_certificate_key /root/cert/mydomain.site/privkey.pem;
            ssl_protocols       TLSv1.2 TLSv1.3;
        
            client_header_timeout 5m;
            keepalive_timeout 5m;
            grpc_read_timeout 315;
            grpc_send_timeout 5m;
        
            location /ваша секретная ссылка {
                    client_max_body_size 0;
                    client_body_timeout 5m;
                    grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                    grpc_pass grpc://127.0.0.1:36490; 
                    #Внимание, нам не нужен gRPCs (хотя он и поддерживается), так как TLS уже обработал NGINX.
            }

            location / {
                    return 401; #Посылаем любопытных 
            }
    }

    server {
            listen 8011 ssl;

            ssl_reject_handshake on; #Обрываем соединение
            ssl_protocols              TLSv1.2 TLSv1.3;
    }
}

У некоторых наверняка возник вопрос: "так, а зачем возится с блоком http? Нельзя ли просто проксировать сразу на inbound?" Можно. НО: у вас по дефолту идёт ответ 404, да и нельзя кастомизировать страницу 404, а если у вас большой легитимный сервис, это может стать реальной проблемой. А ещё работает только режим stream-one, не packet-up или stream-up (можете меня поправить в комментах если у вас работает, потому что у меня не работало).

И, наверняка, вы думаете: "а откуда у вас взялся grpc_pass??? У нас же XHTTP?". А это интересный трюк, который включает мультиплексирование (и, до версии 1.29.4, когда ещё не было поддержки h2 в upstream, включало h2), который сам не знаю как, но работает. С grpc_pass этим работает и stream-one, и stream-up (самый быстрый). Packet-up можно включить, но придётся сделать отдельную директиву для packet-up (т.к. нужен proxy_pass вместо grpc_pass), использовать proxy_http_version 2, а также у вас будет http2 без мультиплексирования (или вообще без http2, если у вас 1.29.2 и ниже). На момент написания статьи актуальна версия 1.29, если появится полномасштабная поддержка http2 + multiplex для streams - напишите, мне правда интересно. Как я понимаю, чтобы получить мультиплексирование - нужно переписывать заново пол обработчика событий NGINX, поэтому этого так скоро не будет. Можете также меня поправить, если я не прав.

Исправляем security=none у клиентов

Итак, NGINX и 3x-ui настроены, можно тестировать! Но если вы просто скопируете inbound из панели - у вас ничего не получится. Это потому что (как я писал выше) вы выставили listen ip на 127.0.0.1, а также поставили security=none и всё сделали правильно. Но клиенту нужен ваш домен, а security=tls, так как NGINX всё-таки обрывает TLS, а на свой localhost стучаться смысла мало :). Поэтому необходимо поставить себе middleware, который будет "исправлять" подписки. Я навайбкодил такой клиент: https://github.com/JM001113/VPN_sub_middleware.

Если прокся чисто для вас и меняться не планирует, просто ручками скопируйте URL подписки и замените все нужные ключи, и пропускайте шаг ниже:

  • Security - вместо none- TLS

  • ALPN - http/1.1,h2

  • host= (нужно добавить в таком виде, даже если он пустой, а он у нас пустой)

Для этого просто ставьте FastAPI, и меняете параметры в .env.example (переименуйте в .env). Учтите что он сделан за 5 минут, так что 100% будут баги. Pull requests are welcome, так сказать.

# Service bind port
SUB_PORT=Ваш порт к которому будет подключаться NGINX

# Upstream 3x-ui subscription source
BASE_SUB_PORT=Порт subscription end панели
BASE_SUB_URL=Base url для subscription сервиса
UPSTREAM_HOST=127.0.0.1 (или сайт если middleware крутится на другом сервере)

# Rewrites applied to every supported link
TARGET_HOST=Домен ИНБАУНДА!
TARGET_PORT=Порт домена инбаунда (443)
TARGET_LINK_NAME=Имя вашей подписки
# Также можно менять другие параметры типа SNI в XHTTP settings
# TARGET_SNI=server.example.com

# Upstream request timeout
UPSTREAM_TIMEOUT_SECONDS=10

Далее просто создаёте venv python -m venv venv, активируете (source venv/bin/activate), устанавливаете зависимости с помощью pip install -r requirements.txt, и запускаете с помощью python main.py. NGINX уже пробрасывает трафик куда надо. Так что можем пользоваться.

Плюсы моего метода

  1. Есть настоящий, легитимный сервис. Например, в этом туториале (отличный, кстати, я по нему изначально делал первый прокси) есть свой домен, но нет сервиса. Если главная страница домена выдаёт 404 — это уже подозрительно. А если главная страница database.домен или service.домен, или api.домен выдаёт 403 или 401, это уже более чем правдоподобно.

  2. Если вам нужно несколько inbound‑ов — можно либо наклепать субдоменов (не советую!), либо же сделать несколько secret paths, каждый для своего inbound.

  3. Расширение «в ширину» — можно сделать несколько айпишников на один и тот же домен или субдомен, и у вас выходит fault tolerant система:). А в случае чего можно просто сменить айпишник только субдомена, и клиенту даже не надо обновлять подписки, только A‑запись.

Минусы

  1. Самый главный — как и в том туториале, человеку хочется просто поставить и забыть, а не вот это всё. И я на это никак не могу ответить, потому что это так. Однако мой способ изначально для тех, кто хочет выстроить или у кого уже есть легитимный сервис.

  2. Нужен домен. Да, его просто купить, и даже дёшево, на том же namecheap.top стоит 3$ за первый год и 6$ за продление, но я не думаю что все хотят мучаться с покупкой криптовалюты.

Заключение

Я не очень уверен, насколько эта статья поможет другим, т.к. вряд ли у всех есть легитимный сервис, за которым можно «прикрыть» VPN‑сервис. Ну а про профессионализм написания я вообще молчу. Это мало того что первая статья на Хабре, это вообще одна из моих первых статей когда-либо написанных для широкой публики. Но зато это бесценный опыт и feedback от читателей, который мне как раз очень нужен. Спасибо, что дочитали до конца!

Туториал, которым я вдохновлялся