You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

334 lines
19 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

---
title: SNI Fallback
---
# Маскировка и разделение трафика по доменам с помощью функции SNI Fallback
VLESS - это очень легкий протокол, который, как и Trojan, не использует сложного шифрования и обфускации трафика. Вместо этого он, подобно тому, как искусный мастер кунг-фу скрывает свою силу, шифрует трафик с помощью протокола TLS, маскируя его под обычный HTTPS-трафик и позволяя ему беспрепятственно проходить через Великий китайский файрвол. Для лучшей маскировки от активного зондирования вместе с VLESS была представлена функция Fallbacks (резервирование). В этой статье мы рассмотрим, как использовать функцию Fallbacks входящего протокола VLESS в Xray совместно с Nginx или Caddy для реализации разделения трафика по доменам при обеспечении полной маскировки.
## Сценарии использования
Из-за XTLS Xray необходимо прослушивать порт 443, что создает проблему, если на сервере уже запущен веб-сайт - сайт либо не сможет работать, либо его придется запускать на другом порту, что нежелательно. Есть три способа решить эту проблему:
- Xray прослушивает другие часто используемые порты (например, 22, 3389, 8443).
Это самое простое решение, но оно не идеально.
- Nginx или HAProxy прослушивает порт 443 и выполняет обратное проксирование на уровне L4 с разделением трафика по SNI, что позволяет использовать один порт для нескольких сервисов.
Этот вариант более сложный и требует определенных знаний Nginx или HAProxy, поэтому мы не будем его здесь подробно рассматривать.
- Xray прослушивает порт 443 и использует функцию Fallbacks для перенаправления трафика веб-сайта на Nginx или Caddy на основе SNI.
Этот вариант имеет среднюю сложность и является тем, который мы рассмотрим в этом руководстве.
## Что такое SNI
**SNI** (Server Name Indication) - это расширение протокола TLS. Те, кто знаком с обратным проксированием, знают, что для правильной маршрутизации трафика по доменному имени необходимо следующее правило:
```nginx
proxy_set_header Host имя_хоста;
```
Эта строка устанавливает HTTP-заголовок "Host" на определенное имя хоста. Зачем это нужно? Обычно у одного сервера один IP-адрес, но на нем может быть запущено несколько сайтов. Пользователи получают IP-адрес по доменному имени и обращаются к серверу, но как сервер определяет, какой именно сайт запрашивает пользователь? Для этого используются виртуальные хосты, основанные на имени.
Когда веб-сервер получает запрос, он проверяет заголовок "Host" и направляет пользователя на нужный сайт. Однако, когда HTTP-трафик шифруется с помощью TLS, этот простой метод перестает работать. TLS-рукопожатие происходит до того, как сервер увидит какие-либо HTTP-заголовки, поэтому сервер не может использовать информацию из заголовка "Host", чтобы решить, какой сертификат предоставить, и тем более не может определить, к какому сайту обращается пользователь.
SNI решает эту проблему, позволяя клиенту отправлять имя хоста как часть TLS-рукопожатия. Поэтому при использовании Nginx для обратного проксирования HTTPS-трафика необходимо добавить в конфигурацию `proxy_ssl_server_name on;`. В этом случае Nginx будет отправлять информацию SNI на проксируемый сервер, решая проблему неработающих виртуальных хостов по HTTPS. Кроме того, при использовании SNI можно получить доступ к нужному сайту, даже не указывая заголовок "Host".
## Идея
![Схема работы Xray Fallbacks](./fallbacks-with-sni-resources/xray-fallbacks.svg)
После получения трафика на порт 443 Xray расшифровывает TLS и перенаправляет трафик с длиной первого пакета менее 18 байт, неверной версией протокола или ошибкой аутентификации на адрес, указанный в `dest`, на основе совпадения `name`, `path` или `alpn`.
## Добавление DNS-записей
![DNS-записи](./fallbacks-with-sni-resources/xray-dns-records.webp)
Измените домен и IP-адрес в соответствии с вашей ситуацией.
## Запрос TLS-сертификата
Поскольку нам нужно разделять трафик по доменам с разными поддоменами, а wildcard-сертификат покрывает только домены между двумя точками (например, сертификат для `*.example.com` не будет действовать для `example.com` и `*.*.example.com`), нам нужно запросить wildcard-сертификат с [SAN](https://ru.wikipedia.org/wiki/Subject_Alternative_Name). Согласно информации на сайте Let's Encrypt[^1], для запроса wildcard-сертификата требуется проверка DNS-01. В этом руководстве мы рассмотрим, как запросить бесплатный TLS-сертификат Let's Encrypt с помощью [acme.sh](https://acme.sh) для домена, управляемого Cloudflare. Инструкции для других провайдеров DNS можно найти в [dnsapi · acmesh-official/acme.sh Wiki](https://github.com/acmesh-official/acme.sh/wiki/dnsapi).
Сначала нужно создать API-токен на [панели управления Cloudflare](https://dash.cloudflare.com/profile/api-tokens). Параметры следующие:
![Настройка прав доступа API-токена](./fallbacks-with-sni-resources/cf-api-token-permissions-for-acme.webp)
Настройка прав доступа очень важна, остальные параметры можно оставить по умолчанию.
После создания вы получите строку символов - это и есть ваш API-токен (`CF_Token`). Сохраните его в надежном месте, так как он больше не будет отображаться.
::: tip Внимание
Следующие действия необходимо выполнять от имени пользователя root. Использование sudo может привести к ошибкам.
:::
```bash
curl https://get.acme.sh | sh # Установка acme.sh
export CF_Token="sdfsdfsdfljlbjkljlkjsdfoiwje" # Установка переменной окружения с API-токеном
acme.sh --issue -d example.com -d *.example.com --dns dns_cf # Запрос сертификата с проверкой DNS-01
mkdir /etc/ssl/xray # Создание каталога для хранения сертификата
acme.sh --install-cert -d example.com --fullchain-file /etc/ssl/xray/cert.pem --key-file /etc/ssl/xray/privkey.key --reloadcmd "chown nobody:nogroup -R /etc/ssl/xray && systemctl restart xray" # Установка сертификата в указанный каталог и настройка команды для автоматического перезапуска Xray после обновления сертификата
```
## Конфигурация Xray
```json
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"port": 443,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "UUID",
"flow": "xtls-rprx-vision"
}
],
"decryption": "none",
"fallbacks": [
{
"name": "example.com",
"path": "/vmessws",
"dest": 5000,
"xver": 1
},
{
"dest": 5001,
"xver": 1
},
{
"alpn": "h2",
"dest": 5002,
"xver": 1
},
{
"name": "blog.example.com",
"dest": 5003,
"xver": 1
},
{
"name": "blog.example.com",
"alpn": "h2",
"dest": 5004,
"xver": 1
}
]
},
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": {
"alpn": ["h2", "http/1.1"],
"certificates": [
{
"certificateFile": "/etc/ssl/xray/cert.pem",
"keyFile": "/etc/ssl/xray/privkey.key"
}
]
}
}
},
{
"listen": "127.0.0.1",
"port": 5000,
"protocol": "vmess",
"settings": {
"clients": [
{
"id": "UUID"
}
]
},
"streamSettings": {
"network": "ws",
"wsSettings": {
"acceptProxyProtocol": true,
"path": "/vmessws"
}
}
}
],
"outbounds": [
{
"protocol": "freedom"
}
]
}
```
Эта конфигурация предназначена для Nginx. Обратите внимание на следующие детали:
- О Proxy Protocol
Proxy Protocol - это протокол, разработанный HaProxy для решения проблемы потери информации о клиенте при проксировании, часто используемый в цепочках прокси-серверов и обратных прокси. Традиционные методы решения этой проблемы, как правило, сложны и имеют много ограничений, в то время как Proxy Protocol очень прост - он просто добавляет пакет данных с информацией об исходном соединении (четверка "источник-назначение:порт") при передаче данных.
У всего есть свои плюсы и минусы, и Proxy Protocol не исключение.
- Если есть отправка, должен быть и прием, и наоборот.
- Один и тот же порт не может одновременно поддерживать соединения с данными Proxy Protocol и без них (например, разные виртуальные хосты (server) Nginx на одном порту, что по сути является следствием предыдущего пункта)[^2][^3].
Если вы столкнулись с ошибками, убедитесь, что ваша конфигурация соответствует этим условиям.
Здесь мы используем Proxy Protocol, чтобы целевой сервер, на который перенаправляется трафик, получал реальный IP-адрес клиента.
Кроме того, если в конфигурации входящего трафика Xray есть `"acceptProxyProtocol": true`, ReadV будет отключен.
- О HTTP/2
Во-первых, порядок элементов в `inbounds.streamSettings.tlsSettings.alpn` важен: `h2` должен быть перед `http/1.1`, чтобы обеспечить совместимость при использовании HTTP/2. Обратный порядок приведет к тому, что HTTP/2 будет понижен до HTTP/1.1 во время согласования, делая конфигурацию недействительной.
В приведенной выше конфигурации каждая запись fallback для Nginx разделена на две. Это связано с тем, что h2 - это обязательное зашифрованное соединение HTTP/2, что хорошо для безопасности передачи данных в Интернете, но не нужно внутри сервера. h2c же - это незашифрованное соединение HTTP/2, подходящее для этой среды. Однако Nginx не может одновременно прослушивать HTTP/1.1 и h2c на одном порту. Чтобы решить эту проблему, необходимо указать `alpn` (в разделе `fallbacks`, а не `tlsSettings`), чтобы сопоставить результаты согласования TLS ALPN.
Рекомендуется указывать `alpn` только в двух случаях[^4]:
- опустить
- `"h2"`
Если вы используете Caddy, то вам не нужно так усложнять, поскольку он **может** одновременно прослушивать HTTP/1.1 и h2c на одном порту. Изменения в конфигурации следующие:
```json
{
"fallbacks": [
{
"name": "example.com",
"path": "/vmessws",
"dest": 5000,
"xver": 1
},
{
"dest": 5001,
"xver": 1
},
{
"name": "blog.example.com",
"dest": 5002,
"xver": 1
}
]
}
```
## Конфигурация Nginx
Nginx будет установлен из официального репозитория.
```bash
sudo apt install curl gnupg2 ca-certificates lsb-release
echo "deb [arch=amd64] http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.list
curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo apt-key add -
sudo apt update
sudo apt install nginx
```
Удалите `/etc/nginx/conf.d/default.conf` и создайте `/etc/nginx/conf.d/fallbacks.conf` со следующим содержимым:
```nginx
set_real_ip_from 127.0.0.1;
real_ip_header proxy_protocol;
server {
listen 127.0.0.1:5001 proxy_protocol default_server;
listen 127.0.0.1:5002 proxy_protocol default_server http2;
location / {
root /srv/http/default;
}
}
server {
listen 127.0.0.1:5003 proxy_protocol;
listen 127.0.0.1:5004 proxy_protocol http2;
server_name blog.example.com;
location / {
root /srv/http/blog.example.com;
}
}
server {
listen 80;
return 301 https://$host$request_uri;
}
```
## Конфигурация Caddy
Инструкции по установке Caddy можно найти в [Install — Caddy Documentation](https://caddyserver.com/docs/install).
Чтобы Caddy мог получать реальный IP-адрес посетителя, необходимо скомпилировать Caddy с модулем Proxy Protocol. Рекомендуется скомпилировать его прямо на сайте Caddy.
```bash
sudo curl -o /usr/bin/caddy "https://caddyserver.com/api/download?os=linux&arch=amd64&p=github.com%2Fmastercactapus%2Fcaddy2-proxyprotocol&idempotency=79074247675458"
sudo chmod +x /usr/bin/caddy
```
Просто замените существующий бинарный файл.
::: tip
Рекомендуется сначала установить Caddy, следуя инструкциям на официальном сайте, а затем заменить бинарный файл. Это избавит от необходимости настраивать запуск сервиса вручную.
:::
Отредактируйте `/etc/caddy/Caddyfile`:
```Caddyfile
{
servers 127.0.0.1:5001 {
listener_wrappers {
proxy_protocol
}
protocol {
allow_h2c
}
}
servers 127.0.0.1:5002 {
listener_wrappers {
proxy_protocol
}
protocol {
allow_h2c
}
}
}
:5001 {
root * /srv/http/default
file_server
log
bind 127.0.0.1
}
http://blog.example.com:5002 {
root * /srv/http/blog.example.com
file_server
log
bind 127.0.0.1
}
:80 {
redir https://{host}{uri} permanent
}
```
## Ссылки
1. [Указание имени сервера - Википедия](https://ru.wikipedia.org/wiki/Указание_имени_сервера)
2. [Home · acmesh-official/acme.sh Wiki](https://github.com/acmesh-official/acme.sh/wiki)
3. [HTTP/2 - Википедия](https://ru.wikipedia.org/wiki/HTTP/2)
## Примечания
[^1]: [Часто задаваемые вопросы - Let's Encrypt - бесплатные SSL/TLS сертификаты](https://letsencrypt.org/ru/docs/faq/)
[^2]: [Proxy Protocol - HAProxy Technologies](https://www.haproxy.com/blog/haproxy/proxy-protocol/)
[^3]: [proxy protocol 介绍及 nginx 配置 - 简书](https://www.jianshu.com/p/cc8d592582c9)
[^4]: [v2fly-github-io/vless.md at master · rprx/v2fly-github-io](https://github.com/rprx/v2fly-github-io/blob/master/docs/config/protocols/vless.md)