Техническое описание схемы: транспорт, что видит узел, чего он увидеть не может, и что проверялось.
Два узла: Даллас и Алматы. На каждом Xray 26.2.6.
| Слой | Что используется |
|---|---|
| Основной вход | VLESS → WebSocket → TLS 1.3, сертификат
Let's Encrypt на поддомене узла. Снаружи неотличим от обычного HTTPS-сайта |
| Резервный вход | VLESS → TCP → REALITY на 8443,
маскировка под сторонний домен с валидным сертификатом |
| Аутентификация | UUID v4 на пользователя, свой у каждого |
| Исходящий | freedom с domainStrategy: UseIPv4 |
Главный вопрос — отвечаю по слоям.
Внутри туннеля идёт ваш собственный TLS до конечного сайта. Узел терминирует только внешний слой (свой TLS) и передаёт дальше байты, которые для него непрозрачны: ключи сессии согласованы между вашим браузером и банком, на сервере их нет и быть не может.
[браузер] --TLS до банка--------------------------> [банк]
\__ VLESS/WS/TLS до узла __/
узел видит: SNI, объём, тайминги
узел НЕ видит: тело, заголовки, пароли, номера карт
Чтобы узел увидел содержимое, потребовалось бы подменить сертификат банка, а это заметит браузер: pinning, CT-логи, предупреждение о недоверенном сертификате. Никакого MITM-прокси в схеме нет, CA в систему не ставится.
Разделение делается на клиенте, до туннеля.
В профиле прописаны правила: зона .ru, .рф, .su,
плюс явный список доменов банков, госуслуг и маркетплейсов, плюс
GEOIP,RU — всё это идёт DIRECT, минуя туннель.
Для этих соединений сервер не видит даже SNI: трафик физически не доходит до него.
Отдельно разведён DNS: русские имена резолвятся российским резолвером напрямую
(nameserver-policy), иначе был бы домашний IP при иностранном резолвере —
заметное несоответствие.
Ответ на «а что, если сервер изымут».
| Xray | loglevel: none, access: none,
error: none — журнал соединений не ведётся |
| systemd-journald | Storage=volatile — журнал только в tmpfs,
на диск не пишется, при перезагрузке исчезает |
| nginx (раздача подписок) | access_log off — токены
не попадают в логи |
Диагностические логи включались днём при
отладке и были затёрты через shred.
| Routing | Блокируются 127.0.0.0/8, RFC1918,
100.64.0.0/10, 169.254.0.0/16, ULA, link-local
и собственные адреса узла. Закрывает SSRF из туннеля к локальным службам |
| Протоколы | bittorrent → blackhole, исходящий
25 → blackhole; на файрволе также 465, 587 |
| SSH | Только ed25519-ключ. PasswordAuthentication no,
PermitRootLogin prohibit-password, fail2ban |
| Файрвол | ufw, default deny incoming; снаружи открыты
только 22, 443, 8443 |
| Обновления | unattended-upgrades включены, все текущие security-обновления установлены |
| Клиенты | Не объединены в L2/L3-сегмент, видеть друг друга не могут |
| Токен | 20 hex-знаков, secrets.token_hex(10) —
80 бит, перебор нереален |
| Отдача | nginx отдаёт только по regex
^/sub/[0-9a-f]+/[0-9a-f]{20}\.(txt|json|yaml)$,
autoindex off, всё остальное 404 |
| Страница | Ключ передаётся во fragment — не уходит в
Referer и не попадает в логи сервера. Валидация origin: принимается
только свой адрес подписки, чужой отклоняется |
| Заголовки | CSP default-src 'self',
frame-ancestors 'none', Referrer-Policy: no-referrer,
X-Content-Type-Options: nosniff. Внешних ресурсов на странице нет |
| Отзыв | UUID удаляется с обоих узлов, файл подписки стирается. Сначала узлы, потом реестр — чтобы сбой не оставил рабочий ключ |
127.0.0.1:8082 (там лежат подписки),
к RFC1918 и к собственному адресу узла — блокируется,
проверено с соседнего узлаsub= на чужой или сторонний адрес подписки —
отклоняется валидаторомinnerHTML/document.write,
значение идёт только в textContent и в href схемы
приложения — вектора нет