# 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 baseline добавлен как пример:

```text
platform/infra/docker-compose.staging.example.yml
platform/infra/reverse-proxy/Caddyfile.staging
platform/infra/.env.staging.example
NODEDC_TASKMANAGER/plane-app/plane.env.staging.example
```

Запускать его нужно только после копирования `.env.staging.example` в ignored `.env.staging` и замены всех placeholder secrets:

```bash
cd /Users/dcconstructions/Downloads/mnt/NODEDC/platform/infra
./scripts/check-staging-env.sh .env.staging
docker compose --env-file .env.staging -f docker-compose.staging.example.yml config
docker compose --env-file .env.staging -f docker-compose.staging.example.yml up -d
```

Для Tasker staging env:

```bash
cd /Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER
./scripts/check-tasker-staging-env.sh plane-app/plane.env.staging
docker compose --env-file plane-app/plane.env.staging -f plane-app/docker-compose.yaml config
```

Можно проверить example compose без реальных secret так:

```bash
NODEDC_STAGING_ENV_FILE=.env.staging.example \
  docker compose --env-file .env.staging.example -f docker-compose.staging.example.yml config
```

Runtime правила:

- 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`.
