Ротация путей Core → Entry
Содержание
Проблема #
VLESS поверх gRPC практически неотличим от легитимного HTTPS: TLS 1.3 с валидным сертификатом, HTTP/2, браузерный TLS-фингерпринт. Распознать такой трафик по сигнатурам крайне сложно.
Однако даже полностью зашифрованный трафик оставляет метаданные. Постоянное соединение по одному и тому же gRPC-пути между двумя хостами — это паттерн. Для реальных gRPC-микросервисов подобная картина нетипична: сервисы обычно обращаются к множеству разных эндпоинтов в разное время. Статический путь, неизменный неделями, может быть выделен статистическим анализом из общего потока.
Решение: менять serviceName автоматически, с заданной периодичностью. Со стороны это выглядит как активность различных сервисов на одном хосте — нормальная картина для любого сервера с gRPC-API.
Что ротируется #
Ротация затрагивает только внутренний канал между Entry и Core. Клиентская сторона остаётся неизменной — клиентам не нужно обновлять конфигурацию.
Клиент ──[фиксированный путь]──► Entry Nginx → Entry Xray inbound
│
Entry Xray outbound
│
[ротируемый путь]
│
▼
Core Nginx → Core Xray inbound
| Нода | Компонент | При ротации |
|---|---|---|
| Core | Nginx | Обновляется location для gRPC-проксирования |
| Core | Xray inbound | Обновляется serviceName в grpcSettings |
| Entry | Xray outbound | Обновляется serviceName → Core |
| Entry | Nginx | Не меняется — клиентский путь фиксирован |
| Entry | Xray inbound | Не меняется — клиентский serviceName фиксирован |
Механизм ротации #
Core-нода является управляющим центром процесса. По расписанию запускается скрипт, который выполняет следующую последовательность:
- Генерирует новый
serviceNameформатаapi.v2.rpc.<16 hex-символов>. - Создаёт резервные копии конфигов Nginx и Xray на Core.
- Вносит изменения, валидирует конфиги (
xray -test,nginx -t). При ошибке — откат из резервной копии, Entry-ноды не трогаются. - Перезагружает Nginx и Xray на Core.
- По SSH подключается к каждой Entry-ноде и обновляет только
serviceNameв outbound Xray. Nginx и inbound Xray на Entry не затрагиваются. - Валидирует конфиг на Entry, перезагружает Xray. При ошибке — откат на Entry.
- Обновляет запись маршрута в registry: новый
serviceNameи дата ротации. Коммитит изменения.
Обработка ошибок #
- Конфиги валидируются перед каждой перезагрузкой. При ошибке — откат из резервной копии, сервисы не перезагружаются.
- Если обновление Core не удалось — Entry-ноды не обновляются. Старый путь остаётся рабочим на обоих концах.
- Если SSH-подключение к Entry-ноде недоступно — ошибка логируется, скрипт продолжает с остальными нодами.
- При следующем запуске недоступные Entry-ноды получат актуальный путь.
Расписание #
Скрипт запускается по systemd timer:
- Интервал: каждый час (
OnCalendar=hourly). - Случайное смещение: 0–30 минут (
RandomizedDelaySec=1800). Persistent=true— если запуск был пропущен (нода выключена), выполняется при следующем старте.
Случайное смещение исключает предсказуемость момента ротации: каждый час путь меняется в случайную минуту в пределах получаса.
Что происходит во время ротации #
При перезагрузке сервисов активные соединения кратковременно обрываются. Клиент переподключается к Entry-ноде автоматически по прежнему URI — смена пути на внутреннем участке Entry → Core для него незаметна.
Схема #
Сводная таблица #
| Компонент | Где хранится | Когда меняется |
|---|---|---|
serviceName Entry → Core | Registry, Nginx/Xray Core, Xray outbound Entry | По расписанию (каждый час ± 30 мин) |
serviceName Client → Entry | Registry, Nginx/Xray Entry | Независимая ротация |
| Клиентский URI | Только на устройстве | Не меняется при ротации Core → Entry |
Перспектива #
В дальнейшем SSH-обновление Entry-нод может быть заменено на управление через Xray gRPC API (hot reload без перезапуска сервисов). Это позволит выполнять ротацию без разрыва активных соединений.