SECURITY - PLATFORM: internal token and staging plan

This commit is contained in:
Codex 2026-05-12 12:49:58 +03:00
parent 9d7f8167d3
commit 95072586b9
5 changed files with 143 additions and 4 deletions

View File

@ -1,5 +1,7 @@
# Security Checklist
Staging path and runbook: `docs/STAGING_SECURITY_PLAN.md`.
## Network
- [x] Local dev compose публикует только reverse proxy port.
@ -26,6 +28,8 @@
- [ ] Admin endpoints требуют `nodedc:superadmin` или `nodedc:launcher:admin`.
- [ ] Все admin actions пишутся в audit log.
- [ ] Удаление пользователя реализовано как deactivate/disable, не hard delete.
- [x] Local runtime не отдает `storage/launcher-data.json` напрямую через public static route.
- [x] Local runtime требует user session для `/api/storage/data`.
## Plane
@ -43,13 +47,20 @@
- [ ] Access/refresh tokens не логируются.
- [ ] Session cookies имеют `secure=true` в staging/production.
- [ ] В production включены HTTPS и HSTS.
- [x] Local internal API token отделен от `PLANE_OIDC_CLIENT_SECRET`.
- [x] Launcher internal access API отклоняет запросы без token, с неверным token и со старым OIDC client secret.
- [x] Tasker internal logout API отклоняет запросы без token и со старым OIDC client secret.
## Acceptance scenarios
- [ ] Без логина Launcher отправляет в Authentik.
- [ ] Пользователь без `nodedc:taskmanager:access` не видит Task Manager в Launcher.
- [ ] Пользователь без `nodedc:taskmanager:access` получает deny на прямой `task.local.nodedc`.
- [x] Без логина Launcher отправляет в Authentik.
- [x] Пользователь без Task Manager app access не видит Task Manager в Launcher.
- [x] Пользователь без Task Manager app access получает deny в Tasker access middleware при прямом доступе.
- [ ] Пользователь с доступом открывает Task Manager.
- [ ] Старый Plane admin после OIDC видит старые workspace/tasks/comments.
- [ ] Деактивированный пользователь теряет доступ.
- [ ] Admin action появляется в audit log.
- [x] Hard-deleted/annulled user теряет stale Tasker identity link и старая unlinked-сессия отзывается middleware.
- [x] Hard-deleted/annulled user удаляется из active Tasker workspace/project members и issue assignees.
- [x] Self-host Tasker workspace invite создает pending request в Launcher, а launcher-managed workspace не принимает self-service invite request.
- [x] Admin action появляется в audit log.
- [x] Повторный accept уже принятого invite отклоняется.

View File

@ -0,0 +1,125 @@
# Staging Security Plan
Актуализировано: 2026-05-12.
Этот документ фиксирует минимальный staging path для закрытого demo release NODE.DC platform. Billing, тарифы и email automation не входят в текущий scope: доступы, approve и передача ссылок остаются ручными через Launcher/root-admin.
## Целевые домены
Для staging нужны отдельные домены от локального `.local.nodedc`:
```text
auth.staging.nodedc.example -> Authentik
launcher.staging.nodedc.example -> Launcher BFF/web
task.staging.nodedc.example -> Task Manager / Operational Core
```
Финальные DNS-имена заменяются перед deploy. Все redirect URI в Authentik должны совпадать с этими доменами и использовать `https://`.
## Публичная поверхность
Снаружи публикуется только edge reverse proxy:
- `80/tcp` — только redirect на HTTPS;
- `443/tcp` — HTTPS termination, HSTS, proxy headers;
- Postgres, Redis/Valkey, MinIO, Authentik server/worker, Launcher BFF и Tasker backend не публикуются напрямую наружу.
Reverse proxy обязан прокидывать:
- `Host`;
- `X-Forwarded-Proto`;
- `X-Forwarded-For`;
- WebSocket/HTTP upgrade для Tasker realtime/live.
## Runtime topology
Staging можно собрать compose-файлом или эквивалентным deployment unit, но правила одинаковые:
- Authentik `server` и `worker` используют отдельный Postgres volume/network;
- Authentik worker не монтирует `/var/run/docker.sock`;
- Launcher хранит control-plane snapshot только в server-side storage;
- Tasker остаётся отдельным приложением со своей БД, storage и доменной моделью;
- Launcher не читает и не пишет Plane DB напрямую;
- интеграция Launcher -> Tasker идёт через internal API с отдельным `NODEDC_INTERNAL_ACCESS_TOKEN`.
## Обязательные env
Значения с `change-me`, local token и dev secrets запрещены в staging.
```text
COOKIE_SECURE=true
COOKIE_DOMAIN=.staging.nodedc.example
NODEDC_INTERNAL_ACCESS_TOKEN=<random-32-bytes-min>
SESSION_SECRET=<random-32-bytes-min>
AUTHENTIK_SECRET_KEY=<random-32-bytes-min>
PG_PASS=<random-32-bytes-min>
LAUNCHER_OIDC_ISSUER=https://auth.staging.nodedc.example/application/o/launcher/
LAUNCHER_OIDC_REDIRECT_URI=https://launcher.staging.nodedc.example/auth/callback
LAUNCHER_OIDC_LOGGED_OUT_REDIRECT_URI=https://launcher.staging.nodedc.example/auth/logged-out
PLANE_OIDC_ISSUER=https://auth.staging.nodedc.example/application/o/task-manager/
PLANE_OIDC_REDIRECT_URI=https://task.staging.nodedc.example/auth/oidc/callback
PLANE_NODEDC_ACCESS_ENFORCEMENT=1
PLANE_NODEDC_ACCESS_ENFORCE_UNLINKED=1
PLANE_NODEDC_ACCESS_TOKEN=<same-as-NODEDC_INTERNAL_ACCESS_TOKEN>
```
OIDC client secrets для Launcher и Tasker должны быть разными. Internal API token не должен совпадать ни с одним OIDC client secret.
## Trusted proxies
Локальный default `127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,::1/128` допустим только для dev.
В staging `AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS` и аналогичные параметры Tasker должны указывать только на фактический reverse-proxy subnet или ingress CIDR. Широкие private ranges без необходимости запрещены.
## Cookies и HTTPS
Для staging:
- TLS обязателен на всех публичных доменах;
- HTTP должен делать permanent redirect на HTTPS;
- `COOKIE_SECURE=true`;
- session cookies остаются `HttpOnly`;
- `SameSite=Lax` допустим для текущего top-level OIDC flow;
- включить HSTS после проверки сертификатов и redirect URI.
## Backup перед staging
Перед первым staging deploy и перед destructive security smoke:
- dump Authentik Postgres;
- dump Tasker/Plane Postgres;
- архив Launcher `server/storage`;
- архив Tasker uploads/MinIO data;
- архив `.env`/secret references без публикации в git;
- зафиксировать image tags Launcher/Tasker/Authentik.
Минимальный restore check: поднять копию DB/storage на isolated host или namespace и проверить login + открытие Tasker.
## Secrets rotation
Rotation policy до допуска внешних пользователей:
- `NODEDC_INTERNAL_ACCESS_TOKEN` — rotate при любом подозрении на утечку и перед production;
- OIDC client secrets — rotate отдельно для Launcher и Tasker;
- `SESSION_SECRET` — rotate с принудительным logout всех пользователей;
- DB passwords — rotate через maintenance window;
- старые secrets удаляются из `.env`, shell history и runtime logs.
## Acceptance перед людьми
Минимальный staging smoke:
1. Super sudo входит в Launcher через `https://launcher...`.
2. Обычный active user видит только разрешённые сервисы.
3. User без Task Manager app access не видит Task Manager в витрине и получает deny по прямому `https://task...`.
4. Blocked/annulled user теряет Launcher и Tasker session после hard refresh.
5. Self-host workspace invite создаёт pending request в Launcher.
6. Launcher-managed workspace не принимает self-service invite request из Tasker.
7. Hard delete удаляет active WorkspaceMember/ProjectMember/IssueAssignee в Tasker.
8. Audit сохраняет admin actions, approve/reject и hard delete.
9. Повторный accept уже принятого invite отклоняется.
10. Прямые backend/internal endpoints без token возвращают `401`.

View File

@ -42,5 +42,6 @@ PLANE_OIDC_REDIRECT_URI=http://task.local.nodedc/auth/oidc/callback
# security
SESSION_SECRET=change-me-generate-with-infra-scripts-init-dev-env
NODEDC_INTERNAL_ACCESS_TOKEN=change-me-generate-with-infra-scripts-init-dev-env
COOKIE_DOMAIN=.local.nodedc
COOKIE_SECURE=false

View File

@ -58,6 +58,7 @@ ensure_env_value LAUNCHER_OIDC_CLIENT_ID nodedc-launcher
ensure_env_value PLANE_OIDC_CLIENT_ID nodedc-task-manager
ensure_env_value LAUNCHER_OIDC_CLIENT_SECRET "$(rand_hex 48)"
ensure_env_value PLANE_OIDC_CLIENT_SECRET "$(rand_hex 48)"
ensure_env_value NODEDC_INTERNAL_ACCESS_TOKEN "$(rand_hex 48)"
ensure_env_value LAUNCHER_OIDC_REDIRECT_URI http://launcher.local.nodedc/auth/callback
ensure_env_value PLANE_OIDC_REDIRECT_URI http://task.local.nodedc/auth/oidc/callback

View File

@ -56,6 +56,7 @@ PLANE_OIDC_REDIRECT_URI=http://task.local.nodedc/auth/oidc/callback
# security
SESSION_SECRET=$(rand 48)
NODEDC_INTERNAL_ACCESS_TOKEN=$(openssl rand -hex 48 | tr -d '\n')
COOKIE_DOMAIN=.local.nodedc
COOKIE_SECURE=false
EOF