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