montana/Монтана-Протокол/Код/montana-vpn/docs/architecture.md

11 KiB
Raw Permalink Blame History

Архитектура Montana VPN — как собрана Helsinki

Этот документ описывает референсное развёртывание Montana VPN на Helsinki VPS (91.132.142.42, Ubuntu 24.04). Все компоненты, конфиги и обоснования живые — то есть так оно реально работает на момент 2026-05-02.

Слои стека

┌──────────────────────────────────────────────────┐
│ клиент (v2rayN / Hiddify / Streisand / sing-box) │
└───────────────────────┬──────────────────────────┘
                        │ TCP :443 (TLS Reality)
                        │ outer SNI = www.googletagmanager.com
                        │ inner = VLESS + xtls-rprx-vision
                        ▼
┌──────────────────────────────────────────────────┐
│ Helsinki VPS (Ubuntu 24.04, x86_64)              │
│                                                  │
│  ┌─────────────────────────────────────────┐    │
│  │ ufw (default deny)                      │    │
│  │   22/tcp  — SSH                          │    │
│  │   80/tcp  — nginx decoy (камуфляж)      │    │
│  │   443/tcp — xray Reality VPN            │    │
│  └─────────────────────────────────────────┘    │
│                                                  │
│  :80 ──► nginx (sites-enabled/decoy)             │
│            └── /var/www/decoy/index.html         │
│                "It works! Default server page."  │
│                                                  │
│  :443 ──► xray 26.2.6 (xtls/xray-core)           │
│            └── inbound vless+reality+vision      │
│            └── outbound freedom (direct)         │
│                                                  │
│  fail2ban  — ssh brute-force protection          │
│  crowdsec  — IP-reputation feed + auto-ban       │
│  sysctl    — fq_codel + BBR (anti-bufferbloat)   │
│                                                  │
└──────────────────────────────────────────────────┘
                        │ outbound (direct)
                        ▼
                       Internet

Почему именно VLESS + Reality + Vision

VLESS — минимальный V2Ray-протокол: только authentication по UUID, без обфускации поверх. Лёгкий, нет накладных расходов.

Reality — TLS-stealing handshake. Серверу не нужен собственный домен с валидным сертификатом. Вместо этого Reality на лету проксирует TLS-handshake к чужому реальному сайту (www.googletagmanager.com:443), и атакующий снаружи видит TLS-fingerprint Google. Только клиент с правильным private_key/short_id распознаёт что это «свой» сервер и переходит в туннельный режим. Атакующий без ключа — получает реальный ответ Google.

Это закрывает класс атак «active probing»: Russian/Iranian/Chinese DPI-боты не отличают Reality-эндпоинт от обычного proxy к Google.

xtls-rprx-vision flow — оптимизация для TCP-trafic. После handshake передаёт payload без дополнительного TLS-обёртывания, что устраняет известный fingerprint «TLS-in-TLS» и снижает CPU-нагрузку.

Helsinki: реальные параметры

Параметр Значение
OS Ubuntu 24.04 LTS, kernel 6.8.0-79-generic
Arch x86_64
RAM 961 MiB (узел Montana + xray поместятся)
Disk 25 GiB, ~5 GiB used
xray v26.2.6 (commit 12ee51e, go1.25.7)
nginx 1.24.0 (Ubuntu)
Reality dest SNI www.googletagmanager.com:443
Listen 0.0.0.0:443 (TCP)
Decoy 0.0.0.0:80 nginx static
Outbound freedom (direct, exit-IP = Helsinki)
Routing blackhole для bittorrent + private IPs
DNS DoH 1.1.1.1
systemd hardening User=nobody, NoNewPrivileges=true, CapabilityBoundingSet=CAP_NET_BIND_SERVICE,CAP_NET_ADMIN
sysctl net.core.default_qdisc = fq_codel (anti-bufferbloat)
ufw default deny incoming, allow 22/80/443
fail2ban SSH bantime 1h→1w incremental, maxretry=3
crowdsec community IP-reputation, auto-ban 4h

Почему www.googletagmanager.com как dest SNI

Reality dest должен:

  1. Разрешать TLS 1.3 + ECH (он разрешает оба)
  2. Иметь X25519 key exchange (Reality использует X25519)
  3. Возвращать valid TLS handshake без strict-SNI блокировок
  4. Быть глобально доступным (Google CDN)
  5. Иметь высокий traffic baseline — чтобы появление новой связи не выделялось

www.googletagmanager.com подходит по всем пунктам. Альтернативы: www.cloudflare.com, www.microsoft.com, www.icloud.com. Не использовать сайты которые могут быть заблокированы в стране клиента (например Twitter).

Почему nginx :80 с decoy

Активный пробер шлёт первым делом HTTP GET на :80. Если сервер отвечает 503 / connection refused / SSH banner — это аномалия, флаг для последующего более глубокого зондирования :443.

Decoy index.html со стандартным «It works!» от Apache/nginx — выглядит как неактивированный VPS дефолтной конфигурации. Не привлекает внимание ботов, ищущих «брошенные» VPS под web-defacement.

Почему freedom outbound, а не каскад

В первоначальной концепции (моя память) Helsinki был front для Frankfurt origin — nginx stream-proxy :443 → 89.19.208.158:443. На практике Helsinki был перестроен в самостоятельный exit-node с прямым freedom outbound: проще, быстрее (один RTT вместо двух), exit-IP финский (хорошая юрисдикция).

Каскадная схема имеет смысл когда:

  • Нужно скрыть реальный IP origin от клиента (zero-trust trust front)
  • Front в дружественной юрисдикции, origin в любой
  • Клиент компрометирован → видит только front-IP

Прямой exit-node имеет смысл когда:

  • Юрисдикция уже хорошая (Финляндия)
  • Caller хочет минимальную latency
  • Нет требования двойной анонимизации

Helsinki — второе.

Почему статический Xray User=nobody, не root

Xray должен слушать :443 (privileged port). По старой Unix-модели это требует root. Современная модель: запускать как nobody + AmbientCapabilities=CAP_NET_BIND_SERVICE. Принцип least-privilege: компрометация xray-процесса даёт nobody, а не root.

Drop-in /etc/systemd/system/xray.service.d/10-donot_touch_single_conf.conf — стандартный paranoid-блок официального xray installer. Перезаписывает ExecStart чтобы гарантировать что запускается именно /usr/local/etc/xray/config.json, а не глобальный /etc/xray/... или конфиг из current-dir.

Почему fq_codel + BBR

VPN-трафик идёт через шифрованный TCP. Default Linux qdisc (pfifo_fast) при перегрузке буфера вызывает bufferbloat — RTT прыгает с 30ms до 500ms+. fq_codel (Fair Queue + Controlled Delay) держит buffer drain time на уровне ~5ms даже под нагрузкой.

tcp_congestion_control=bbr — Google's BBR. На lossy-каналах (Wi-Fi клиента, сотовая связь) BBR даёт 2-5× throughput vs CUBIC default.

Для VPN с ~10-20 одновременными клиентами это разница «работает идеально» vs «ютуб тормозит».

Что НЕ делает этот стек

  • Не маскирует timing-correlation. Если глобальный наблюдатель видит трафик клиента → Helsinki + трафик Helsinki → target — он может корреляцией восстановить «кто куда ходил».
  • Не защищает от компрометации клиентского устройства. Если RAT на клиенте — VPN бесполезен.
  • Не log-free. xray пишет access.log + error.log. Можно отключить ("loglevel": "none") — оставлено для оператора.
  • Не двойной hop. Clean exit-IP, не Tor.

Связь с протоколом Montana

Узел Montana (singleton mode без сетевого слоя) не использует этот VPN. Узел тикает VDF локально и пишет state в /var/lib/montana/, портов наружу не открывает.

VPN и узел Montana — два независимых systemd-сервиса на одном хосте. Можно поднять оба (scripts/install-vps-full.sh), либо только один (montana-vpn/install.sh или scripts/install-vps.sh). Конфликтов по ресурсам нет — узел single-thread, xray async-IO.

После M6 (когда у узла появится сетевой слой) — узел получит свой порт (например :9501), и можно будет (опционально) пускать его за тот же Reality-эндпоинт через xray inbound на отдельном порту. Сейчас это не предусмотрено архитектурно.