[105-4][@ant] refactor: свернуть CoopID-overlay'и в единый docker-compose и перенести authentik_db+coop_domain_db в существующий postgres — чтобы весь стек поднимался одной командой docker compose up -d, как того требует прод-деплой через плейбук, без отдельного контейнера postgres
Typecheck / desktop (pull_request) Failing after 9m11s
Typecheck / controller (pull_request) Successful in 14m5s

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
coopops
2026-06-10 08:36:49 +00:00
parent 33548c2200
commit aa482316ee
6 changed files with 208 additions and 210 deletions
+3 -2
View File
@@ -128,10 +128,11 @@ const envVarsSchema = z.object({
.transform(v => (v ? [...new Set(v.split(',').map(s => s.trim()).filter(Boolean))] : []))
.pipe(z.array(z.string().url({ message: 'Каждый элемент BLOCKCHAIN_RPC_LIST — валидный URL' }))),
CHAIN_ID: z.string().min(1, { message: 'Не должно быть пустым' }),
// coop_domain_db (CoopID, Story 1.4): отдельная БД в coop-postgres (Story 1.1).
// coop_domain_db (CoopID, Story 1.4): отдельная БД в общем сервисе postgres.
// Пароль — значением или файлом (*_FILE приоритетнее; путь /run/secrets/... в контейнере).
// Дефолт-порт 5532 = host-маппинг существующего postgres (PG_HOST_PORT) для запуска вне контейнера.
COOP_DOMAIN_DB_HOST: z.string().default('127.0.0.1'),
COOP_DOMAIN_DB_PORT: z.coerce.number().default(5632),
COOP_DOMAIN_DB_PORT: z.coerce.number().default(5532),
COOP_DOMAIN_DB_USERNAME: z.string().default('coop_app_user'),
COOP_DOMAIN_DB_DATABASE: z.string().default('coop_domain_db'),
COOP_DOMAIN_DB_PASSWORD: z.string().optional(),
-122
View File
@@ -1,122 +0,0 @@
# Overlay CoopID: authentik OIDC Provider (per-coop) + выделенный postgres:16.
#
# Запуск вместе с базовым стеком:
# bash scripts/coopid-gen-secrets.sh # один раз, генерирует infra/coopid/secrets/
# docker compose -f docker-compose.yaml -f docker-compose.authentik.yml -f docker-compose.edge.yml up -d
#
# Существующий сервис postgres (18, БД voskhod) намеренно не используется:
# смена его образа на 16 сделала бы data dir нечитаемым. Две новые БД
# (authentik_db + coop_domain_db) живут в отдельном coop-postgres c ролевой
# изоляцией. Redis не дублируем — authentik работает в monoredis, DB-индекс 1.
# Подробности: infra/coopid/README.md
services:
coop-postgres:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/coop_pg_superuser_password
secrets:
- coop_pg_superuser_password
- coop_pg_authentik_password
- coop_pg_app_password
volumes:
- coop_postgres_data:/var/lib/postgresql/data
- ./infra/coopid/postgres/init:/docker-entrypoint-initdb.d:ro
ports:
- "127.0.0.1:${COOP_PG_HOST_PORT:-5632}:5432"
healthcheck:
# Не pg_isready: он не проверяет существование БД и маскирует упавший init-скрипт.
test: ["CMD-SHELL", "psql -U postgres -d authentik_db -c 'select 1' >/dev/null 2>&1 && psql -U postgres -d coop_domain_db -c 'select 1' >/dev/null 2>&1"]
interval: 10s
timeout: 5s
retries: 12
authentik-server:
image: ghcr.io/goauthentik/server:2026.2
restart: unless-stopped
command: server
environment: &authentik-env
AUTHENTIK_SECRET_KEY: file:///run/secrets/authentik_secret_key
AUTHENTIK_POSTGRESQL__HOST: coop-postgres
AUTHENTIK_POSTGRESQL__PORT: 5432
AUTHENTIK_POSTGRESQL__NAME: authentik_db
AUTHENTIK_POSTGRESQL__USER: authentik_user
AUTHENTIK_POSTGRESQL__PASSWORD: file:///run/secrets/coop_pg_authentik_password
AUTHENTIK_REDIS__HOST: monoredis
# controller использует DB 0; при шаринге одного redis несколькими коопами — развести индексы.
AUTHENTIK_REDIS__DB: ${AUTHENTIK_REDIS_DB:-1}
AUTHENTIK_BOOTSTRAP_PASSWORD: file:///run/secrets/authentik_bootstrap_password
AUTHENTIK_BOOTSTRAP_TOKEN: file:///run/secrets/authentik_bootstrap_token
AUTHENTIK_ERROR_REPORTING__ENABLED: "false"
AUTHENTIK_DISABLE_UPDATE_CHECK: "true"
AUTHENTIK_DISABLE_STARTUP_ANALYTICS: "true"
# Подставляется blueprint'ом coopid-password-policy в headers-mapping вебхука.
COOPID_WEBHOOK_TOKEN: ${COOPID_WEBHOOK_TOKEN:-}
volumes: &authentik-volumes
- ./infra/coopid/authentik/blueprints:/blueprints/custom:ro
secrets: &authentik-secrets
- authentik_secret_key
- authentik_bootstrap_password
- authentik_bootstrap_token
- coop_pg_authentik_password
ports:
- "127.0.0.1:${AUTHENTIK_HOST_PORT:-9008}:9000"
depends_on:
coop-postgres:
condition: service_healthy
monoredis:
condition: service_started
authentik-worker:
image: ghcr.io/goauthentik/server:2026.2
restart: unless-stopped
command: worker
environment: *authentik-env
volumes: *authentik-volumes
secrets: *authentik-secrets
depends_on:
coop-postgres:
condition: service_healthy
monoredis:
condition: service_started
# Аддитивное расширение сервиса из базового compose: ключ подписи
# participant_certificate и vault-ключ кооператива монтируются в controller
# (читаются CryptoService начиная со Stories 1.71.8).
coopback:
environment:
AUTH_V2_WEBHOOK_TOKEN_FILE: /run/secrets/authentik_webhook_token
AUTH_V2_SESSION_BINDING_SECRET_FILE: /run/secrets/auth_v2_session_binding_secret
AUTHENTIK_INTERNAL_URL: http://authentik-server:9000
secrets:
- coop_cert_key
- coop_vault_key
- coop_pg_app_password
- authentik_webhook_token
- auth_v2_session_binding_secret
secrets:
coop_pg_superuser_password:
file: ./infra/coopid/secrets/coop_pg_superuser_password
coop_pg_authentik_password:
file: ./infra/coopid/secrets/coop_pg_authentik_password
coop_pg_app_password:
file: ./infra/coopid/secrets/coop_pg_app_password
authentik_secret_key:
file: ./infra/coopid/secrets/authentik_secret_key
authentik_bootstrap_password:
file: ./infra/coopid/secrets/authentik_bootstrap_password
authentik_bootstrap_token:
file: ./infra/coopid/secrets/authentik_bootstrap_token
authentik_webhook_token:
file: ./infra/coopid/secrets/authentik_webhook_token
auth_v2_session_binding_secret:
file: ./infra/coopid/secrets/auth_v2_session_binding_secret
coop_cert_key:
file: ./infra/coopid/secrets/coop_cert_key
coop_vault_key:
file: ./infra/coopid/secrets/coop_vault_key
volumes:
coop_postgres_data:
-46
View File
@@ -1,46 +0,0 @@
# Overlay CoopID edge: caddy (внешний TLS 1.3) + crowdsec.
# Применяется ТОЛЬКО вместе с docker-compose.authentik.yml (caddy проксирует
# на authentik-server). Внутри docker-сети трафик plain HTTP, внешний TLS
# терминирует caddy. В Story 1.1 crowdsec только разбирает access-логи caddy;
# активный bouncer (блокировки) — Story 9.2.
services:
caddy:
image: caddy:2.11
restart: unless-stopped
environment:
# localhost → self-signed (внутренний CA caddy); реальный домен → Let's Encrypt.
COOPID_DOMAIN: ${COOPID_DOMAIN:-localhost}
ports:
# Прод с реальным доменом: CADDY_BIND=0.0.0.0, порты 80/443 — иначе ACME
# HTTP-01 challenge недостижим снаружи и Let's Encrypt не выдаст сертификат.
- "${CADDY_BIND:-127.0.0.1}:${CADDY_HTTP_PORT:-8088}:80"
- "${CADDY_BIND:-127.0.0.1}:${CADDY_HTTPS_PORT:-8443}:443"
volumes:
- ./infra/coopid/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
- caddy_logs:/var/log/caddy
depends_on:
- authentik-server
- coopback
crowdsec:
image: crowdsecurity/crowdsec:v1.7.8
restart: unless-stopped
environment:
COLLECTIONS: "crowdsecurity/caddy crowdsecurity/http-cve"
volumes:
- ./infra/coopid/crowdsec/acquis.yaml:/etc/crowdsec/acquis.yaml:ro
- crowdsec_db:/var/lib/crowdsec/data
- crowdsec_config:/etc/crowdsec
- caddy_logs:/var/log/caddy:ro
depends_on:
- caddy
volumes:
caddy_data:
caddy_config:
caddy_logs:
crowdsec_db:
crowdsec_config:
+136
View File
@@ -46,8 +46,23 @@ services:
POSTGRES_DB: voskhod
volumes:
- postgres_data:/var/lib/postgresql
# init-скрипт CoopID выполняется ТОЛЬКО при пустом data dir: на свежей
# машине создаёт authentik_db + coop_domain_db с ролевой изоляцией. На уже
# инициализированном томе БД заводятся вручную (см. infra/coopid/README.md).
- ./infra/coopid/postgres/init:/docker-entrypoint-initdb.d:ro
secrets:
# пароли прикладных ролей CoopID — init-скрипт читает их из /run/secrets.
- coop_pg_authentik_password
- coop_pg_app_password
ports:
- "127.0.0.1:${PG_HOST_PORT:-5532}:5432"
healthcheck:
# authentik стартует только после готовности обеих БД CoopID; pg_isready
# их бы не проверил и замаскировал недоинициализированный том.
test: ["CMD-SHELL", "psql -U postgres -d authentik_db -c 'select 1' >/dev/null 2>&1 && psql -U postgres -d coop_domain_db -c 'select 1' >/dev/null 2>&1"]
interval: 10s
timeout: 5s
retries: 12
coopback:
build:
@@ -69,6 +84,14 @@ services:
- "0.0.0.0:${COOPBACK_HOST_PORT:-2998}:2998"
working_dir: /app
command: pnpm --filter @coopenomics/controller run dev
# CoopID: ключ подписи participant_certificate (Stories 1.71.8), vault-ключ
# кооператива, пароль роли coop_domain_db, токены вебхука и session_binding.
secrets:
- coop_cert_key
- coop_vault_key
- coop_pg_app_password
- authentik_webhook_token
- auth_v2_session_binding_secret
cooparser:
build:
@@ -124,7 +147,120 @@ services:
timeout: 3s
retries: 10
# ── CoopID: per-coop OIDC (authentik) ──────────────────────────────────────
# Данные authentik живут отдельной БД authentik_db в общем сервисе postgres
# (роль authentik_user, ролевая изоляция от coop_domain_db). Redis — общий
# monoredis, DB-индекс 1 (controller сидит в индексе 0).
authentik-server:
image: ghcr.io/goauthentik/server:2026.2
restart: unless-stopped
command: server
environment: &authentik-env
AUTHENTIK_SECRET_KEY: file:///run/secrets/authentik_secret_key
AUTHENTIK_POSTGRESQL__HOST: postgres
AUTHENTIK_POSTGRESQL__PORT: 5432
AUTHENTIK_POSTGRESQL__NAME: authentik_db
AUTHENTIK_POSTGRESQL__USER: authentik_user
AUTHENTIK_POSTGRESQL__PASSWORD: file:///run/secrets/coop_pg_authentik_password
AUTHENTIK_REDIS__HOST: monoredis
AUTHENTIK_REDIS__DB: ${AUTHENTIK_REDIS_DB:-1}
AUTHENTIK_BOOTSTRAP_PASSWORD: file:///run/secrets/authentik_bootstrap_password
AUTHENTIK_BOOTSTRAP_TOKEN: file:///run/secrets/authentik_bootstrap_token
AUTHENTIK_ERROR_REPORTING__ENABLED: "false"
AUTHENTIK_DISABLE_UPDATE_CHECK: "true"
AUTHENTIK_DISABLE_STARTUP_ANALYTICS: "true"
# Подставляется blueprint'ом coopid-password-policy в headers-mapping вебхука.
COOPID_WEBHOOK_TOKEN: ${COOPID_WEBHOOK_TOKEN:-}
volumes: &authentik-volumes
- ./infra/coopid/authentik/blueprints:/blueprints/custom:ro
secrets: &authentik-secrets
- authentik_secret_key
- authentik_bootstrap_password
- authentik_bootstrap_token
- coop_pg_authentik_password
ports:
- "127.0.0.1:${AUTHENTIK_HOST_PORT:-9008}:9000"
depends_on:
postgres:
condition: service_healthy
monoredis:
condition: service_started
authentik-worker:
image: ghcr.io/goauthentik/server:2026.2
restart: unless-stopped
command: worker
environment: *authentik-env
volumes: *authentik-volumes
secrets: *authentik-secrets
depends_on:
postgres:
condition: service_healthy
monoredis:
condition: service_started
# ── CoopID edge: внешний TLS 1.3 (caddy) + разбор access-логов (crowdsec) ───
caddy:
image: caddy:2.11
restart: unless-stopped
environment:
# localhost → self-signed (внутренний CA caddy); реальный домен → Let's Encrypt.
COOPID_DOMAIN: ${COOPID_DOMAIN:-localhost}
ports:
# Прод с реальным доменом: CADDY_BIND=0.0.0.0, порты 80/443 — иначе ACME
# HTTP-01 challenge недостижим снаружи и Let's Encrypt не выдаст сертификат.
- "${CADDY_BIND:-127.0.0.1}:${CADDY_HTTP_PORT:-8088}:80"
- "${CADDY_BIND:-127.0.0.1}:${CADDY_HTTPS_PORT:-8443}:443"
volumes:
- ./infra/coopid/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
- caddy_logs:/var/log/caddy
depends_on:
- authentik-server
- coopback
crowdsec:
image: crowdsecurity/crowdsec:v1.7.8
restart: unless-stopped
environment:
COLLECTIONS: "crowdsecurity/caddy crowdsecurity/http-cve"
volumes:
- ./infra/coopid/crowdsec/acquis.yaml:/etc/crowdsec/acquis.yaml:ro
- crowdsec_db:/var/lib/crowdsec/data
- crowdsec_config:/etc/crowdsec
- caddy_logs:/var/log/caddy:ro
depends_on:
- caddy
# Все секреты CoopID — file-based из infra/coopid/secrets/ (каталог в .gitignore).
# На свежей машине: bash scripts/coopid-gen-secrets.sh. На prod раскладывает плейбук.
secrets:
coop_pg_authentik_password:
file: ./infra/coopid/secrets/coop_pg_authentik_password
coop_pg_app_password:
file: ./infra/coopid/secrets/coop_pg_app_password
authentik_secret_key:
file: ./infra/coopid/secrets/authentik_secret_key
authentik_bootstrap_password:
file: ./infra/coopid/secrets/authentik_bootstrap_password
authentik_bootstrap_token:
file: ./infra/coopid/secrets/authentik_bootstrap_token
authentik_webhook_token:
file: ./infra/coopid/secrets/authentik_webhook_token
auth_v2_session_binding_secret:
file: ./infra/coopid/secrets/auth_v2_session_binding_secret
coop_cert_key:
file: ./infra/coopid/secrets/coop_cert_key
coop_vault_key:
file: ./infra/coopid/secrets/coop_vault_key
volumes:
mongo_data:
postgres_data:
minio_data:
caddy_data:
caddy_config:
caddy_logs:
crowdsec_db:
crowdsec_config:
+63 -35
View File
@@ -1,63 +1,91 @@
# CoopID — инфраструктура per-coop (Story 1.1)
Стек OIDC-аутентификации кооператива: authentik 2026.2 (server + worker),
выделенный postgres:16 с двумя БД, caddy (внешний TLS 1.3) и crowdsec.
Подключается к базовому дев-стеку двумя аддитивными overlay-файлами —
существующие сервисы (`postgres` 18/voskhod, `monoredis`, `coopback`, `desktop`)
не изменяются и не пересоздаются.
Стек OIDC-аутентификации кооператива встроен в **основной** `docker-compose.yaml`
монорепы: authentik 2026.2 (server + worker), caddy (внешний TLS 1.3) и crowdsec.
Отдельный postgres не поднимается — данные authentik и доменные данные CoopID
живут **двумя отдельными БД в существующем сервисе `postgres`**:
- `authentik_db` — роль `authentik_user` (данные authentik);
- `coop_domain_db` — роль `coop_app_user` (vaults, audit, кэши — миграции V2.4.x).
Ролевая изоляция: подключиться к чужой БД может только её владелец (и суперюзер).
Redis — общий `monoredis` (authentik в DB-индексе 1, controller в 0).
Весь стек поднимается **одной командой** — никаких overlay `-f`-цепочек.
## Запуск
```bash
# 1. Секреты (один раз; идемпотентно)
# 1. Секреты (один раз; идемпотентно — существующие не перезаписывает)
bash scripts/coopid-gen-secrets.sh
# 2. Весь стек с overlays
docker compose -f docker-compose.yaml -f docker-compose.authentik.yml -f docker-compose.edge.yml up -d
# 2. Весь стек
docker compose up -d
```
Чтобы не повторять `-f`-цепочку, можно добавить в `.env`:
```
COMPOSE_FILE=docker-compose.yaml:docker-compose.authentik.yml:docker-compose.edge.yml
```
## Состав
## Состав (в docker-compose.yaml)
| Сервис | Образ | Назначение |
|---|---|---|
| `coop-postgres` | postgres:16 | Две БД: `authentik_db` (роль `authentik_user`) и `coop_domain_db` (роль `coop_app_user`); ролевая изоляция — подключение только владельцу |
| `postgres` | postgres:18 | Общий postgres. Кроме `voskhod` держит `authentik_db` и `coop_domain_db` (создаются init-скриптом на свежем томе либо вручную — см. ниже) |
| `authentik-server` | ghcr.io/goauthentik/server:2026.2 | OIDC Provider; UI/API на :9000 внутри сети |
| `authentik-worker` | ghcr.io/goauthentik/server:2026.2 | Фоновые задачи authentik |
| `caddy` | caddy:2.11 | Внешний HTTPS (TLS 1.3 min): `/api/coopback/*` → controller, остальное → authentik; JSON access-log |
| `crowdsec` | crowdsecurity/crowdsec:v1.7.8 | Разбор access-логов caddy (активный bouncer — Story 9.2) |
Redis отдельно не поднимается: authentik использует существующий `monoredis`
(DB-индекс 1; controller сидит в индексе 0 по умолчанию).
## Две БД в существующем postgres
`infra/coopid/postgres/init/01-init.sh` смонтирован в `/docker-entrypoint-initdb.d`
и заводит роли+БД **только при пустом data dir** (правило официального образа
postgres). Поэтому:
- **Свежая машина / пустой том** — `docker compose up -d` создаёт обе БД сам.
- **Уже инициализированный том** (как на текущих dev-стендах) — init-скрипт
не запускается, БД заводятся вручную тем же SQL:
```bash
AUTHPW=$(cat infra/coopid/secrets/coop_pg_authentik_password)
APPPW=$(cat infra/coopid/secrets/coop_pg_app_password)
docker compose exec -T postgres psql -v ON_ERROR_STOP=1 \
-v apw="$AUTHPW" -v cpw="$APPPW" -U postgres -d postgres <<'SQL'
CREATE ROLE authentik_user LOGIN PASSWORD :'apw';
CREATE ROLE coop_app_user LOGIN PASSWORD :'cpw';
CREATE DATABASE authentik_db OWNER authentik_user;
CREATE DATABASE coop_domain_db OWNER coop_app_user;
REVOKE CONNECT ON DATABASE authentik_db FROM PUBLIC;
REVOKE CONNECT ON DATABASE coop_domain_db FROM PUBLIC;
SQL
```
Затем coopback на старте применит миграции `coop_domain_db` (V2.4.0/V2.4.1).
## Секреты
Все секреты — file-based Docker Secrets из `infra/coopid/secrets/` (каталог в
`.gitignore`): `coop_cert_key` (secp256k1 PEM — подпись `participant_certificate`),
File-based Docker Secrets из `infra/coopid/secrets/` (каталог в `.gitignore`):
`coop_cert_key` (secp256k1 PEM — подпись `participant_certificate`),
`coop_vault_key`, `authentik_secret_key`, bootstrap-пароль/токен `akadmin`,
три пароля postgres. В `.env` и compose значений секретов нет; контейнеры
читают их из `/run/secrets/*` (postgres — `*_FILE`, authentik — `file://`-схема).
`authentik_webhook_token`, `auth_v2_session_binding_secret`, два пароля ролей
postgres. Суперюзер postgres — inline-пароль в compose, отдельного секрета нет.
В `.env`/compose значений секретов нет; контейнеры читают их из `/run/secrets/*`
(postgres-роли — через init-скрипт, authentik — `file://`, coopback — `*_FILE`).
На prod секреты раскладывает плейбук с правами `0400`.
## Порты (dev, INSTANCE_INDEX=1)
## Порты (dev, mono-ai-3)
- `127.0.0.1:8443` — caddy HTTPS (self-signed при `COOPID_DOMAIN=localhost`)
- `127.0.0.1:8088` — caddy HTTP
- `127.0.0.1:9008` — authentik напрямую (мимо caddy, для отладки)
- `127.0.0.1:5632`coop-postgres
- `127.0.0.1:9008` — authentik напрямую (мимо caddy, для отладки; `AUTHENTIK_HOST_PORT`)
- `127.0.0.1:5532` — postgres (обе БД, `PG_HOST_PORT`)
## Проверка
```bash
docker compose -f docker-compose.yaml -f docker-compose.authentik.yml -f docker-compose.edge.yml ps
curl -sk --tlsv1.3 https://127.0.0.1:8443/ -o /dev/null -w '%{http_code}\n' # 2xx/3xx от authentik
# Ролевая изоляция (ожидаемо: permission denied):
docker compose exec coop-postgres psql -U coop_app_user -d authentik_db -c 'select 1'
docker compose ps
# SNI обязателен: caddy с авто-TLS для localhost без SNI отдаёт TLS internal error.
curl -sk --tlsv1.3 --resolve localhost:8443:127.0.0.1 https://localhost:8443/ \
-o /dev/null -w '%{http_code}\n' # 302/200 от authentik (порт = CADDY_HTTPS_PORT)
# Ролевая изоляция (ожидаемо permission denied):
docker compose exec postgres psql -U coop_app_user -d authentik_db -c 'select 1'
```
Учётка администратора authentik: `akadmin`, пароль — в
@@ -65,12 +93,12 @@ docker compose exec coop-postgres psql -U coop_app_user -d authentik_db -c 'sele
## Troubleshooting
- **Init-скрипты postgres выполняются только при пустом data dir.** Если первый
старт coop-postgres упал на середине init (healthcheck вечно красный, БД/ролей
нет) или вы перегенерировали pg-пароли при живом volume — пересоздайте том:
`docker compose ... stop coop-postgres && docker compose ... rm -f coop-postgres
&& docker volume rm <project>_coop_postgres_data`, затем `up -d` заново.
Ротация паролей живой БД делается `ALTER ROLE`, не перегенерацией секрета.
- **Init-скрипт postgres выполняется только при пустом data dir.** На живом томе
заводите БД вручную (см. блок выше). Ротация паролей ролей — `ALTER ROLE`, не
перегенерацией секрета.
- **Прод с реальным доменом:** задайте `COOPID_DOMAIN`, `CADDY_BIND=0.0.0.0`,
`CADDY_HTTP_PORT=80`, `CADDY_HTTPS_PORT=443` — ACME HTTP-01 должен быть
достижим снаружи на 80/443.
- **authentik на postgres:18:** официальная поддержка на pg16, но pg18 как
сервер обратносовместим (Django+psycopg). На dev/testnet общий postgres
используется сознательно — единый стек одной командой важнее версии-в-версию.
+6 -5
View File
@@ -45,11 +45,12 @@ if ! grep -q '^COOPID_WEBHOOK_TOKEN=' "$ROOT/.env" 2>/dev/null; then
printf '\n# Webhook-токен authentik→coopback (= infra/coopid/secrets/authentik_webhook_token)\nCOOPID_WEBHOOK_TOKEN=%s\n' "$(cat "$SECRETS_DIR/authentik_webhook_token")" >> "$ROOT/.env"
echo " + COOPID_WEBHOOK_TOKEN добавлен в .env"
fi
# postgres: file-секреты в plain compose — bind-mount с хостовыми правами,
# а init-скрипты официального образа выполняются под uid 999 (postgres) —
# поэтому пароли postgres world-readable. Только для dev; prod-права — плейбук.
gen 0444 coop_pg_superuser_password rand_pass
# postgres: пароли прикладных ролей CoopID (authentik_user, coop_app_user).
# Суперюзер postgres использует inline-пароль из docker-compose.yaml — отдельный
# секрет ему не нужен. file-секреты в plain compose монтируются bind-mount'ом с
# хостовыми правами, а init-скрипты официального образа выполняются под uid 999
# (postgres) — поэтому пароли world-readable. Только dev; prod-права (0400) — плейбук.
gen 0444 coop_pg_authentik_password rand_pass
gen 0444 coop_pg_app_password rand_pass
echo "Готово. Дальше: docker compose -f docker-compose.yaml -f docker-compose.authentik.yml -f docker-compose.edge.yml up -d"
echo "Готово. Дальше: docker compose up -d (весь стек одной командой)."