АРХ - NODEDC PLATFORM: локальный Authentik и reverse proxy

This commit is contained in:
Codex 2026-05-04 10:17:04 +03:00
parent 0f89c4d126
commit 55db22b8d2
9 changed files with 327 additions and 13 deletions

View File

@ -52,6 +52,17 @@ task.local.nodedc -> Plane proxy/runtime
Все внешние запросы идут через reverse proxy. Все внешние запросы идут через reverse proxy.
На этом этапе reverse proxy реализуется через Caddy, а Authentik запускается за ним без прямой публикации host ports.
Текущие приложения подключаются как внешние upstream:
```text
launcher.local.nodedc -> host.docker.internal:5173
task.local.nodedc -> host.docker.internal:8090
```
Это сохраняет Launcher и Plane в их текущих репозиториях и runtime-папках.
## Environment ## Environment
Базовые переменные должны жить в: Базовые переменные должны жить в:
@ -66,11 +77,39 @@ platform/infra/.env
platform/infra/.env.example platform/infra/.env.example
``` ```
Для генерации локальных secrets:
```bash
cd /Users/dcconstructions/Downloads/mnt/NODEDC/platform
./infra/scripts/init-dev-env.sh
```
Скрипт создает `platform/infra/.env`, выставляет права `600` и не перезаписывает существующий файл.
## Start commands
```bash
cd /Users/dcconstructions/Downloads/mnt/NODEDC/platform
docker compose --env-file infra/.env -f infra/docker-compose.dev.yml up -d
```
Проверка:
```bash
docker compose --env-file infra/.env -f infra/docker-compose.dev.yml ps
curl -I -H 'Host: auth.local.nodedc' http://127.0.0.1/
curl -I -H 'Host: task.local.nodedc' http://127.0.0.1/
```
## Authentik version note
Официальный compose Authentik для текущей ветки 2026.2 использует PostgreSQL, `server` и `worker`. Redis, указанный в раннем ТЗ как ожидаемый сервис, в актуальном официальном compose не используется. Если позже будет выбран старый pinned Authentik или отдельная HA-схема, Redis надо вернуть отдельной задачей.
## Implementation order ## Implementation order
1. Согласовать локальные domain/port bindings. 1. Согласовать локальные domain/port bindings.
2. Добавить reverse proxy config. 2. Добавить reverse proxy config.
3. Поднять Authentik server, worker, Postgres и Redis. 3. Поднять Authentik server, worker и Postgres.
4. Прокинуть Authentik за proxy с корректными headers. 4. Прокинуть Authentik за proxy с корректными headers.
5. Подключить текущий Launcher как внешний сервис или через compose service. 5. Подключить текущий Launcher как внешний сервис или через compose service.
6. Подключить текущий Plane runtime как внешний service target. 6. Подключить текущий Plane runtime как внешний service target.

View File

@ -2,13 +2,13 @@
## Network ## Network
- [ ] Наружу опубликованы только reverse proxy ports. - [x] Local dev compose публикует только reverse proxy port.
- [ ] Postgres не опубликован наружу в staging/production. - [ ] Postgres не опубликован наружу в staging/production.
- [ ] Redis не опубликован наружу в staging/production. - [ ] Redis не опубликован наружу в staging/production.
- [ ] MinIO/storage не опубликован наружу в staging/production. - [ ] MinIO/storage не опубликован наружу в staging/production.
- [ ] Внутренние API не доступны напрямую извне. - [ ] Внутренние API не доступны напрямую извне.
- [ ] Authentik получает корректные `X-Forwarded-Proto`, `X-Forwarded-For`, `Host`. - [x] Authentik получает `X-Forwarded-Proto`, `X-Forwarded-For`, `Host` через Caddy.
- [ ] WebSocket headers настроены для Authentik/Plane/live. - [x] Caddy reverse proxy сохраняет HTTP/1.1/WebSocket upgrade behavior для upstream.
## Authentik ## Authentik
@ -17,6 +17,7 @@
- [ ] Для каждого приложения задана отдельная access policy. - [ ] Для каждого приложения задана отдельная access policy.
- [ ] Группы app access заведены отдельно от app-local ролей. - [ ] Группы app access заведены отдельно от app-local ролей.
- [ ] MFA/enrollment policy вынесены в отдельный этап. - [ ] MFA/enrollment policy вынесены в отдельный этап.
- [x] Authentik local compose не публикует server/worker/Postgres ports напрямую.
## Launcher ## Launcher

View File

@ -3,10 +3,28 @@ AUTH_DOMAIN=auth.local.nodedc
LAUNCHER_DOMAIN=launcher.local.nodedc LAUNCHER_DOMAIN=launcher.local.nodedc
TASK_DOMAIN=task.local.nodedc TASK_DOMAIN=task.local.nodedc
# proxy
PLATFORM_HTTP_PORT=80
LOCAL_LAUNCHER_UPSTREAM=host.docker.internal:5173
LOCAL_TASK_MANAGER_UPSTREAM=host.docker.internal:8090
# authentik image
AUTHENTIK_IMAGE=ghcr.io/goauthentik/server
AUTHENTIK_TAG=2026.2.2
# authentik database
PG_DB=authentik
PG_USER=authentik
PG_PASS=change-me-generate-with-infra-scripts-init-dev-env
# authentik # authentik
AUTHENTIK_SECRET_KEY= AUTHENTIK_SECRET_KEY=change-me-generate-with-infra-scripts-init-dev-env
AUTHENTIK_POSTGRES_PASSWORD= AUTHENTIK_ERROR_REPORTING__ENABLED=false
AUTHENTIK_REDIS_HOST=redis-authentik
# optional first-start bootstrap
# AUTHENTIK_BOOTSTRAP_EMAIL=admin@nodedc.local
# AUTHENTIK_BOOTSTRAP_PASSWORD=
# AUTHENTIK_BOOTSTRAP_TOKEN=
# launcher oidc # launcher oidc
LAUNCHER_OIDC_ISSUER=http://auth.local.nodedc/application/o/launcher/ LAUNCHER_OIDC_ISSUER=http://auth.local.nodedc/application/o/launcher/
@ -21,10 +39,6 @@ PLANE_OIDC_CLIENT_SECRET=
PLANE_OIDC_REDIRECT_URI=http://task.local.nodedc/auth/oidc/callback PLANE_OIDC_REDIRECT_URI=http://task.local.nodedc/auth/oidc/callback
# security # security
SESSION_SECRET= SESSION_SECRET=change-me-generate-with-infra-scripts-init-dev-env
COOKIE_DOMAIN=.local.nodedc COOKIE_DOMAIN=.local.nodedc
COOKIE_SECURE=false COOKIE_SECURE=false
# current external local apps
LOCAL_LAUNCHER_URL=http://localhost:5173
LOCAL_TASK_MANAGER_URL=http://localhost:8090

View File

@ -8,19 +8,57 @@
- shared env examples; - shared env examples;
- будущие docker compose файлы. - будущие docker compose файлы.
На нулевом этапе здесь фиксируется только каркас. Рабочий `docker-compose.dev.yml` создается отдельным этапом после согласования ports/domains и стратегии подключения текущих Launcher/Plane runtime. Первый local dev слой проксирует текущие локальные приложения без физического переноса репозиториев:
- `auth.local.nodedc` -> `authentik-server:9000`;
- `launcher.local.nodedc` -> `host.docker.internal:5173`;
- `task.local.nodedc` -> `host.docker.internal:8090`.
Authentik построен по актуальной официальной Docker Compose схеме 2026.2: PostgreSQL 16, server и worker. Redis для Authentik в этой версии официального compose не используется.
## Expected files ## Expected files
```text ```text
infra/ infra/
.env.example .env.example
scripts/init-dev-env.sh
docker-compose.dev.yml docker-compose.dev.yml
docker-compose.staging.yml docker-compose.staging.yml
reverse-proxy/ reverse-proxy/
authentik/ authentik/
``` ```
## Local start
1. Add local domains to `/etc/hosts`:
```text
127.0.0.1 auth.local.nodedc
127.0.0.1 launcher.local.nodedc
127.0.0.1 task.local.nodedc
```
2. Generate local secrets:
```bash
./infra/scripts/init-dev-env.sh
```
3. Start infra:
```bash
docker compose --env-file infra/.env -f infra/docker-compose.dev.yml up -d
```
4. Check services:
```bash
docker compose --env-file infra/.env -f infra/docker-compose.dev.yml ps
curl -I -H 'Host: auth.local.nodedc' http://127.0.0.1/
```
Generated Authentik bootstrap credentials are stored only in `infra/.env`.
## Current decision ## Current decision
Текущий Plane runtime не переносится в compose платформы до backup и отдельного шага миграции. Текущий Plane runtime не переносится в compose платформы до backup и отдельного шага миграции.

35
infra/authentik/README.md Normal file
View File

@ -0,0 +1,35 @@
# Authentik Local Bootstrap
This directory stores local Authentik bootstrap assets for NODE.DC.
## Current scope
The first infra pass runs Authentik from the official Docker Compose shape for the 2026.2 release line:
- PostgreSQL 16;
- authentik server;
- authentik worker;
- no Redis service in the current official compose template;
- Caddy reverse proxy in front of Authentik and current local apps.
## Bootstrap variables
For a first local install, put these variables in `infra/.env`:
```bash
AUTHENTIK_BOOTSTRAP_EMAIL=admin@nodedc.local
AUTHENTIK_BOOTSTRAP_PASSWORD=<local generated password>
AUTHENTIK_BOOTSTRAP_TOKEN=<local generated token>
```
These are read only on first startup. Do not commit `infra/.env`.
## Future blueprint work
Later phases should add reproducible configuration for:
- NODE.DC Launcher Application/Provider;
- NODE.DC Task Manager Application/Provider;
- groups and policies;
- admin service token scope;
- exports or blueprints for repeatable setup.

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,95 @@
name: nodedc-platform
services:
reverse-proxy:
image: docker.io/library/caddy:2-alpine
restart: unless-stopped
env_file:
- path: .env
required: false
ports:
- "${PLATFORM_HTTP_PORT:-80}:80"
volumes:
- ./reverse-proxy/Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
depends_on:
authentik-server:
condition: service_started
extra_hosts:
- "host.docker.internal:host-gateway"
postgresql-authentik:
image: docker.io/library/postgres:16-alpine
restart: unless-stopped
env_file:
- path: .env
required: false
environment:
POSTGRES_DB: ${PG_DB:-authentik}
POSTGRES_PASSWORD: ${PG_PASS:?database password required}
POSTGRES_USER: ${PG_USER:-authentik}
healthcheck:
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
interval: 30s
timeout: 5s
retries: 5
start_period: 20s
volumes:
- authentik-database:/var/lib/postgresql/data
authentik-server:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2026.2.2}
command: server
restart: unless-stopped
env_file:
- path: .env
required: false
environment:
AUTHENTIK_POSTGRESQL__HOST: postgresql-authentik
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS:?database password required}
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS: ${AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS:-127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,::1/128}
depends_on:
postgresql-authentik:
condition: service_healthy
expose:
- "9000"
- "9443"
shm_size: 512mb
volumes:
- authentik-data:/data
- ./authentik/custom-templates:/templates
authentik-worker:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2026.2.2}
command: worker
restart: unless-stopped
user: root
env_file:
- path: .env
required: false
environment:
AUTHENTIK_POSTGRESQL__HOST: postgresql-authentik
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS:?database password required}
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
depends_on:
postgresql-authentik:
condition: service_healthy
shm_size: 512mb
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- authentik-data:/data
- authentik-certs:/certs
- ./authentik/custom-templates:/templates
volumes:
authentik-database:
authentik-data:
authentik-certs:
caddy-data:
caddy-config:

View File

@ -0,0 +1,27 @@
{
auto_https off
}
http://{$AUTH_DOMAIN:auth.local.nodedc} {
reverse_proxy authentik-server:9000 {
header_up Host {host}
header_up X-Forwarded-Proto {scheme}
header_up X-Forwarded-For {remote_host}
}
}
http://{$LAUNCHER_DOMAIN:launcher.local.nodedc} {
reverse_proxy {$LOCAL_LAUNCHER_UPSTREAM:host.docker.internal:5173} {
header_up Host {host}
header_up X-Forwarded-Proto {scheme}
header_up X-Forwarded-For {remote_host}
}
}
http://{$TASK_DOMAIN:task.local.nodedc} {
reverse_proxy {$LOCAL_TASK_MANAGER_UPSTREAM:host.docker.internal:8090} {
header_up Host {host}
header_up X-Forwarded-Proto {scheme}
header_up X-Forwarded-For {remote_host}
}
}

64
infra/scripts/init-dev-env.sh Executable file
View File

@ -0,0 +1,64 @@
#!/usr/bin/env sh
set -eu
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
INFRA_DIR=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)
ENV_FILE="$INFRA_DIR/.env"
if [ -f "$ENV_FILE" ]; then
echo "Refusing to overwrite existing $ENV_FILE" >&2
exit 1
fi
rand() {
openssl rand -base64 "$1" | tr -d '\n'
}
cat > "$ENV_FILE" <<EOF
# domains
AUTH_DOMAIN=auth.local.nodedc
LAUNCHER_DOMAIN=launcher.local.nodedc
TASK_DOMAIN=task.local.nodedc
# proxy
PLATFORM_HTTP_PORT=80
LOCAL_LAUNCHER_UPSTREAM=host.docker.internal:5173
LOCAL_TASK_MANAGER_UPSTREAM=host.docker.internal:8090
# authentik image
AUTHENTIK_IMAGE=ghcr.io/goauthentik/server
AUTHENTIK_TAG=2026.2.2
# authentik database
PG_DB=authentik
PG_USER=authentik
PG_PASS=$(rand 36)
# authentik core
AUTHENTIK_SECRET_KEY=$(rand 60)
AUTHENTIK_ERROR_REPORTING__ENABLED=false
AUTHENTIK_BOOTSTRAP_EMAIL=admin@nodedc.local
AUTHENTIK_BOOTSTRAP_PASSWORD=$(rand 36)
AUTHENTIK_BOOTSTRAP_TOKEN=$(rand 36)
# launcher oidc
LAUNCHER_OIDC_ISSUER=http://auth.local.nodedc/application/o/launcher/
LAUNCHER_OIDC_CLIENT_ID=
LAUNCHER_OIDC_CLIENT_SECRET=
LAUNCHER_OIDC_REDIRECT_URI=http://launcher.local.nodedc/auth/callback
# plane oidc
PLANE_OIDC_ISSUER=http://auth.local.nodedc/application/o/task-manager/
PLANE_OIDC_CLIENT_ID=
PLANE_OIDC_CLIENT_SECRET=
PLANE_OIDC_REDIRECT_URI=http://task.local.nodedc/auth/oidc/callback
# security
SESSION_SECRET=$(rand 48)
COOKIE_DOMAIN=.local.nodedc
COOKIE_SECURE=false
EOF
chmod 600 "$ENV_FILE"
echo "Created $ENV_FILE"
echo "Open $ENV_FILE to read the generated local akadmin bootstrap credentials."