DOCS - PLATFORM: add DevOps security handoff
This commit is contained in:
parent
ffd29c4f0b
commit
60e70bf86d
|
|
@ -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.<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.
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# Security Checklist
|
# Security Checklist
|
||||||
|
|
||||||
Staging path and runbook: `docs/STAGING_SECURITY_PLAN.md`.
|
Staging path and runbook: `docs/STAGING_SECURITY_PLAN.md`.
|
||||||
|
DevOps handoff: `docs/DEVOPS_SECURITY_HANDOFF.md`.
|
||||||
|
|
||||||
## Network
|
## Network
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue