NODEDC_PLATFORM/docs/DEVOPS_SECURITY_HANDOFF.md

276 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# DevOps Security Handoff
Актуализировано: 2026-05-12.
Документ фиксирует границу передачи NODE.DC platform DevOps-инженеру. Локальная разработка закрывает код, конфиги, preflight и smoke-сценарии, которые можно честно проверить без реального staging host. DevOps закрывает серверные домены, TLS, secrets, firewall и финальную staging acceptance.
## Что уже подготовлено локально
### Platform / Authentik / reverse proxy
Репозиторий:
```text
/Users/dcconstructions/Downloads/mnt/NODEDC/platform
```
Готовые артефакты:
```text
docs/STAGING_SECURITY_PLAN.md
docs/SECURITY_CHECKLIST.md
infra/docker-compose.staging.example.yml
infra/reverse-proxy/Caddyfile.staging
infra/.env.staging.example
infra/scripts/check-staging-env.sh
```
Локально проверено:
```bash
cd platform/infra
NODEDC_STAGING_ENV_FILE=.env.staging.example docker compose --env-file .env.staging.example -f docker-compose.staging.example.yml config
docker run --rm --env-file .env.staging.example -v "$PWD/reverse-proxy/Caddyfile.staging:/etc/caddy/Caddyfile:ro" caddy:2-alpine caddy validate --config /etc/caddy/Caddyfile
./scripts/check-staging-env.sh .env.staging.example
```
Ожидаемый результат: compose и Caddyfile валидны; preflight на `.env.staging.example` падает, потому что example содержит placeholder secrets.
### Launcher / control plane
Репозиторий:
```text
/Users/dcconstructions/Downloads/mnt/data/nodedc_launcher
```
Подготовлено:
- control-plane snapshot перенесён из public static в server-only storage;
- `/storage/launcher-data.json` закрыт;
- `/api/storage/data` и `/api/storage/upload` требуют session;
- `/api/apps` отдаёт каталог сервисов с флагами доступа; карточки сервисов видны всем authenticated users, но launch разрешён только при app access;
- hard delete вызывает Tasker cleanup: sessions, identity links, workspace/project memberships, issue assignees;
- internal API token отделён от OIDC client secret;
- повторный accept уже принятого invite отклоняется.
Локально проверено:
```bash
cd nodedc_launcher
node --check server/dev-server.mjs
npm run build
npm test
curl -i http://launcher.local.nodedc/api/me
curl -i http://launcher.local.nodedc/api/apps
curl -i http://launcher.local.nodedc/api/services/task-manager/launch
```
Ожидаемый результат без session: `401`.
### Task Manager / Operational Core
Репозиторий:
```text
/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER
```
Готовые артефакты:
```text
plane-app/plane.env.staging.example
scripts/check-tasker-staging-env.sh
```
Подготовлено:
- `PLANE_NODEDC_ACCESS_TOKEN` больше не fallback-ится на `PLANE_OIDC_CLIENT_SECRET`;
- unlinked old sessions могут отзываться через `PLANE_NODEDC_ACCESS_ENFORCE_UNLINKED=1`;
- internal logout умеет чистить ExternalIdentityLink, WorkspaceMember, ProjectMember, IssueAssignee;
- self-host workspace invite снова создаёт pending request в Launcher;
- launcher-managed workspace отклоняет self-service invite request;
- Tasker proxy получает `TRUSTED_PROXIES` из env.
Локально проверено:
```bash
cd NODEDC_TASKMANAGER
docker compose --env-file plane-app/plane.env.staging.example -f plane-app/docker-compose.yaml config
./scripts/check-tasker-staging-env.sh plane-app/plane.env.staging.example
```
Ожидаемый результат: compose валиден; preflight на example падает из-за placeholder secrets.
## Что DevOps должен сделать на сервере
## Последний локальный smoke перед передачей
Выполнено 2026-05-12 после rebuild `nodedc/plane-backend:local` и `nodedc/plane-frontend:ru`, затем `./setup.sh stop && ./setup.sh start`.
Контейнеры Tasker:
```text
admin, api, beat-worker, live, plane-db, plane-minio, plane-mq, plane-redis, proxy, space, web, worker — Up
web/admin/space — healthy
```
HTTP smoke:
```text
http://auth.local.nodedc/ -> 302
http://launcher.local.nodedc/healthz -> 200
http://task.local.nodedc/ -> 200
http://localhost:8090/ -> 200
```
Unauth negative paths:
```text
GET http://launcher.local.nodedc/api/me -> 401
GET http://launcher.local.nodedc/api/apps -> 401
GET http://launcher.local.nodedc/api/services/task-manager/launch -> 401
POST http://launcher.local.nodedc/api/internal/access/check without token -> 401
POST http://task.local.nodedc/api/internal/nodedc/logout/ without token -> 401
```
Этот smoke закрывает локальную runtime-ready часть. Он не заменяет DevOps staging smoke на реальных HTTPS-доменах.
### 1. Подготовить DNS и host
Выбрать реальные домены:
```text
auth.<staging-domain>
launcher.<staging-domain>
task.<staging-domain>
```
Все DNS-записи должны указывать на staging host или ingress. Порты `80/tcp` и `443/tcp` должны быть доступны снаружи для TLS/ACME.
### 2. Создать реальные env-файлы
Platform:
```bash
cd platform/infra
cp .env.staging.example .env.staging
```
Tasker:
```bash
cd NODEDC_TASKMANAGER
cp plane-app/plane.env.staging.example plane-app/plane.env.staging
```
Заменить все `replace-with-*` на реальные значения. Нельзя использовать local/dev secrets, `change-me`, `local-dev`, `.local.nodedc`, `localhost`.
Обязательное правило:
- `NODEDC_INTERNAL_ACCESS_TOKEN` должен совпадать с `PLANE_NODEDC_ACCESS_TOKEN`;
- `NODEDC_INTERNAL_ACCESS_TOKEN` не должен совпадать ни с одним OIDC client secret;
- `LAUNCHER_OIDC_CLIENT_SECRET` и `PLANE_OIDC_CLIENT_SECRET` должны быть разными;
- `COOKIE_SECURE=true`;
- все public/OIDC/internal URLs должны быть `https://`.
### 3. Ограничить proxy trust
В staging нельзя оставлять broad ranges:
```text
0.0.0.0/0
::/0
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
127.0.0.0/8
```
Нужно указать только фактический subnet reverse proxy / ingress:
```text
AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS=<edge-or-ingress-cidr>
TRUSTED_PROXIES=<edge-or-ingress-cidr>
```
### 4. Прогнать preflight
Platform:
```bash
cd platform/infra
./scripts/check-staging-env.sh .env.staging
docker compose --env-file .env.staging -f docker-compose.staging.example.yml config
```
Tasker:
```bash
cd 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
```
Preflight обязан пройти на реальных env-файлах. Если падает — staging не запускать.
### 5. Поднять runtime
Platform:
```bash
cd platform/infra
docker compose --env-file .env.staging -f docker-compose.staging.example.yml up -d
```
Tasker команда зависит от выбранной topology. Если используется текущий Plane runtime:
```bash
cd NODEDC_TASKMANAGER
docker compose --env-file plane-app/plane.env.staging -f plane-app/docker-compose.yaml up -d
```
Если Tasker стоит за platform reverse proxy, наружу публикуется только platform edge. Postgres, Redis/Valkey, MinIO, RabbitMQ, Authentik server/worker, Launcher BFF и Tasker API не публикуются напрямую наружу.
### 6. Bootstrap Authentik
Создать/обновить:
- отдельное Application/Provider для Launcher;
- отдельное Application/Provider для Tasker;
- отдельные OIDC client secrets;
- группы `nodedc:superadmin`, `nodedc:launcher:admin`, `nodedc:launcher:user`, `nodedc:taskmanager:admin`, `nodedc:taskmanager:user`;
- access policies для каждого приложения;
- redirect/logout URI только на staging HTTPS domains.
### 7. Проверить финальный staging smoke
Минимальный smoke:
1. `https://auth...`, `https://launcher...`, `https://task...` открываются по HTTPS.
2. HTTP делает redirect на HTTPS.
3. Ответы содержат HSTS.
4. Cookies выставляются как `Secure` и `HttpOnly`.
5. Без login Launcher ведёт в Authentik.
6. Active user видит все карточки сервисов, но открыть может только разрешённые сервисы.
7. User без Task Manager app access видит карточку Task Manager в Launcher, не может открыть сервис и получает deny по прямому `https://task...`.
8. Blocked/annulled user теряет Launcher и Tasker session после hard refresh.
9. Self-host workspace invite создаёт pending request в Launcher.
10. Launcher-managed workspace не принимает self-service invite request из Tasker.
11. Hard delete удаляет active Tasker WorkspaceMember/ProjectMember/IssueAssignee.
12. Повторный accept уже принятого invite отклоняется.
13. Internal endpoints без token дают `401`.
14. Audit содержит admin actions: approve/reject/access change/hard delete.
15. Снаружи не доступны Postgres, Redis/Valkey, MinIO, RabbitMQ, Authentik server/worker, Launcher BFF и Tasker API-порты.
## Что не входит в текущую передачу
- billing;
- тарифы;
- email automation;
- production HA/backup automation;
- production monitoring/SIEM;
- public self-service без ручного approve.
Эти темы не являются блокерами текущего закрытого demo release.