quality / commitlint (push) Skipped
quality / changes (push) Successful in 8s
quality / docker-check (push) Skipped
quality / openapi (push) Successful in 26s
quality / web (push) Successful in 1m27s
quality / go (push) Successful in 1m18s
quality / bird2 (push) Successful in 16s
CD / quality (push) Successful in 3m43s
CD / publish (push) Successful in 3m11s
- Enhanced the speaker installation documentation to clarify the use of TCP port 179 and the logging commands for monitoring BIRD and evobgp-agent. - Updated the speaker form dialog to include additional information about MikroTik connections and logging commands. - Modified the BIRD configuration to include logging to stderr for better visibility during operations. - Adjusted the Docker Compose configuration to ensure proper network settings and sysctl configurations for BGP functionality.
148 lines
9.1 KiB
Markdown
148 lines
9.1 KiB
Markdown
# Удалённые 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 → после создания реплики: docker-команды (bird2 + agent + Traefik) |
|
||
| 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) — стабильный ключ подписи бандлов. `EVOBGP_NODE_DISPATCH_ENABLED=1`.
|
||
2. Cloudflare: A/AAAA `AGENT_DOMAIN` → публичный IP VPS реплики, режим **DNS only** (серый облачко), как Web UI в [quickstart.md](quickstart.md).
|
||
3. **Web UI → Сеть → Спикеры:** создайте спикер `role=replica`. Укажите **домен агента**, **IP ноды**, **BGP source** (по умолчанию = IP ноды), **email Let's Encrypt**, **Cloudflare DNS API token** (`Zone:DNS:Edit`), **IP панели** (CIDR whitelist).
|
||
4. В диалоге «Установка на ноду» скопируйте **docker-команды** (секреты `agent_secret` и `node_token` показываются **один раз**). Репозиторий EvoBGP на ноде не нужен: команда пишет `/opt/evobgp-speaker/docker-compose.yaml` (bird2 + agent + Traefik DNS-01) и делает `docker compose up -d`.
|
||
5. Если образы из приватного реестра — на VPS заранее `docker login git.shx.one`.
|
||
6. Не делайте `docker compose down -v` на реплике без бэкапа тома `evobgp_speaker_traefik_letsencrypt` (`acme.json`).
|
||
|
||
Эталонный compose в репозитории (lab / ручной запуск): [docker-compose.remote-speaker.yaml](../deploy/compose/docker-compose.remote-speaker.yaml). Prod-установка с панели — paste из UI.
|
||
|
||
## HTTPS на ноде (DNS-01)
|
||
|
||
Сертификат **не** выписывает Control Plane и **не** Cloudflare Origin CA. Его выпускает **Traefik на самой реплике** (`evobgp-edge`), resolver `letsencrypt`, **ACME DNS-01** через Cloudflare.
|
||
|
||
| Кто | Что делает |
|
||
|-----|------------|
|
||
| Оператор | DNS only: `AGENT_DOMAIN` → IP VPS |
|
||
| Traefik на **ноде** | `dnschallenge=true`, `provider=cloudflare` |
|
||
| `CF_DNS_API_TOKEN` | В env **реплики** (вшит в команду из UI). Traefik создаёт TXT `_acme-challenge.<AGENT_DOMAIN>` |
|
||
| Let's Encrypt | Проверяет TXT, отдаёт сертификат |
|
||
| Том | `evobgp_speaker_traefik_letsencrypt` → `/letsencrypt/acme.json` |
|
||
| CP → нода | `https://AGENT_DOMAIN/v1/agent/*` + `Authorization: Bearer <agent_secret>` + Traefik `ipallowlist` (`PANEL_IP_WHITELIST`) |
|
||
|
||
Порты:
|
||
|
||
| Порт | Кто | Зачем |
|
||
|------|-----|-------|
|
||
| **443** | IP CP (`PANEL_IP_WHITELIST`) | HTTPS dispatch, health, `GET /v1/agent/bird/protocols` |
|
||
| **179** | BGP peers | Data plane — Docker `ports: 179:179/tcp`, как на панели |
|
||
| **80** | любой | редирект HTTP → HTTPS (не HTTP-01 ACME) |
|
||
|
||
В панели хостера / security group откройте **TCP 179** (скрипт compose это не делает). Overlay (`bird_bgp_source_ipv4` / `node_ipv4`) задаёт `router id`; host-сеть bird2 не используется.
|
||
|
||
DNS-01 ходит **исходящим** к Cloudflare API и Let's Encrypt; inbound 80 для выпуска сертификата не нужен. Agent слушает `:8443` только во внутренней docker-сети; снаружи — Traefik 443.
|
||
|
||
Токен Cloudflare для панели (`evobgp-edge` на CP) в процесс API **не проброшен** — для реплики его задают в форме создания.
|
||
|
||
Profile `plain` в файле репозитория — только lab без Traefik.
|
||
|
||
## Compose-профили (файл в репозитории)
|
||
|
||
| Profile | Состав |
|
||
|---------|--------|
|
||
| `production` | bird2 (`speaker-net`, `179:179`) + agent + Traefik LE |
|
||
| `plain` | bird2 + agent без Traefik (lab; agent на хосте) |
|
||
| `fallback` | + `sync-bundle` polling (`scripts/sync-bundle.sh`) |
|
||
|
||
Команда из UI — самодостаточный yaml **без profiles** (эквивалент production).
|
||
|
||
## Подготовка VPS
|
||
|
||
`bird2` в docker-сети с `ports: 179:179/tcp` и `sysctls` ip_forward (как панель). Команда из UI дополнительно включает sysctl на хосте:
|
||
|
||
```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
|
||
```
|
||
|
||
## Логи на реплике
|
||
|
||
BIRD пишет в stderr (`log stderr all`), agent — в stdout. На VPS:
|
||
|
||
```bash
|
||
cd /opt/evobgp-speaker
|
||
docker compose logs -f bird2
|
||
docker compose logs -f evobgp-agent
|
||
```
|
||
|
||
До первого apply бандла с `protocol bgp` порт 179 может быть CLOSED (нет listener). После sync в логах agent: `sync start` / `sync ok` / `sync failed`.
|
||
|
||
## Безопасность (три участка)
|
||
|
||
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://...` (в команде из UI)
|
||
- [ ] `EVOBGP_NODE_DISPATCH_ENABLED=1` на CP
|
||
- [ ] `EVOBGP_BUNDLE_SEED_HEX` на CP (не менять после выдачи pubkey репликам)
|
||
- [ ] Уникальные `agent_secret` и node token на спикер
|
||
- [ ] Не использовать profile `plain` в prod
|
||
- [ ] Не отключать verify-bundle в agent
|
||
- [ ] Не `docker compose down -v` без бэкапа `acme.json`
|
||
|
||
## 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
|
||
|
||
| Симптом | Проверка |
|
||
|---------|----------|
|
||
| `CHANGE_ME_*` в yaml | В форме не заполнены email LE / CF token / IP панели / домен |
|
||
| Traefik отдаёт дефолтный сертификат | DNS only; token `Zone:DNS:Edit`; логи `evobgp-edge`; том acme.json |
|
||
| 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 не поднимается / сканер CLOSED | `179:179` в compose; SG хостера; `docker compose logs bird2`; пир MikroTik на IP ноды; бандл применён (`sync ok`) |
|
||
|
||
## Ограничения (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` | реплика (из UI, один раз) |
|
||
| `EVOBGP_NODE_TOKEN` | реплика (API-ключ role=node, из UI) |
|
||
| `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` | реплика (в команде из UI) |
|
||
| `PANEL_IP_WHITELIST` | Traefik на реплике |
|
||
| `CF_DNS_API_TOKEN` | Traefik на реплике |
|
||
| `LETSENCRYPT_EMAIL` | Traefik на реплике |
|