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.
104 lines
3.6 KiB
Terraform
104 lines
3.6 KiB
Terraform
variable "zitadel_domain" {
|
|
description = "External domain of the ZITADEL instance, without scheme."
|
|
type = string
|
|
default = "zitadel.riotpiao.com"
|
|
}
|
|
|
|
variable "zitadel_port" {
|
|
description = "Port for the ZITADEL API. 443 behind ingress, 8080 for a direct pod port-forward."
|
|
type = string
|
|
default = "443"
|
|
}
|
|
|
|
variable "zitadel_insecure" {
|
|
description = "Call the API over plain HTTP (set true only when port-forwarding the pod)."
|
|
type = bool
|
|
default = false
|
|
}
|
|
|
|
variable "jwt_profile_file" {
|
|
description = "Path to the iam-admin machine key JSON extracted from the iam-admin Secret."
|
|
type = string
|
|
default = null
|
|
}
|
|
|
|
variable "org_id" {
|
|
description = <<-EOT
|
|
ID of the ZITADEL organization that holds the project. Read it from the
|
|
console: https://zitadel.riotpiao.com -> Organization settings -> Resource
|
|
ID. The chart's setup job creates the default org; this module never creates
|
|
one, because creating a second org would split the role and grant space.
|
|
EOT
|
|
type = string
|
|
}
|
|
|
|
variable "project_id" {
|
|
description = <<-EOT
|
|
Stable project ID. ZITADEL puts the project ID in the access token `aud`
|
|
claim, so pinning it keeps audience checks readable across a rebuild.
|
|
Every consumer that validates `aud` against a client ID must be re-pointed
|
|
to this value — see the plan §5.2.
|
|
EOT
|
|
type = string
|
|
default = "homelab"
|
|
}
|
|
|
|
variable "project_name" {
|
|
description = "Display name of the project that carries every role and application."
|
|
type = string
|
|
default = "homelab"
|
|
}
|
|
|
|
variable "machine_key_expiry" {
|
|
description = "Expiry of every machine key, RFC3339. Matches the chart's default iam-admin key window; rotate by changing this value and re-applying."
|
|
type = string
|
|
default = "2029-01-01T00:00:00Z"
|
|
}
|
|
|
|
variable "api_app_consumers" {
|
|
description = <<-EOT
|
|
Machine users that also need an API application (the client_credentials
|
|
replacement). Only the gateway does: every other agent signs a JWT with its
|
|
machine key and does not need a client_credentials grant.
|
|
EOT
|
|
type = map(string)
|
|
default = { api-gw = "Go LLM gateway" }
|
|
}
|
|
|
|
variable "enable_groups_claim_action" {
|
|
description = <<-EOT
|
|
Create the Actions v1 script and its trigger that synthesizes the `groups`
|
|
claim. Keep this false until the live go/no-go gate passes: the claim is
|
|
load-bearing for kube-apiserver RBAC, MinIO policies, Grafana role mapping
|
|
and every service that reads `groups`. Turning it on with an unverified
|
|
script can break every consumer at once.
|
|
EOT
|
|
type = bool
|
|
default = false
|
|
}
|
|
|
|
variable "groups_claim_script" {
|
|
description = <<-EOT
|
|
Path to the JavaScript body for the groups-claim action, read only when
|
|
enable_groups_claim_action is true. The body is deliberately not shipped
|
|
blind: the ZITADEL v1 Actions runtime API must be confirmed against the live
|
|
instance before a script is committed, because a wrong call silently emits
|
|
no claim instead of failing loudly.
|
|
EOT
|
|
type = string
|
|
default = null
|
|
|
|
validation {
|
|
# try() is required: HCL does not short-circuit `||`, so fileexists(null)
|
|
# would fail the whole plan even with the action disabled.
|
|
condition = try(fileexists(var.groups_claim_script), true)
|
|
error_message = "groups_claim_script must name an existing file when it is set."
|
|
}
|
|
|
|
# Enabling the action without a script would reach file(null) inside actions.tf.
|
|
validation {
|
|
condition = !var.enable_groups_claim_action || var.groups_claim_script != null
|
|
error_message = "enable_groups_claim_action requires groups_claim_script."
|
|
}
|
|
}
|