Xray VLESS Reality и свои сайтики на одном порту

Понадобилось вот разместить сайт и Xray. На одном хостинге. На одном порту. И чтобы ещё SSL работал.

Итак, мы имеем:

  • Xray с VLESS Reality настроен на домен, скажем, google.com и работает на порту 443
  • Сайт myblog12345.ru хостится на 443 порту через nginx.

Данная проблема зачастую решается модулем stream для nginx примерно так:

stream {
  map $ssl_preread_server_name $sni_name {
      google.com           xray;
      myblog12345.ru       www;
      default              xray;
  }

  upstream xray {
      server 127.0.0.1:8443; # слушает xray
  }

  upstream www {
      server 127.0.0.1:7443; # слушает nginx
  }

  server {
      listen          443; # отдаём наружу
      proxy_pass      $sni_name;
      ssl_preread     on;
  }
}

server {
    listen 80;
    server_name myblog12345.ru;
    return 301 https://$host$request_uri;
}

server {
    listen 7443 ssl;
    server_name myblog12345.ru;

    # ... настройки SSL

    root /var/www/myblog12345.ru;

    index index.html index.htm;

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

Я тоже настроил всё таким способом, но радовался недолго - в access log nginx попадают не реальные IP пользователей, а 127.0.0.1. Соответственно, какие-либо правила фильтрации/безопасности не настроить.

Проблема передачи адреса обычно решается директивой proxy_protocol в блоке stream.server. Только вот есть нюанс - эта директива не поддерживает динамическое переключение. То есть, мы или весь трафик оборачиваем в прокси, или отключаем это. Для Xray такое не прокатит.

Проблему я решил отказом от nginx stream в пользу haproxy.

Итак, меньше слов - больше дела.

  1. Xray настраиваем таким образом, чтобы тот слушал порт 8443. Все номера портов взяты для примера, это не принципиально.
"listen": "0.0.0.0",
"port": 8443,
  1. Конфиг haproxy выглядит так:
global
	log /dev/log	local0
	log /dev/log	local1 notice
	chroot /var/lib/haproxy
	stats socket /run/haproxy/admin.sock mode 660 level admin
	stats timeout 30s
	user haproxy
	group haproxy
	daemon

defaults
    mode tcp
    timeout connect 5s
    timeout client 60s
    timeout server 60s
    log global
    option tcplog

frontend tls_frontend
    bind *:443
    mode tcp
    option tcplog

    tcp-request inspect-delay 5s
    tcp-request content capture req.ssl_sni len 50

    use_backend xray_backend if { req.ssl_sni -i -m end google.com }
    use_backend www_backend if { req.ssl_sni -i -m end myblog12345.ru }
    default_backend xray_backend

backend xray_backend
    mode tcp
    server xray_server 127.0.0.1:8443

backend www_backend
    mode tcp
    server www_server 127.0.0.1:7443 send-proxy
  1. Конфиг самого сайта в nginx выглядит так. Сайт для SSL слушает порт 7443:
server {
    listen 80;
    server_name myblog12345.ru;
    return 301 https://$host$request_uri;
}

server {
    listen 7443 ssl proxy_protocol; # <- Важно добавить proxy_protocol, иначе получим ошибку SSL
    server_name myblog12345.ru;

    # ловим IP пользователя от haproxy
    real_ip_header proxy_protocol;
    set_real_ip_from 127.0.0.1;

    # ... настройки SSL

    root /var/www/myblog12345.ru;

    index index.html index.htm;

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

Теперь такая связка позволяет работать с реальным IP пользователя.