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

137 lines
11 KiB
Markdown
Raw Normal View 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 на отдельном порту. Сейчас это не предусмотрено архитектурно.