mirror of
https://github.com/XTLS/Xray-docs-next.git
synced 2025-01-21 02:01:41 +03:00
3b23ce3ea2
* add Russian lang support --------- Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
334 lines
19 KiB
Markdown
334 lines
19 KiB
Markdown
---
|
||
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)
|
||
|
||
|