VLESS Reality маскировка под собственный сайт

Добрый день, настраиваю VLESS Reality с маскировкой под свой сайт и столкнулся с проблемой.

Настроил так, чтобы на порту 443 висел и nginx с сайтом, и xray XTLS-Reality. Если хотят зайти на один из моих сайтов - nginx обрабатывает всё как обычно, если заходят на домен vpn, то запрос перенаправляется в xray.

Но при url тесте в nekoray и попытках подключения к VPN соединение не удаётся. Логи nginx показывают только это:

[25/Dec/2024:18:41:17 +0000] "GET / HTTP/2.0" 200 746 "-" "Chrome"
[25/Dec/2024:18:41:17 +0000] "GET / HTTP/2.0" 200 746 "-" "Chrome"
[25/Dec/2024:18:41:17 +0000] "GET / HTTP/2.0" 200 746 "-" "Chrome"
[25/Dec/2024:18:41:18 +0000] "GET / HTTP/2.0" 200 746 "-" "Chrome"

В логах 3X-UI ничего нет.
При этом, если использовать маскировку под чужой домен, всё работает нормально.

Мой nginx.conf:

user nginx;
worker_processes auto;
pid /run/nginx.pid;

events {
    worker_connections 1024;
}

stream {
    map $ssl_preread_server_name $name {
        site.mydomain.com             www;
        othersite.mydomain.com     www;
      	vpn.mydomain.com             xray;
        default                                 xray;
    }

    upstream xray {
        server 127.0.0.1:8050;
    }

    upstream www {
        server 127.0.0.1:7443;
    }

    server {
        listen               443;
        listen               [::]:443;
        proxy_pass      $name;
        ssl_preread     on;
    }

    server {
        listen 127.0.0.1:8050;
        proxy_pass 127.0.0.1:8443;
    }
}

http {
    include /etc/nginx/mime.types;
    server {
        listen 80;
        listen [::]:80;
        return 301 https://$host$request_uri;
    }

    server {	
        listen 127.0.0.1:7443 ssl;
    	http2 on;

        server_name site.mydomain.com;

        ssl_certificate /etc/letsencrypt/live/site.mydomain.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/site.mydomain.com/privkey.pem;

    	ssl_protocols TLSv1.3;
        ssl_prefer_server_ciphers on;
        ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';

        root /var/www/site.mydomain.com;
        index index.html;

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

Конфиг подключения, указан 8443 порт, но подключаюсь по 443:

У меня мало опыта работы с nginx, и VPN я поднимаю впервые, буду рад советам и любой помощи, спасибо!

Как есть вы должны подключаться к Xray по 8443, он там вас ждет. А на 443 вас встречает Nginx, понятно, что он знать не знает ни про какие VLESS ключи и туннели.

    server {
        listen               443;
        listen               [::]:443;
        proxy_pass      $name;
        ssl_preread     on;
    }

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

Спасибо! Всё оказалось проще, чем я думал)

После маскировки под свой сайт пинг увеличился в 2 раза) когда маскировался под www.ign.com пинг был 67-70мс, теперь стал 120-130мс, с чем это может быть связано?
UPD: отбой, похоже проблема была никак не связана с маскировкой и с моей машиной вообще, просто был сбой на стороне хостера

Добрый день!
Можете подсказать по какому гайду все это делали?
Вожусь уже второй день, и никак не могу заставить работать такой вариант, чтобы маскироваться своим доменом.

никаких гайдов особо нет, вот код моего nginx.conf

user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;

events {
	worker_connections 768;
}

http {

	sendfile on;
	tcp_nopush on;
	types_hash_max_size 2048;
	include /etc/nginx/mime.types;
	default_type application/octet-stream;
	access_log /var/log/nginx/access.log;
	gzip on;
	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/sites-enabled/*;
}

вот содержимое nginx/sites-available/default.conf

server {
	listen 80 default_server;
	listen 1111 ssl default_server;
	listen 8443 ssl default_server;
        server_name yourdomain.name;
	
	root /var/www/html;
	# Разрешить доступ к index.html
    	location = /index.html {
        index index.html;
    }

    	# Разрешить доступ к favicon.ico
    	location = /favicon.ico {
        # Можно указать root или, если файл находится в том же каталоге:
        root /var/www/html;
    }

    	# Разрешить доступ к корню, который отдает index.html
    	location = / {
        try_files /index.html =403;
    }

    	# Все остальные запросы блокируются
    	location / {
        return 403;
    }
        ssl_certificate /root/cert/yourdomain.name/fullchain.pem;
        ssl_certificate_key /root/cert/yourdomain.name/privkey.pem;
	ssl_session_cache shared:le_nginx_SSL:10m;
	ssl_session_timeout 1440m;
	ssl_session_tickets off;
	ssl_protocols TLSv1.2 TLSv1.3;
	ssl_prefer_server_ciphers off;
	ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
}

содержимое nginx/sites-available/static_site

server {

    server_name localhost;

    location / {
        root /var/www/html;
        index index.html;
    }
}

соответственно в панели 3x-ui
port: 443 (панель его слушает, в случае если нет успешной авторизации - кидает его в nginx на 1111 порт)
dest: 127.0.0.1:1111
SNI: yourdomain.name
yourdomain.name - меняешь на свой домен, пути к SSL сертификату тоже надо поставить актуальные
ессно по пути /var/www/html/ предполагается наличие некого index.html и favicon.ico
еще лишние порты закрыть, 1111 оставить только для localhost на всякий

Устанавливаешь nginx. Настраиваешь его слушать ssl соединение на 127.0.0.1:port, добавляешь пути к сертификатам (выше скинули примерный конфиг). Главное, в качестве порта не указывай 443, потому что на нём будет XRay сидеть. В настройках REALITY в качестве Dest указываешь 127.0.0.1port, а в SNI запихиваешь свой домен, если покупал.

Вот как вариант, довольно хороший пример конфигураций nginx / сервера / клиента - тык

Хм, интересно… То есть можно без своего домена сделать steal oneself? Если же нет, и домен в любом случае нужен, то подойдёт ли бесплатный домен третьего уровня по типу qwertyuiop.duckdns.org?

без своего домена не получится, но домен 3его уровня теоретически можно, LE вроде как даст SSL сертификат

Вот на него то я и наткнулся ранее (только на сначала на эту ссылку Xray REALITY with 'steal oneself') и целый день мучался… Даже сайт стал мимикрировать под livelove-anime.jp )) Но вот само подлюкчение к vps через клиента не заработало…
Возможно я уже на vps-ке накрутил за последние два дня экспериментов.
В общем сброшу ее и начну по новой.

выше я выложил 100% работоспособный конфиг и настройки со своей vps

Можете попробовать вот как тут описывал. Общо, но зато проще, чем у Чики-китайца настраивается.

Можно, но будут нужны самозаверенные сертификаты. Про такой вариант я писал здесь. Если надо, могу подробнее рассказать, как это делается, но у меня сейчас нет уверенности, что такая схема сможет пройти active probing, ради которого REALITY и создавался. Если вас такое не смущает, то в SNI можно указывать что угодно или не указывать вообще ничего (даже неважно, под свой сайт вы маскируетесь или нет), но отчасти теряются преимущества REALITY, как я сказал.

К сожалению, никаких гайдов именно по маскировке под собственный домен не находил, приходилось по крупицам искать инфу. Но могу поделиться всеми конфигами.

Xray в итоге должен работать на 443 порту, в dest нужно указать локалхост - 127.0.0.1 и какой-нибудь другой порт, например 7443. в SNI указываете домен своего сайта, под который маскируетесь.

И nginx должен слушать этот 127.0.0.1:7443 для вашего маскировочного сайта.
Мой итоговый nginx.conf:

user nginx;
worker_processes auto;
pid /run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    server {
        listen 80;
        listen [::]:80;
        return 301 https://$host$request_uri;
    }

    server {	
        listen 127.0.0.1:7443 ssl;
	    http2 on;

        server_name site.mydomain.com;

        ssl_certificate /etc/letsencrypt/live/site.mydomain.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/site.mydomain.com/privkey.pem;

	    ssl_protocols TLSv1.3;
        ssl_prefer_server_ciphers on;
        ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';

        root /var/www/site.mydomain.com;
        index index.html;

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

site.mydomain.com - ваш домен, под который вы притворяетесь. И домен должен быть привязан именно к этой машине через указание ip адреса машины в A-записи.

Если nginx у вас работает в докере, как и у меня, то нужно указать network_mode: host для nginx в docker-compose.yml или пробросить необходимые порты

А так не совсем понятно, с чем у вас возникли проблемы, поэтому полностью всё расписать не могу

Тоже недавно настраивал VLESS-REALITY-Steal-Oneself, на примере вот отсюда:

получилось что-то вроде такого:

nginx.conf
user nginx;
worker_processes auto;

error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
    use                epoll;
    multi_accept       on;
}

http {
    log_format main '[$time_local] $proxy_protocol_addr "$http_referer" "$http_user_agent"';
    access_log /var/log/nginx/access.log main;

    server_tokens   off;

    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;

    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;
        listen [::]:80;
        return 301 https://$host$request_uri;
    }

    server {
        listen                  127.0.0.1:8001 ssl default_server;

        ssl_reject_handshake    on;

        ssl_protocols           TLSv1.3;

        ssl_session_timeout     1h;
        ssl_session_cache       shared:SSL:10m;
    }

    server {
        listen                     127.0.0.1:8001 ssl proxy_protocol;
        http2                      on; 

        set_real_ip_from           127.0.0.1;
        real_ip_header             proxy_protocol;

        server_name                subdomain.domain.com; # Твой домен

        ssl_certificate            /root/cert/subdomain.domain.com/fullchain.pem; # Сертификат
        ssl_certificate_key        /root/cert/subdomain.domain.com/privkey.pem; # Приватный ключ

        ssl_protocols              TLSv1.3;
        ssl_ciphers                TLS13_AES_128_GCM_SHA256:TLS13_AES_256_GCM_SHA384:TLS13_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305;
        ssl_prefer_server_ciphers  on;

        ssl_stapling               on;
        ssl_stapling_verify        on;
        resolver                   1.1.1.1 valid=60s;
        resolver_timeout           2s;

        location / {
            sub_filter                            $proxy_host $host;
            sub_filter_once                       off;

	    #deny                                  all; # Если хочешь иметь 403 Forbidden при заходе на сайт, откоменчиваешь эту строку и закоменчиваешь две снизу
	    set $website                          www.lovelive-anime.jp; # Сюда можно добавить любую ссылку, контент с этого сайта будет отображаться при заходе на твой сайт
	    proxy_pass                            https://$website;
            resolver                              1.1.1.1;

            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;
        }
    }
}
Настройки 3x-ui


В чем смысл маскироваться под свой сайт ? Делать вид что там файлопомойка легальная ?

Я далеко не эксперт, но вот мои мысли по поводу этого:

  1. Потенциально меньше задержек и более стабильное соединение, благодаря тому, что сервер и твой домен для маскировки висят на одном айпи
  2. Когда маскируешься под чужой домен, ты отправляешь на него довольно много запросов и владелец сайта может заблеклистить айпи твоего сервера, соответственно v+r сразу же отвалится
  3. Как ты и сказал, на своем домене можно поднять какой-нибудь некстклауд и притвориться файлопомойкой, чтобы у цензора не было вопросов к количеству трафика, который ты гоняешь через свой домен
  4. При наличии своего домена, можно настроить проксирование через CDN, благодаря новомодному XHTTP или “устаревшему” gRPC, что сделает твой прокси еще более устойчивым к потенциальным нападкам от цензора

А с чего от вас запросы идут на воруемый SNI? Когда вы заходите на Xray вы устанавливаете туннель и работаете через него, на условный yahoo.com:443 вы вообще не обращаетесь. Туда Xray перекидывает не ваши VLESS, а прочие запросы. Это только одинокий РКН, который когда-то, возможно, придет вас пощупать. И случайные школоботы, которые пришли вас сканировать на предмет дыр. Так они все подряд сканируют. А на yahoo перекинутся только те, которые к вам почему-то по 443 обращаются. А кому еще нужен ваш IP? Там серьезному трафику на воруемый домен неоткуда взяться.

Reality на каждый запрос (каждый открытый сайт с клиента это запрос) делает обращение к заданному sni чтобы получить его сертификат и передать клиенту