From 60e70bf86d39af2c6c62844d8ac56c5cc1ae99c4 Mon Sep 17 00:00:00 2001 From: Codex Date: Tue, 12 May 2026 13:19:34 +0300 Subject: [PATCH] DOCS - PLATFORM: add DevOps security handoff --- docs/DEVOPS_SECURITY_HANDOFF.md | 275 ++++++++++++++++++++++++++++++++ docs/SECURITY_CHECKLIST.md | 1 + 2 files changed, 276 insertions(+) create mode 100644 docs/DEVOPS_SECURITY_HANDOFF.md diff --git a/docs/DEVOPS_SECURITY_HANDOFF.md b/docs/DEVOPS_SECURITY_HANDOFF.md new file mode 100644 index 0000000..f001ac4 --- /dev/null +++ b/docs/DEVOPS_SECURITY_HANDOFF.md @@ -0,0 +1,275 @@ +# 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` отдаёт только приложения с 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. +launcher. +task. +``` + +Все 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= +TRUSTED_PROXIES= +``` + +### 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. diff --git a/docs/SECURITY_CHECKLIST.md b/docs/SECURITY_CHECKLIST.md index 75d5239..6e02621 100644 --- a/docs/SECURITY_CHECKLIST.md +++ b/docs/SECURITY_CHECKLIST.md @@ -1,6 +1,7 @@ # Security Checklist Staging path and runbook: `docs/STAGING_SECURITY_PLAN.md`. +DevOps handoff: `docs/DEVOPS_SECURITY_HANDOFF.md`. ## Network