feat(security): Zitadel — SSO для домашних сервисов (envoy-internal) #55

Merged
hermes merged 9 commits from refs/pull/55/head into main 2026-09-20 09:45:26 +00:00
hermes commented 2026-09-19 23:27:08 +00:00 (Migrated from git.grachevko.ru)

Что это

Zitadel как SSO/OIDC для домашних сервисов, публикация через envoy-internal. Вход на том же хосте sso.<домен>, на котором живёт Zitadel: Login V2 — на префиксе /ui/v2/login, всё остальное — на API/консоль Zitadel.

Пришло на смену Authelia (PR #54 закрыт: нет регистрации и self-service сброса пароля без почты).

Состав (7 файлов, ns security)

файл что
zitadel/ks.yaml Flux-Kustomization, wait + health-check на HelmRelease, dependsOn cloudnative-pg (ns database) и external-secrets-stores (ns security), postBuild-подстановка ${SECRET_DOMAIN}
zitadel/app/ocirepository.yaml официальный чарт oci://ghcr.io/zitadel/zitadel-charts/zitadel, тег 10.0.6 (= Zitadel v4.15.3)
zitadel/app/helmrelease.yaml конфиг Zitadel: внешняя БД, Login V2, HTTPRoute, ресурсы, PDB
zitadel/app/postgres.yaml CNPG Cluster/postgres: instances: 3, база и владелец zitadel, 1Gi proxmox-ssd, enableSuperuserAccess: true
zitadel/app/externalsecret.yaml masterkey из Vault (kv/zitadel) в Secret zitadel-masterkey
zitadel/app/kustomization.yaml, security/kustomization.yaml сборка

Почему такие решения

  1. Официальный чарт, а не app-template: он же ставит init-джобу (миграции), setup-джобу (инстанс, iam-admin, ключ login-сервиса), Login V2, PDB и умеет Gateway API (gateway.httpRoute) — на app-template это пришлось бы писать руками.
  2. Вложенный postgres выключен (postgresql.enabled: false) — правило репо: базы только CNPG.
  3. Пароли БД из секретов CNPG: .Values.env чарт прокидывает и в Deployment, и в init/setup-джобы (helper zitadel.dbEnv), поэтому в git ничего не попадает, а миграции ходят теми же кредами. postgres-app (пользователь zitadel) + postgres-superuser (админ для провижининга).
  4. masterkey — из Vault: это корневой секрет (шифрует приватные ключи: подпись токенов, секреты OIDC-клиентов, SMTP), без него данные не расшифровать. zitadel.masterkeySecretName: zitadel-masterkey.
  5. driftDetection: Disabled: релиз собирается на Helm-хуках, иначе контроллер видит дрейф в hook-ресурсах.
  6. cleanupJob.enabled: false: post-delete-хук чарта удаляет секреты, созданные setup-джобой (ключ login-сервиса, ключ и PAT машинного админа) — они должны пережить переустановку релиза.
  7. TLS до БД выключен: соединение внутрикластерное, CNPG пускает без TLS. Нужен TLS — у чарта есть dbSslCaCrtSecret/dbSslUserCrtSecret/dbSslAdminCrtSecret, а PKI у CNPG уже есть.
  8. Реплики: 2 у Zitadel и 2 у login (Zitadel без состояния), PDB minAvailable: 1 у обоих. Реплики и ресурсы — ключи верхнего уровня чарта; внутри zitadel: живёт только конфигурация приложения (проверено рендером: с вложенными ключами deployment остаётся в 1 реплике).
  9. iam-admin остаётся (дефолт FirstInstance): машинный админ IAM с ключом и PAT — пригодится для IaC/API. Срок действия ключа и PAT — дефолт чарта 2029-01-01.

Требуется от владельца до деплоя

Секрет в Vault (fish, одна строка, генерация на месте):

vault kv put kv/zitadel masterkey=(openssl rand -hex 16)

Ровно 32 символа; копию значения — в менеджер паролей (без masterkey бэкап БД бесполезен).

Что дальше (после мёржа)

  1. Проверить джобы zitadel-init и zitadel-setup, затем поды zitadel-* и zitadel-login-*.
  2. Первый вход в https://sso.<домен> → визард настройки: организация и человек-админ (в v4 человеческий админ не создаётся чартом: LoginClient/LoginClientPatPath из чарта удалены).
  3. Завести OIDC-приложение и повесить SecurityPolicy (OIDC) на первый защищаемый сервис — отдельным MR.
  4. DNS: sso.<домен> уже резолвится в 192.168.40.11; в MikroTik осталась старая запись A sso.grachevko.ru → 192.168.1.2 (комментарий «old cluster h1») — почистить.
  5. PAT машинного админа лежит в секрете iam-admin-pat (в ns security), ключ — в zitadel-machinekey.

Проверено

  • flate test all — 73 passed, flate build all без ошибок.
  • Рендер объектов: Deployment/zitadel и Deployment/zitadel-login — replicas: 2; PDB у обоих minAvailable: 1; Job/zitadel-init и Job/zitadel-setup с env БД и masterkey; HTTPRoute/zitadel (/ → zitadel:8080) и HTTPRoute/zitadel-login (/ui/v2/login → zitadel-login:3000) на хосте sso.${SECRET_DOMAIN}; ConfigMap zitadel-config-yaml содержит Host/User/Admin/SSL и FirstInstance с iam-admin.
  • Факты по чарту сверены с тегом 10.0.6 (Chart.yaml, values.yaml, шаблоны job_init/job_setup/deployment/httproute, _helpers.tpl), а не с main ветки.
## Что это Zitadel как SSO/OIDC для домашних сервисов, публикация через `envoy-internal`. Вход на том же хосте `sso.<домен>`, на котором живёт Zitadel: `Login V2` — на префиксе `/ui/v2/login`, всё остальное — на API/консоль Zitadel. Пришло на смену Authelia (PR #54 закрыт: нет регистрации и self-service сброса пароля без почты). ## Состав (7 файлов, ns `security`) | файл | что | |---|---| | `zitadel/ks.yaml` | Flux-Kustomization, `wait` + health-check на HelmRelease, `dependsOn` cloudnative-pg (ns `database`) и external-secrets-stores (ns `security`), postBuild-подстановка `${SECRET_DOMAIN}` | | `zitadel/app/ocirepository.yaml` | официальный чарт `oci://ghcr.io/zitadel/zitadel-charts/zitadel`, тег **10.0.6** (= Zitadel **v4.15.3**) | | `zitadel/app/helmrelease.yaml` | конфиг Zitadel: внешняя БД, Login V2, HTTPRoute, ресурсы, PDB | | `zitadel/app/postgres.yaml` | CNPG `Cluster/postgres`: `instances: 3`, база и владелец `zitadel`, 1Gi `proxmox-ssd`, `enableSuperuserAccess: true` | | `zitadel/app/externalsecret.yaml` | masterkey из Vault (`kv/zitadel`) в Secret `zitadel-masterkey` | | `zitadel/app/kustomization.yaml`, `security/kustomization.yaml` | сборка | ## Почему такие решения 1. **Официальный чарт, а не app-template**: он же ставит init-джобу (миграции), setup-джобу (инстанс, `iam-admin`, ключ login-сервиса), Login V2, PDB и умеет Gateway API (`gateway.httpRoute`) — на app-template это пришлось бы писать руками. 2. **Вложенный postgres выключен** (`postgresql.enabled: false`) — правило репо: базы только CNPG. 3. **Пароли БД из секретов CNPG**: `.Values.env` чарт прокидывает и в Deployment, и в init/setup-джобы (helper `zitadel.dbEnv`), поэтому в git ничего не попадает, а миграции ходят теми же кредами. `postgres-app` (пользователь `zitadel`) + `postgres-superuser` (админ для провижининга). 4. **masterkey — из Vault**: это корневой секрет (шифрует приватные ключи: подпись токенов, секреты OIDC-клиентов, SMTP), без него данные не расшифровать. `zitadel.masterkeySecretName: zitadel-masterkey`. 5. **`driftDetection: Disabled`**: релиз собирается на Helm-хуках, иначе контроллер видит дрейф в hook-ресурсах. 6. **`cleanupJob.enabled: false`**: post-delete-хук чарта удаляет секреты, созданные setup-джобой (ключ login-сервиса, ключ и PAT машинного админа) — они должны пережить переустановку релиза. 7. **TLS до БД выключен**: соединение внутрикластерное, CNPG пускает без TLS. Нужен TLS — у чарта есть `dbSslCaCrtSecret`/`dbSslUserCrtSecret`/`dbSslAdminCrtSecret`, а PKI у CNPG уже есть. 8. **Реплики**: 2 у Zitadel и 2 у login (Zitadel без состояния), PDB `minAvailable: 1` у обоих. Реплики и ресурсы — ключи **верхнего уровня** чарта; внутри `zitadel:` живёт только конфигурация приложения (проверено рендером: с вложенными ключами deployment остаётся в 1 реплике). 9. **`iam-admin`** остаётся (дефолт `FirstInstance`): машинный админ IAM с ключом и PAT — пригодится для IaC/API. Срок действия ключа и PAT — дефолт чарта `2029-01-01`. ## Требуется от владельца до деплоя Секрет в Vault (fish, одна строка, генерация на месте): ```fish vault kv put kv/zitadel masterkey=(openssl rand -hex 16) ``` Ровно 32 символа; копию значения — в менеджер паролей (без masterkey бэкап БД бесполезен). ## Что дальше (после мёржа) 1. Проверить джобы `zitadel-init` и `zitadel-setup`, затем поды `zitadel-*` и `zitadel-login-*`. 2. Первый вход в `https://sso.<домен>` → визард настройки: организация и человек-админ (в v4 человеческий админ не создаётся чартом: `LoginClient`/`LoginClientPatPath` из чарта удалены). 3. Завести OIDC-приложение и повесить `SecurityPolicy` (OIDC) на первый защищаемый сервис — отдельным MR. 4. DNS: `sso.<домен>` уже резолвится в `192.168.40.11`; в MikroTik осталась старая запись `A sso.grachevko.ru → 192.168.1.2` (комментарий «old cluster h1») — почистить. 5. PAT машинного админа лежит в секрете `iam-admin-pat` (в ns `security`), ключ — в `zitadel-machinekey`. ## Проверено - `flate test all` — 73 passed, `flate build all` без ошибок. - Рендер объектов: Deployment/zitadel и Deployment/zitadel-login — `replicas: 2`; PDB у обоих `minAvailable: 1`; Job/zitadel-init и Job/zitadel-setup с env БД и masterkey; HTTPRoute/zitadel (`/` → `zitadel:8080`) и HTTPRoute/zitadel-login (`/ui/v2/login` → `zitadel-login:3000`) на хосте `sso.${SECRET_DOMAIN}`; ConfigMap `zitadel-config-yaml` содержит Host/User/Admin/SSL и `FirstInstance` с `iam-admin`. - Факты по чарту сверены с тегом **10.0.6** (Chart.yaml, values.yaml, шаблоны job_init/job_setup/deployment/httproute, `_helpers.tpl`), а не с main ветки.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
grachevko/home-ops!55
No description provided.