Эпик: awg-dash — приложение управления VPN-шлюзом AmneziaWG с интеллектуальной маршрутизацией #15

Open
opened 2026-10-21 23:24:06 +03:00 by embeddevio · 1 comment
Owner

Цель

Превратить PoC (embeddevio/amneziawg-docker-gateway: bash + iptables, всё через CLI) в продукт: один Docker-образ = шлюз AmneziaWG + policy routing + split-tunnel + REST API + Web UI (тёмная тема) + метрики.
Источники: docs/technical-specification.md (репозиторий PoC), entrypoint.sh и Dockerfile оттуда же — референс поведения, подлежащий переписыванию на управляемый код.

Scope (in)

FR-1..FR-5 из ТЗ целиком: управление туннелем, интеллектуальная маршрутизация (GeoIP / DNS / All-VPN / Direct-only), Web UI (6 страниц), мониторинг и метрики, REST API v1 + WebSocket, Docker HEALTHCHECK, CI/CD и публикация образа в registry.

Scope (out, v1)

Мультитуннель (>1 активного .conf), multi-tenant и роли пользователей, кластеризация/HA, мобильное приложение, автоконфигурация клиентских устройств (ZeroTier-подобное), proxy-ARP-режим шлюза.


Ключевые решения (ADR, принимает TechLead)

  1. Язык backend: Python 3.12 + FastAPI (async) для control-plane. Обоснование: скорость разработки, готовые pydantic-схемы под OpenAPI-first, async-WebSocket; узкое место (компиляция/запуск awg, iptables) — не CPU-логика, а вызовы бинарей. Go оставляем там, где он уже есть: amneziawg-go + amneziawg-tools собираются в multi-stage Dockerfile (как в PoC). Полный переход на Go не даёт выигрыша в производительности маршрутизации (её делает ядро), но удорожает frontend-контракт и найм.
  2. Архитектура: модульный монолит в одном образе (2 процесса: awg-agent под root — сеть/iptables/ipset/awg; api под непривилегированным пользователем — FastAPI+REST+WS+статика UI). Обмен — unix-socket /run/awg/agent.sock + общий SQLite/файлы. Микросервисы отклонены: один хост, один шлюз, привилегии всё равно нужны только агенту; сетевые вызовы и координация добавили бы отказа без пользы.
  3. Весь сетевой слой — через reconcile-цикл: desired state (SQLite) → diff → идемпотентное применение (ipset atomic swap, iptables -C перед -A) → rollback при ошибке. Императивные скрипты PoC не переиспользуем как есть.
  4. Web-сервер: встроенный (uvicorn + отдача статики UI через FastAPI). Внешний nginx не нужен; закладываем reverse-proxy-совместимость (base-path, X-Forwarded-*).
  5. GeoIP: RIPE Stat API как primary (бесплатно, те же ~11.4k префиксов, что в PoC) + локальный кэш-снапшот на диске + абстракция GeoProvider (задел под ip2location/MaxMind). При недоступности API — работаем на кэше, но в режиме Direct-only fallback запрещён (иначе утечка трафика).
  6. DNS: встроенный dnsmasq в контейнере (ipset по доменам, dnssec off, upstream проброс) + опция «внешний DNS». Иначе DNS-based split-tunnel требует ручной настройки DNS на клиентах.
  7. Данные: SQLite (WAL) для правил/правил-исключений/аудита + /config (конфиги .conf, 0600) + /data (кэш GeoIP, история метрик). Тома обязательны, иначе конфигурация не переживёт рестарт.
  8. Безопасность UI/API: токен доступа (Bearer для API) + сессия/CSRF для UI, rate limit, маскировка PrivateKey/PresharedKey в API, логах и на странице конфигурации, bind по умолчанию на LAN-интерфейс с предупреждением.

Что в ТЗ отсутствует и добавлено в объём (обязательно к реализации)

  • Fail-closed / kill-switch: при падении handshake трафик, предназначенный для VPN, не должен утекать напрямую (сейчас PoC при неудаче загрузки GeoIP пускает всё через VPN — обратное поведение тоже надо явно задать и проверить).
  • MTU/MSS clamping (TCPMSS --clamp-mss-to-pmtu) — без этого часть сайтов «висит» на 1500 MTU.
  • DNS-leak защита: принудительный перехват 53 порта локальной подсети на dnsmasq шлюза, иначе DNS-split не работает.
  • Аутентификация API/UI, rate limit, audit log изменений конфигурации (кто/когда/what).
  • IPv6-политика: явное решение (по умолчанию — direct, если в туннеле ipv6 не настроен) и тесты.
  • Атомарность применения конфига: валидация → staging → apply → rollback (при ошибке возврат предыдущего .conf и правил).
  • Наблюдаемость: /metrics Prometheus, ротация логов, экспорт истории метрик (SQLite → retention).
  • Восстановление после рестарта хоста: автозапуск, повторная сборка ipset/iptables из desired state при старте (idempotent bootstrap).
  • Проверка совместимости: iptables-nft vs legacy, наличие модулей amneziawg/wireguard, fallback на userspace amneziawg-go.
  • CI-раннер: нужен рабочий Gitea Actions runner (или внешний) для build/test/push образа — проверить до старта Sprint 0.
  • Runbook: prerequisites хоста, статические маршруты на роутере, откат версии образа.

Definition of Done (продукт 1.0)

# Критерий Проверка
1 Образ в registry разворачивается командой из ТЗ (docker run с --network=host, /dev/net/tun, NET_ADMIN, SYS_MODULE) smoke в staging
2 FR-1..FR-5 закрыты, все подзадачи эпика в статусе Done доска + issue
3 CI green: lint + unit + integration; coverage ≥ 70%; security scan low/none CI-артефакты в PR
4 Split-tunnel корректен в 4 режимах, есть автотесты QA-отчёт, e2e зелёный
5 Handshake < 30 с, авто-восстановление при обрыве, аптайм ≥ 45 ч QA-отчёт performance/resilience
6 Секреты не утекают в UI/API/логи; API/UI защищены токеном security-чеклист
7 README + runbook + ADR в репозитории docs

Риски

Риск Влияние Митигация
Нет CI-раннера Gitea Нет автоматизации гейтов Поднять runner в Sprint 0; до этого — локальный прогон
amneziawg-go сборка в образе Блокирует весь продукт Переиспользуем проверенный Dockerfile PoC
GeoIP 11k+ префиксов: время старта Долгий bootstrap Кэш-снапшот + atomic swap, замер в Sprint 1
DNS-split на динамических CNAME Частичная неработоспособность TTL-обновление, тесты, документированный лимит
Один физический стенд (нет второго ISP) Неполное e2e netns-стенд + мок-endpoint в CI

Поглощает

Эпик #9 «панель состояния VPN-туннеля» и его подзадачи #10–#13 — входят в объём Sprint 1/2 этого эпика (статус туннеля, UI-индикатор, e2e, healthcheck). Отдельно закрывать не нужно, закроем по мере выполнения.

Порядок работ и доска

Милстоуны: Sprint 0 (фундамент) → Sprint 1 (ядро шлюза) → Sprint 2 (API+UI) → Sprint 3 (QA+релиз). Приоритеты P0 (критично для релиза) / P1 / P2. Состояние задачи выражается лейблами status/backlog → status/ready → status/in-progress → status/review → status/blocked. Владелец и оценка указаны в каждой подзадаче. Merge в main и деплой в prod — только через TechLead по гейтам (CI green, coverage ≥70%, security scan low/none, ревью владельца роли).

## Цель Превратить PoC (`embeddevio/amneziawg-docker-gateway`: bash + iptables, всё через CLI) в продукт: **один Docker-образ** = шлюз AmneziaWG + policy routing + split-tunnel + REST API + Web UI (тёмная тема) + метрики. Источники: `docs/technical-specification.md` (репозиторий PoC), `entrypoint.sh` и `Dockerfile` оттуда же — референс поведения, подлежащий переписыванию на управляемый код. ## Scope (in) FR-1..FR-5 из ТЗ целиком: управление туннелем, интеллектуальная маршрутизация (GeoIP / DNS / All-VPN / Direct-only), Web UI (6 страниц), мониторинг и метрики, REST API v1 + WebSocket, Docker HEALTHCHECK, CI/CD и публикация образа в registry. ## Scope (out, v1) Мультитуннель (>1 активного .conf), multi-tenant и роли пользователей, кластеризация/HA, мобильное приложение, автоконфигурация клиентских устройств (ZeroTier-подобное), proxy-ARP-режим шлюза. --- ## Ключевые решения (ADR, принимает TechLead) 1. **Язык backend: Python 3.12 + FastAPI (async)** для control-plane. Обоснование: скорость разработки, готовые pydantic-схемы под OpenAPI-first, async-WebSocket; узкое место (компиляция/запуск awg, iptables) — не CPU-логика, а вызовы бинарей. Go оставляем там, где он уже есть: `amneziawg-go` + `amneziawg-tools` собираются в multi-stage Dockerfile (как в PoC). Полный переход на Go не даёт выигрыша в производительности маршрутизации (её делает ядро), но удорожает frontend-контракт и найм. 2. **Архитектура: модульный монолит в одном образе** (2 процесса: `awg-agent` под root — сеть/iptables/ipset/awg; `api` под непривилегированным пользователем — FastAPI+REST+WS+статика UI). Обмен — unix-socket `/run/awg/agent.sock` + общий SQLite/файлы. Микросервисы отклонены: один хост, один шлюз, привилегии всё равно нужны только агенту; сетевые вызовы и координация добавили бы отказа без пользы. 3. **Весь сетевой слой — через reconcile-цикл**: desired state (SQLite) → diff → идемпотентное применение (ipset atomic swap, iptables `-C` перед `-A`) → rollback при ошибке. Императивные скрипты PoC не переиспользуем как есть. 4. **Web-сервер: встроенный** (uvicorn + отдача статики UI через FastAPI). Внешний nginx не нужен; закладываем reverse-proxy-совместимость (base-path, X-Forwarded-*). 5. **GeoIP: RIPE Stat API как primary** (бесплатно, те же ~11.4k префиксов, что в PoC) + локальный кэш-снапшот на диске + абстракция `GeoProvider` (задел под ip2location/MaxMind). При недоступности API — работаем на кэше, но в режиме Direct-only fallback запрещён (иначе утечка трафика). 6. **DNS: встроенный dnsmasq** в контейнере (ipset по доменам, dnssec off, upstream проброс) + опция «внешний DNS». Иначе DNS-based split-tunnel требует ручной настройки DNS на клиентах. 7. **Данные: SQLite (WAL)** для правил/правил-исключений/аудита + `/config` (конфиги .conf, 0600) + `/data` (кэш GeoIP, история метрик). Тома обязательны, иначе конфигурация не переживёт рестарт. 8. **Безопасность UI/API**: токен доступа (Bearer для API) + сессия/CSRF для UI, rate limit, маскировка `PrivateKey/PresharedKey` в API, логах и на странице конфигурации, bind по умолчанию на LAN-интерфейс с предупреждением. --- ## Что в ТЗ отсутствует и добавлено в объём (обязательно к реализации) - **Fail-closed / kill-switch**: при падении handshake трафик, предназначенный для VPN, не должен утекать напрямую (сейчас PoC при неудаче загрузки GeoIP пускает всё через VPN — обратное поведение тоже надо явно задать и проверить). - **MTU/MSS clamping** (TCPMSS --clamp-mss-to-pmtu) — без этого часть сайтов «висит» на 1500 MTU. - **DNS-leak защита**: принудительный перехват 53 порта локальной подсети на dnsmasq шлюза, иначе DNS-split не работает. - **Аутентификация API/UI**, rate limit, audit log изменений конфигурации (кто/когда/what). - **IPv6-политика**: явное решение (по умолчанию — direct, если в туннеле ipv6 не настроен) и тесты. - **Атомарность применения конфига**: валидация → staging → apply → rollback (при ошибке возврат предыдущего .conf и правил). - **Наблюдаемость**: `/metrics` Prometheus, ротация логов, экспорт истории метрик (SQLite → retention). - **Восстановление после рестарта хоста**: автозапуск, повторная сборка ipset/iptables из desired state при старте (idempotent bootstrap). - **Проверка совместимости**: iptables-nft vs legacy, наличие модулей `amneziawg`/`wireguard`, fallback на userspace `amneziawg-go`. - **CI-раннер**: нужен рабочий Gitea Actions runner (или внешний) для build/test/push образа — проверить до старта Sprint 0. - **Runbook**: prerequisites хоста, статические маршруты на роутере, откат версии образа. --- ## Definition of Done (продукт 1.0) | # | Критерий | Проверка | |---|---|---| | 1 | Образ в registry разворачивается командой из ТЗ (`docker run` с --network=host, /dev/net/tun, NET_ADMIN, SYS_MODULE) | smoke в staging | | 2 | FR-1..FR-5 закрыты, все подзадачи эпика в статусе Done | доска + issue | | 3 | CI green: lint + unit + integration; coverage ≥ 70%; security scan low/none | CI-артефакты в PR | | 4 | Split-tunnel корректен в 4 режимах, есть автотесты | QA-отчёт, e2e зелёный | | 5 | Handshake < 30 с, авто-восстановление при обрыве, аптайм ≥ 45 ч | QA-отчёт performance/resilience | | 6 | Секреты не утекают в UI/API/логи; API/UI защищены токеном | security-чеклист | | 7 | README + runbook + ADR в репозитории | docs | ## Риски | Риск | Влияние | Митигация | |---|---|---| | Нет CI-раннера Gitea | Нет автоматизации гейтов | Поднять runner в Sprint 0; до этого — локальный прогон | | `amneziawg-go` сборка в образе | Блокирует весь продукт | Переиспользуем проверенный Dockerfile PoC | | GeoIP 11k+ префиксов: время старта | Долгий bootstrap | Кэш-снапшот + atomic swap, замер в Sprint 1 | | DNS-split на динамических CNAME | Частичная неработоспособность | TTL-обновление, тесты, документированный лимит | | Один физический стенд (нет второго ISP) | Неполное e2e | netns-стенд + мок-endpoint в CI | ## Поглощает Эпик #9 «панель состояния VPN-туннеля» и его подзадачи #10–#13 — входят в объём Sprint 1/2 этого эпика (статус туннеля, UI-индикатор, e2e, healthcheck). Отдельно закрывать не нужно, закроем по мере выполнения. ## Порядок работ и доска Милстоуны: **Sprint 0** (фундамент) → **Sprint 1** (ядро шлюза) → **Sprint 2** (API+UI) → **Sprint 3** (QA+релиз). Приоритеты P0 (критично для релиза) / P1 / P2. Состояние задачи выражается лейблами `status/backlog → status/ready → status/in-progress → status/review → status/blocked`. Владелец и оценка указаны в каждой подзадаче. Merge в `main` и деплой в prod — только через TechLead по гейтам (CI green, coverage ≥70%, security scan low/none, ревью владельца роли).
embeddevio added the epic label 2026-10-21 23:24:06 +03:00
Author
Owner

Декомпозиция эпика #15 (TechLead)

Создано 31 подзадача (#16–#46), распределены по 4 милстоунам. Роль владельца, оценка и приоритет — в теле каждой задачи.

# Задача Владелец SP P Спринт
16 Каркас монорепо, ADR, линтеры devops 3 P0 0
17 CI-пайплайн с гейтами devops 5 P0 0
18 Multi-stage Dockerfile devops 5 P0 0
19 OpenAPI 3.1 + TS-клиент backend 3 P0 0
20 SQLite/миграции/audit/rollback backend 5 P0 0
21 dev/staging стенд + netns devops 3 P0 0
22 VPN Controller (.conf, up/down, статус) backend 8 P0 1
23 Watchdog + fail-closed backend 5 P0 1
24 iptables/ipset Manager (+MSS, IPv6) backend 8 P0 1
25 GeoIP RIPE: кэш/расписание/fallback backend 5 P0 1
26 DNS split-tunnel (dnsmasq) backend 8 P1 1
27 Режимы + reconcile + исключения backend 5 P0 1
28 Metrics Collector backend 5 P0 1
29 Device tracker backend 5 P2 1
30 Импорт списков с diff backend 5 P1 1
31 Каркас FastAPI + health/ready backend 3 P0 2
32 REST API v1 (все эндпоинты) backend 8 P0 2
33 WS /metrics/stream backend 3 P1 2
34 AuthN/AuthZ + секреты backend 5 P0 2
35 FE-каркас (тёмная тема) frontend 5 P0 2
36 Dashboard frontend 5 P0 2
37 /routing (DnD + diff) frontend 8 P1 2
38 /config (DnD .conf) frontend 5 P1 2
39 /devices /logs /settings frontend 8 P2 2
40 Тест-план + pytest-инфра qa 5 P0 3
41 Integration split-tunnel/GeoIP/config qa 8 P0 3
42 Playwright E2E UI qa 8 P1 3
43 Performance и resilience qa 5 P1 3
44 Security-проверки qa 5 P0 3
45 Релизный пайплайн + runbook + smoke devops 5 P1 3
46 Prometheus + Grafana devops 3 P2 3

Итого: 167 SP (Sprint 0 — 24, Sprint 1 — 54, Sprint 2 — 50, Sprint 3 — 39).

Статусы на доске

  • status/ready (можно брать в работу): #16–#21.
  • status/backlog: остальные 25 задач.
  • Зависимости указаны в поле Deps каждой задачи (например: #32 зависит от #19/#22/#27; #41 — от #27/#26/#40).

Маппинг старого эпика #9

Старое Новое
#10 REST API статуса туннеля #22 (контроллер+статус), #32 (эндпоинт)
#11 UI-панель состояния #36 (Dashboard)
#12 E2E up/down/API-down #41 (integration)
#13 healthcheck + деплой в staging #31 (/ready), #45 (staging smoke)

Правила работы

1 задача = 1 ветка feat/<N>-slug = 1 PR. Гейты мержа: CI green + coverage ≥70% + security low/none + ревью владельца роли. Merge в main и деплой в prod — только TechLead после гейтов. Первое действие: при отсутствии Gitea Actions runner — эскалация (#17) как блокер.

## Декомпозиция эпика #15 (TechLead) Создано 31 подзадача (#16–#46), распределены по 4 милстоунам. Роль владельца, оценка и приоритет — в теле каждой задачи. | # | Задача | Владелец | SP | P | Спринт | |---|---|---|---|---|---| | 16 | Каркас монорепо, ADR, линтеры | devops | 3 | P0 | 0 | | 17 | CI-пайплайн с гейтами | devops | 5 | P0 | 0 | | 18 | Multi-stage Dockerfile | devops | 5 | P0 | 0 | | 19 | OpenAPI 3.1 + TS-клиент | backend | 3 | P0 | 0 | | 20 | SQLite/миграции/audit/rollback | backend | 5 | P0 | 0 | | 21 | dev/staging стенд + netns | devops | 3 | P0 | 0 | | 22 | VPN Controller (.conf, up/down, статус) | backend | 8 | P0 | 1 | | 23 | Watchdog + fail-closed | backend | 5 | P0 | 1 | | 24 | iptables/ipset Manager (+MSS, IPv6) | backend | 8 | P0 | 1 | | 25 | GeoIP RIPE: кэш/расписание/fallback | backend | 5 | P0 | 1 | | 26 | DNS split-tunnel (dnsmasq) | backend | 8 | P1 | 1 | | 27 | Режимы + reconcile + исключения | backend | 5 | P0 | 1 | | 28 | Metrics Collector | backend | 5 | P0 | 1 | | 29 | Device tracker | backend | 5 | P2 | 1 | | 30 | Импорт списков с diff | backend | 5 | P1 | 1 | | 31 | Каркас FastAPI + health/ready | backend | 3 | P0 | 2 | | 32 | REST API v1 (все эндпоинты) | backend | 8 | P0 | 2 | | 33 | WS /metrics/stream | backend | 3 | P1 | 2 | | 34 | AuthN/AuthZ + секреты | backend | 5 | P0 | 2 | | 35 | FE-каркас (тёмная тема) | frontend | 5 | P0 | 2 | | 36 | Dashboard | frontend | 5 | P0 | 2 | | 37 | /routing (DnD + diff) | frontend | 8 | P1 | 2 | | 38 | /config (DnD .conf) | frontend | 5 | P1 | 2 | | 39 | /devices /logs /settings | frontend | 8 | P2 | 2 | | 40 | Тест-план + pytest-инфра | qa | 5 | P0 | 3 | | 41 | Integration split-tunnel/GeoIP/config | qa | 8 | P0 | 3 | | 42 | Playwright E2E UI | qa | 8 | P1 | 3 | | 43 | Performance и resilience | qa | 5 | P1 | 3 | | 44 | Security-проверки | qa | 5 | P0 | 3 | | 45 | Релизный пайплайн + runbook + smoke | devops | 5 | P1 | 3 | | 46 | Prometheus + Grafana | devops | 3 | P2 | 3 | **Итого: 167 SP** (Sprint 0 — 24, Sprint 1 — 54, Sprint 2 — 50, Sprint 3 — 39). ### Статусы на доске - `status/ready` (можно брать в работу): #16–#21. - `status/backlog`: остальные 25 задач. - Зависимости указаны в поле **Deps** каждой задачи (например: #32 зависит от #19/#22/#27; #41 — от #27/#26/#40). ### Маппинг старого эпика #9 | Старое | Новое | |---|---| | #10 REST API статуса туннеля | #22 (контроллер+статус), #32 (эндпоинт) | | #11 UI-панель состояния | #36 (Dashboard) | | #12 E2E up/down/API-down | #41 (integration) | | #13 healthcheck + деплой в staging | #31 (/ready), #45 (staging smoke) | ### Правила работы 1 задача = 1 ветка `feat/<N>-slug` = 1 PR. Гейты мержа: CI green + coverage ≥70% + security low/none + ревью владельца роли. Merge в `main` и деплой в prod — только TechLead после гейтов. Первое действие: при отсутствии Gitea Actions runner — эскалация (#17) как блокер.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: embeddevio/awg-dash#15