Commit Graph
17 Commits
Author SHA1 Message Date
rock 2f62351731 feat(iam): one script to provision Zitadel end to end
scripts/iam/zitadel-provision.py replaces the manual sequence that
provision-rbac.py did for Authentik. Phases: keys, apply, secrets, users,
verify. `all` runs keys -> apply -> secrets -> verify; users stays manual
because the password hash must come from a human, one user at a time.

- keys: extract the chart's iam-admin machine key and PAT to ~/.config/zitadel
  at 0600, outside the repo.
- apply: terraform apply terraform/zitadel with the machine key.
- secrets: write generated client secrets into the SOPS files, write client ids
  into the plain manifests that carry them, write machine keys per agent.
  Requires an exactly-one-match regex before it edits a manifest, prints the
  diff without --apply-config, and is a no-op when the value already matches.
- users: import a human user with the Django pbkdf2_sha256 hash converted to the
  passwap MCF form ZITADEL accepts.
- verify: discovery endpoint, client count, and it refuses to clear cutover
  while the groups-claim action is disabled.

Design points that came out of review:
- Multi-document SOPS files (minio) are rewritten whole, because `sops set`
  cannot address one document. sops reads the creation rule from the filename,
  so the temp file is passed with --filename-override; without it sops finds no
  matching rule and refuses. Ciphertext is staged in the destination directory
  and installed with os.replace, so the mode is preserved and EXDEV is
  impossible. SIGTERM/SIGINT trigger cleanup of the decrypted temp file.
- homarr is listed as manual: the live workload mounts Secret homarr-oidc, which
  no SOPS file in git defines, and the tracked homarr-auth-oidc.enc.yaml has no
  consumer. Writing the new secret into the orphan file would report success
  while the dashboard kept the old one.
- Secrets never enter argv: curl reads its config from stdin, so the PAT and the
  password hash do not appear in ps.

.gitignore: allow this one script through the scripts/iam/*.py ignore rule (it
embeds no credential), and ignore authentik-m2m-tokens.env, which holds live M2M
tokens and was one `git add -A` away from being committed.

Checks: py_compile clean, --help lists every phase, convert_hash verified on a
synthetic hash, the 3-document SOPS rewrite tested on a real copy (3 documents
survive, value set, second run unchanged, mode 644 kept, no temp left), all
three client-id regexes match exactly once, dry-run leaves the manifests
untouched. Reviewer round: BLOCK on the sops creation rule, the orphan homarr
Secret, the missing client-id writes and argv exposure; all fixed and
re-reviewed to SIGN-OFF.

Not run against a live ZITADEL: no instance exists yet.
2026-10-24 03:38:21 -07:00
rock f81ff0e2fc feat(iam): declare Zitadel identity objects in Terraform
Adds terraform/zitadel: the provisioning half the ArgoCD wiring left open.
It declares 1 project, 19 project roles, 12 OIDC applications, 5 machine users
plus keys, one API application for the gateway, project grants, per-agent claim
metadata, and a disabled groups-claim action.

Facts established while writing it:

- Client IDs cannot be preserved. ZITADEL v4.19.2 CreateOIDCApplicationRequest
  has no client_id field, and provider 3.8.7 rejects a settable application id
  (terraform validate reports "Invalid or unknown key"). Plan §3.2 assumed a
  client_id-preserving re-point; every consumer now also changes client ID.
- Role keys equal the existing group names, so the groups claim can be
  byte-identical and no RBAC file changes. v4.19.2 allows role keys of 1-200
  characters with no character-set restriction.
- Machine users get JWT profile keys, not passwords. Per-agent claims move to
  user metadata and reach tokens only through the groups-claim action.
- The action stays gated: enable_groups_claim_action defaults to false because
  the action runtime API is unverified. Two validations stop a null script from
  reaching file().

Checks: terraform fmt -check -recursive clean, terraform validate 0 errors,
plan reaches only the expected missing-credentials error, structural gate
0 error / 0 warning, and one reviewer round: BLOCK on a plan-time
fileexists(null) and on the missing api-gw machine path, both fixed, re-reviewed
to SIGN-OFF.

State is local and git-ignored; it carries generated client secrets and machine
keys. Move it to MinIO before a second operator applies.

Not applied: no Zitadel instance is running yet.
2026-10-24 03:38:21 -07:00
rock c5aadd98f8 chore: gitignore IAM provisioning scripts 2026-09-08 15:29:42 -07:00
Story Crater Bot 0fe3d25936 fix(ci): make the hardcoded-secret scan blocking and close the .gitignore/.sops.yaml gaps that let a plaintext deploy key through — also untracks tfplan binaries and skills-lock.json 2026-08-18 15:08:04 -07:00
Story Crater Bot 285151bd18 feat(bootstrap): add Phase 1c nginx ingress controller 2026-08-18 15:08:03 -07:00
Story Crater Bot 5b3307ffee fix(bootstrap): correct cluster config + complete Phase4 ArgoCD bootstrap permanent fixes 2026-08-18 15:08:03 -07:00
Story Crater Bot f4e3564adf chore: untrack docs/ and keep as local design notes (not part of the GitOps tree) 2026-08-18 15:08:03 -07:00
Story Crater Bot 41f5b05395 refactor(k8s): consolidate to infra/+apps/ single-source tree, dedicated per-app CNPG (authentik-db/temporal-db), wire monitoring-config, forgejo→cicd ns, drop orphan/stale (data-schemas, ollama, story-crater, sqs/argocd, key-rotation) 2026-08-18 15:08:03 -07:00
Story Crater Bot 54fa540b33 feat:Fix the bootstrap to be deploy key application 2026-08-18 15:08:03 -07:00
Story Crater Bot 5a8e5ae3fe docs: rewrite CLAUDE.md for ArgoCD GitOps, track in git 2026-08-18 15:08:03 -07:00
Story Crater Bot 5b34e71111 feat(terraform): restructure control planes into a 3-node map with LAN etcd advertise and live machine CA — enables talos-cp-1/2/3 HA and drops worker configs 2026-08-18 15:08:02 -07:00
Story Crater Bot 491e88e493 feat(ci,iac): Consolidate Forgejo CI workflows and add Talos Terraform IaC
Consolidate three separate Forgejo Actions (argocd-sync, security-scan, validate-k8s) into single cluster-ci workflow for cleaner CI/CD pipeline with proper job sequencing and reduced auth overhead.

Add Terraform configuration for Talos cluster machine configs:
- Provider setup for Talos
- Centralized variables for CP and worker configs
- Template-based config generation for controlplane.yaml and worker-*.yaml
- Sensitive data separated in terraform.tfvars (gitignored)
- Local state tracking for infrastructure
2026-08-18 15:08:01 -07:00
Story Crater BotandClaude Haiku 4.5 ee385e59a1 feat(phase0): setup SOPS for encrypted secret management
Phase 0 groundwork for helmfile→ArgoCD migration using SOPS (Secrets Operations):

1. Install SOPS + AGE encryption
   - AGE key generated and stored locally at ~/.sops/key.txt
   - Public key embedded in .sops.yaml for file encryption rules

2. Create K8s Secret for AGE private key
   - kubectl: create secret generic sops-age -n argocd --from-file=keys.txt=~/.sops/key.txt
   - ArgoCD will use this key to decrypt secrets at sync time

3. Encrypt initial secrets
   - k8s/base/secrets.enc.yaml: AES256_GCM encrypted secrets for all services
   - Placeholder values (will be replaced with real values per environment)
   - Secrets never visible in git (encrypted at rest)

4. Configure SOPS
   - .sops.yaml: creation rules for k8s/*/secrets.enc.yaml files
   - All future secret files auto-encrypt on edit (sops -e)

Setup: Store AGE key as K8s Secret in argocd namespace:
  export KUBECONFIG=cluster-config/kubeconfig
  kubectl create secret generic sops-age -n argocd --from-file=keys.txt=~/.sops/key.txt

Next: Configure ArgoCD Helm plugin to decrypt secrets on sync (Phase 0 continuation).

Co-Authored-By: Claude Haiku 4.5 <[email protected]>
2026-08-18 15:08:01 -07:00
Story Crater Bot 29d5ba3e6e chore(terraform): clean up TF state git-tracking (gitignore, drop cached state, dep bump) 2026-08-18 15:08:01 -07:00
Story Crater Bot 4e473978b5 Step 1 complete: Bootstrap layer with ArgoCD, cert-manager, namespaces imported to TF
- ArgoCD migrated to argocd namespace
- Cert-manager issuers/certs created
- 20 namespaces imported with pod-security labels
- S3 backend temporarily offline (MinIO), using local backup
- Pending: Remove metadata drift from helm releases, re-apply
2026-08-18 15:08:00 -07:00
Story Crater Bot 7835ac2932 feat: Terraform foundation for cluster & app bootstrap
Phase 1 infrastructure-as-code setup:
- Core providers (kubernetes, helm, null)
- 15 Helm repositories (grafana, minio, prometheus, etc.)
- Namespace scaffolding (15 namespaces with pod-security labels)
- Storage classes (longhorn, longhorn-kafka with prevent_destroy)
- TLS certificate bootstrap (selfsigned, CA, wildcard cert)
- Remote state backend config (local for now, S3/GCS TODO)
- Variable definitions for all secrets/OIDC clients

Tested: terraform plan passes with no changes (bootstrap infrastructure ready)
Next: Create 25 helm_release resources (Phase 2-4)

Kept helmfile intact; network/Cilium managed via helmfile (no config risk)
Co-Authored-By: Claude Haiku 4.5 <[email protected]>
2026-08-18 15:08:00 -07:00
Story Crater Bot 5b00616f41 docs: add foundation docs and cluster configuration templates
- README: cluster architecture, quick start, use cases
- USAGE: stack topology, custom CLI reference
- TROUBLESHOOTING: operational safety rules
- .env.example: configuration template
- Makefile: build shortcuts
2026-07-11 19:16:34 -07:00