# Удалённые BGP-спикеры (Remnawave-style) Runbook для реплик **bird2 + evobgp-agent** на отдельных VPS. Control plane (`evobgp-all`) инициирует доставку после `module_refresh` → `deploy_apply`; реплика **не** собирает префиксы сама. ## Модель | Remnawave | EvoBGP | |-----------|--------| | Panel → Node:PORT | CP POST `https://AGENT_DOMAIN/v1/agent/sync` | | SECRET_KEY | `agent_secret` (Bearer) | | Copy compose | Web UI → карточка спикера | | Push Xray JSON | Wake-up → pull signed bundle → verify Ed25519 → apply | Подробнее: [architecture.md](architecture.md). ## Быстрый старт 1. **CP (microvps-full):** зафиксируйте `EVOBGP_BUNDLE_SEED_HEX` (32 байта hex) — стабильный ключ подписи бандлов. 2. **Web UI → Сеть → Спикеры:** создайте спикер `role=replica`, укажите **Agent domain**, **IP ноды**, **BGP source** (по умолчанию = IP ноды). 3. Сохраните **`agent_secret`** (показывается один раз) и скопируйте **docker-compose** из UI. 4. Выдайте **node API-ключ** ([access.md](access.md)) для `EVOBGP_NODE_TOKEN`. 5. `GET /v1/bundle/signing-public-key` → `EVOBGP_BUNDLE_PUBKEY_BASE64` на реплике. 6. На VPS реплики: ```bash cd deploy/compose cp .env.remote-speaker.example .env.remote-speaker cp .env.remote-speaker-tls.example .env.remote-speaker-tls # заполните переменные из UI docker compose -f docker-compose.remote-speaker.yaml \ --env-file .env.remote-speaker --env-file .env.remote-speaker-tls \ --profile production up -d ``` 7. **CP:** `EVOBGP_NODE_DISPATCH_ENABLED=1` — Panel шлёт wake-up после publish. 8. Cloudflare: `AGENT_DOMAIN` → IP VPS, **DNS only** (как Web UI в [quickstart.md](quickstart.md)). ## Compose-профили | Profile | Состав | |---------|--------| | `production` | bird2 (host) + agent + Traefik LE | | `plain` | bird2 + agent на хосте без Traefik (только lab) | | `fallback` | + `sync-bundle` polling (`scripts/sync-bundle.sh`) | Файлы: [docker-compose.remote-speaker.yaml](../deploy/compose/docker-compose.remote-speaker.yaml). ## Firewall | Порт | Кто | Зачем | |------|-----|-------| | **443** | IP CP (`PANEL_IP_WHITELIST`) | HTTPS dispatch, health, **`GET /v1/agent/bird/protocols`** (live peer sessions) | | **179** | BGP peers | Data plane | | **80** | ACME | Traefik → 443 | ## Подготовка VPS (перед `docker compose up`) `bird2` — **`network_mode: host`**. Docker **не может** задать `net.ipv4.ip_forward` в таком контейнере; включите на **хосте**: ```bash sysctl -w net.ipv4.ip_forward=1 sysctl -w net.ipv6.conf.all.forwarding=1 echo 'net.ipv4.ip_forward=1' | tee /etc/sysctl.d/99-evobgp-bird.conf echo 'net.ipv6.conf.all.forwarding=1' >> /etc/sysctl.d/99-evobgp-bird.conf sysctl --system ``` ## Безопасность (три участка) 1. **CP → реплика:** HTTPS (LE) + Traefik ipallowlist + `agent_secret`. 2. **Реплика → CP:** HTTPS + роль `node` (только bundle/latest/enroll). 3. **Конфиг:** Ed25519 `bundle.sig`, SHA-256 manifest, `bird -p`, LKG на ноде. Prod checklist: - [ ] `EVOBGP_CONTROL_PLANE_URL=https://...` - [ ] `EVOBGP_NODE_DISPATCH_ENABLED=1` на CP - [ ] `EVOBGP_BUNDLE_SEED_HEX` на CP (не менять после выдачи pubkey репликам) - [ ] Уникальные `agent_secret` и node token на спикер - [ ] Не использовать profile `plain` в prod - [ ] Не отключать verify-bundle в agent ## Per-speaker BGP source В UI: **IP ноды** (`meta_json.node_ipv4`) и **BGP source IPv4** (`bird_bgp_source_ipv4`, default = IP ноды). Pipeline накладывает overlay при `GET .../bundle/{revision_id}` — меняются `router id` и peer `local`. Tenant `/v1/settings` (`bird_bgp_source_ipv4`) — fallback для master / если у спикера не задано. ## Drift и dispatch - `published_revision_id` vs `last_applied_revision_id` — в UI и `evobgp-deploy`. - Job `deploy_apply` meta: `node_dispatch.results[]` — статус wake-up per speaker. - Canary: `POST /v1/speakers/{id}/apply` с `revision_id`. ## Troubleshooting | Симптом | Проверка | |---------|----------| | `sysctl net.ipv4.ip_forward not allowed in host network` | Уберите sysctls из compose (уже так в main); включите ip_forward на VPS (см. выше) | | `no service selected` | `--profile production` или `COMPOSE_PROFILES=production` | | Offline в UI | `GET https://AGENT_DOMAIN/v1/agent/health` с CP; LE cert; whitelist | | dispatch error | CP logs job meta; firewall 443; `agent_secret` | | verify-bundle fail | pubkey совпадает с CP seed; пересоберите pubkey после смены seed | | BGP не поднимается | bird2 `network_mode: host`; peers; MD5 BGP отдельно от HTTP sync | ## Ограничения (scale-review) - Peers **не** фильтруются по `speaker_id` — один tenant-wide peers fragment на все реплики. - Разные peer-наборы per site — отдельная итерация pipeline. - Если Panel не достучится до agent — включите profile `fallback` (polling). ## Связанные env | Переменная | Где | |------------|-----| | `EVOBGP_NODE_DISPATCH_ENABLED=1` | CP | | `EVOBGP_AGENT_SECRET` | реплика | | `EVOBGP_NODE_TOKEN` | реплика | | `EVOBGP_FIREWALL_FAILOVER_ENABLED=1` | реплика (опционально: отдавать `/v1/firewall/blocklist` при недоступности CP) | | `EVOBGP_FIREWALL_STATE_FILE` | реплика (default `/var/lib/evobgp-agent/firewall-state.json`) | | `EVOBGP_BUNDLE_PUBKEY_BASE64` | реплика | | `PANEL_IP_WHITELIST` | Traefik на реплике |