# Предоставление доступа Как выдавать доступ к control plane API, веб-клиентам и репликам BIRD (`evobgp-node`). Секреты храните в менеджере секретов, переменных окружения оркестратора или зашифрованных файлах — не коммитьте реальные ключи в Git. ## Portal SSO (JWT) Единый вход через **auth-portal** (app id `bgp`). См. [integrate-evobgp.md](https://git.shx.one/denozord/auth-portal/src/branch/main/docs/integrate-evobgp.md) в репозитории auth-portal. | Переменная | Назначение | |------------|------------| | `AUTH_REQUIRED` / `EVOBGP_AUTH_REQUIRED` | Включить проверку portal JWT для UI | | `AUTH_JWT_SECRET` / `EVOBGP_AUTH_JWT_SECRET` | Тот же секрет, что `JWT_SECRET` портала (HS256) | | `AUTH_ISSUER` | Issuer JWT (как на портале) | | `AUTH_PORTAL_URL` | URL портала (также `GET /v1/auth/config`) | | `AUTH_AUDIT_INGEST_SECRET` / `EVOBGP_AUTH_AUDIT_INGEST_SECRET` | Shared secret для push CRUD audit в auth-portal (`POST /api/v1/ingest/audit`, `source_app=bgp`) | | `EVOBGP_PORTAL_TENANT_ID` | Fallback tenant для portal JWT, если в токене нет `bgp_tenant_id` / `tenants.bgp` | Источник tenant (по приоритету): 1. JWT claim `tenants.bgp` или `bgp_tenant_id` (задаётся в auth-portal → **Админ → Приложения** → поле «EvoBGP tenant ID») 2. Env `EVOBGP_PORTAL_TENANT_ID` Compose: переменные `AUTH_*` / `EVOBGP_PORTAL_TENANT_ID` должны быть в `environment:` сервиса **`evobgp-all`** (см. `deploy/compose/stack.microvps-full.yaml`). Просто положить их в `.env` без проброса в контейнер недостаточно. `VITE_AUTH_*` в runtime `.env` **не** меняют уже собранный `evobgp-web` образ. UI берёт режим из `GET /v1/auth/config` (`required` ← `AUTH_REQUIRED`, `portal_url` ← `AUTH_PORTAL_URL`). Проверка после рестарта: ```bash curl -sS https://bgp.shnt.top/v1/auth/config # {"required":true,"portal_url":"https://auth.shnt.top"} ``` Права — строки `bgp:
:` из каталога портала (dashboard, modules, lookup, network, …). Apply/rollback требуют `bgp:operations:admin`. **Ownership:** modules, peers, firewall clients/rules с `created_by_user_id` видны создателю и portal `is_admin` (API keys — весь tenant). UI: `VITE_AUTH_ENABLED`, `VITE_AUTH_PORTAL_URL`. App Switcher: `CURRENT_APP_ID=bgp`, конфиг с `GET {portal}/api/v1/app-switcher`. ## API-ключи (`EVOBGP_API_KEYS`) Формат переменной окружения: список записей через **запятую** без пробелов внутри логики парсера (пробелы вокруг записей допускаются при обрезке). Каждая запись: ```text || ``` - **token** — произвольная строка, передаётся клиентом как `Authorization: Bearer `. - **tenant_id** — идентификатор арендатора; все операции store привязываются к этому tenant для данного ключа. - **role** — одна из ролей ниже (регистр для проверки уровня в коде приводится к lower case). Пример для двух ключей одного tenant: ```text opkey|01ARZ3NDEKTSV4RRFFQ69G5FAV|operator,nodekey|01ARZ3NDEKTSV4RRFFQ69G5FAV|node ``` При включённом демо-сиде сервер при старте может вывести в лог готовую подсказку с реальным `tenant_id` из БД — см. лог `evobgp-api` / `evobgp-all`. Ключи из `EVOBGP_API_KEYS` загружаются при старте и **дополняют** ключи из таблицы `api_key` в БД (break-glass / bootstrap). После первого operator-ключа можно создавать остальные через API или веб-настройки. ### Управление через API и UI При подключённой БД управлять ключами может: - API-ключ с ролью **`operator`**, или - portal JWT с **`is_admin`** / правом **`bgp:access:admin`** (админ auth-portal). Эндпоинты: `GET|POST /v1/api-keys`, `GET|PATCH|DELETE /v1/api-keys/{id}`, `POST /v1/api-keys/{id}/rotate` — см. OpenAPI, тег **API keys**. В веб-панели: **Права доступа** (`/access`) → блок «API-ключи». Токен для браузера (API-key gate) — в **Настройки** (`/settings`). Полный токен возвращается **один раз** в ответе `201` (создание) и `200` (ротация). В списках — только `prefix` (первые 8 символов). В БД хранится SHA-256 токена, не plaintext. `GET /v1/auth/session` — `tenant_id`, `kind` (`apikey`|`jwt`), для API-ключа — `role`; для JWT — `user_id`, `email`, `permissions`, `is_admin`. ### Роли | Роль | Уровень | Назначение | |------|---------|------------| | `viewer` | 1 | Только чтение (списки, GET сущностей) там, где это разрешено политикой обработчиков. | | `editor` | 2 | Чтение + создание/изменение CRUD (модули, записи, peers и т.д.), без опасных операций уровня оператора. | | `operator` | 3 | Полный операторский доступ: apply, rollback, настройки, отмена задач и т.п. (как задано в handlers). | | `node` | отдельная | Только API для реплики: latest revision, скачивание бандла, enroll. Роль **`node` запрещена** для обычного CRUD — ответ `403 Forbidden`. | | `firewall` | отдельная | Только data-plane firewall-клиента: `GET /v1/firewall/blocklist`, `POST /v1/firewall/apply-report`, `POST /v1/firewall/heartbeat`. Токен в таблице `firewall_client`, не в `api_key`. См. [firewall.md](firewall.md). | Обратное ограничение: для эндпоинтов ноды требуется именно роль **`node`**; остальные роли получают отказ. ### Токен `dev` (локальная разработка) Если в store доступен демо-tenant (`DemoIDs`, обычно `EVOBGP_SEED_DEMO` не равен `0`), заголовок **`Authorization: Bearer dev`** даёт роль **`operator`** для этого tenant. **Не зависит** от `EVOBGP_DEV_INSECURE`. Без demo-tenant токен `dev` может быть задан в `EVOBGP_API_KEYS` (break-glass). **Запрещено** в продакшене: не оставляйте demo-seed с известным токеном `dev` на боевых данных. Переменная `EVOBGP_DEV_INSECURE` в текущей версии **не влияет** на аутентификацию (оставлена в compose для совместимости; не включайте в production — см. SEC-02 в инженерных правилах). ### PostgreSQL monitoring и maintenance (control plane) При `EVOBGP_DATABASE_URL` (не memory backend): | Операция | Минимальная роль | |----------|------------------| | `GET /v1/monitoring/postgres/*`, `GET /v1/monitoring/correlation` | viewer | | `POST /v1/postgres/vacuum`, `vacuum-analyze`, `analyze`, `reindex`, `cleanup` | **operator** (async job, rate limit 60s на kind) | | `GET /v1/postgres/maintenance/logs` | viewer | Метрики **instance-level** (не per-tenant). CLI: `evobgp-api db …` / `evobgp-all db …`. ### Синхронные «тяжёлые» GET (control plane) - `POST /v1/modules/{module_id}/cdn-sources/preview` — загрузка CDN в том же HTTP-запросе (лимит тела ~8 MiB, см. OpenAPI). - `GET /v1/bird/status` (если маршрут включён в деплое) — опрос локального `birdc`, таймаут сервера ~12 с. ### Детерминированный ключ подписи бандлов (тесты) `EVOBGP_BUNDLE_SEED_HEX` — необязательная hex-строка для инициализации ключа подписи бандлов (удобно в CI и локальных прогонах). В проде обычно используется случайная генерация при старте, если seed не задан. ## Публичный ключ бандла для нод При старте API в лог печатается строка **bundle signing public key (base64)**. Альтернатива для operator: **`GET /v1/bundle/signing-public-key`** → поле `public_key_base64` для `EVOBGP_BUNDLE_PUBKEY_BASE64` на реплике. Использование в `evobgp-node` / agent: ```text evobgp-node verify-bundle -f bundle.tar.gz -pubkey-base64 "<из_лога_API>" evobgp-node apply-bundle -f bundle.tar.gz -extract-dir /path/to/dir -pubkey-base64 "<...>" ``` Команда `pull-bundle` использует **тот же Bearer-токен**, что зарегистрирован с ролью **`node`**: ```text evobgp-node pull-bundle -base-url http://control.example:8080 -token "" -speaker-id "" ``` ## Panel→Node dispatch (удалённые спикеры) На control plane (prod): ```text EVOBGP_NODE_DISPATCH_ENABLED=1 EVOBGP_BUNDLE_SEED_HEX=<32 bytes hex, стабильный> ``` После `deploy_apply` CP шлёт `POST https://AGENT_DOMAIN/v1/agent/sync` с `Authorization: Bearer `. На реплике — `EVOBGP_AGENT_SECRET`, Traefik `PANEL_IP_WHITELIST`. Подробнее: [remote-speakers.md](remote-speakers.md). ## Runtime log-файлы (`EVOBGP_RUNTIME_LOGS_DIR`) Файловые логи Docker-сервисов (sidecar `stack-runtime-logs` в compose) читаются API **только** в процессе **`evobgp-all`**, когда заданы обе переменные: ```text EVOBGP_SERVICE=evobgp-all EVOBGP_RUNTIME_LOGS_DIR=/opt/evobgp/runtime-logs ``` В dev-профиле compose каталог на хосте обычно `./runtime-logs`, в контейнере — mount на `/opt/evobgp/runtime-logs`. Если каталог не задан или роль процесса не `evobgp-all`, FS-эндпоинты (`/files`, `/auto-*`) отвечают **503** (`runtime_logs_unavailable`). `GET /v1/runtime-logs/cleanup-audit` доступен без volume. Очистка файлов — роль **operator+**; операции пишутся в `runtime_log_cleanup_audit`. Автоочистка настраивается в tenant settings (`runtime_logs_auto_enabled`, `runtime_logs_max_file_mb`, `runtime_logs_auto_schedule`, `runtime_logs_auto_mode`); scheduler — только в `evobgp-all`. Опционально: `EVOBGP_RUNTIME_LOGS_POLICY_TENANT` — tenant, чьи settings читает scheduler (иначе первый tenant с включённой автоочисткой). Retention строк audit: пресет maintenance policy `runtime_log_cleanup_audit` (90d) в Monitoring → PostgreSQL → Политики. ## CORS для веб-интерфейса Браузерные запросы с другого origin требуют заголовков CORS на API. Задайте список разрешённых origin через **`EVOBGP_CORS_ORIGINS`** (через запятую), например: ```text http://localhost:5173,http://127.0.0.1:5173,https://ui.example.com ``` Разрешённые заголовки включают `Authorization`, `Content-Type`, `Idempotency-Key`, `Accept`, `X-Tenant-Id` (см. `internal/httpapi/cors.go`). ## Pipeline: TTL и внешние вызовы | Переменная | Назначение | |------------|------------| | `EVOBGP_ASN_CACHE_TTL_SEC` | TTL кэша объявленных префиксов RIPEstat (default 1800) | | `EVOBGP_ASN_HOLDER_TTL_SEC` | TTL имени holder AS (default 7 суток) | | `EVOBGP_CDN_DNS_CACHE_TTL_SEC` | TTL DNS при проверке CDN URL (default 300) | | `EVOBGP_DOMAIN_CACHE_TTL_SEC` | TTL кэша DoH A/AAAA (default 300) | | `EVOBGP_CDN_PARTIAL_OK` | Сохранять prior-строки skipped CDN-источников при частичном сбое | ## Заголовок `X-Tenant-Id` (решение: не реализован) **Решение (done):** заголовок **`X-Tenant-Id` не переключает tenant** в handlers и **не планируется** без отдельного ADR на супер-роли. В [openapi.yaml](openapi.yaml) он помечен как reserved / unimplemented; tenant всегда из API-ключа, portal JWT claim или `EVOBGP_PORTAL_TENANT_ID`. Клиенты **не должны** полагаться на `X-Tenant-Id`. ## Доступ к репозиторию и CI Чтобы коллега мог читать код, открывать PR и видеть результаты Gitea Actions: - Выдайте права на репозиторий в вашей forge (Gitea/GitHub/GitLab): как минимум **Read** для просмотра, **Write** для веток и PR. - Требования к runner и описание workflow — [.gitea/README.md](../.gitea/README.md). Секреты для публикации образов или внешних сервисов в базовом CI не обязательны; добавляйте их отдельно под свои workflow. ## Краткая матрица (ориентир) | Действие | viewer | editor | operator | node | portal admin / `bgp:access:admin` | |----------|--------|--------|----------|------|-----------------------------------| | GET модули, ревизии, peers, speakers | да | да | да | нет | по permissions | | POST/PATCH/DELETE CRUD сущностей | нет | да | да | нет | по permissions | | apply, rollback, PATCH settings | нет | нет | да | нет | `bgp:operations:admin` | | Управление API-ключами (`/v1/api-keys`) | нет | нет | да | нет | да | | bundle, latest revision, enroll | нет | нет | нет | да | нет | Точные проверки по каждому маршруту — в коде `internal/httpapi` и в схеме безопасности операций в OpenAPI. ## Связанные документы - [api.md](api.md) — список групп эндпоинтов. - [quickstart.md](quickstart.md) — запуск с примером ключей.