ce7cb702218693d08ea6d83464d2457c9250027c

This commit is contained in:
ms2k 2026-11-12 20:46:28 +09:00
commit 34c5f8963c
20 changed files with 145336 additions and 0 deletions

View file

@ -0,0 +1,98 @@
# This file is maintained automatically by "tofu init".
# Manual edits may be lost in future updates.
provider "registry.opentofu.org/hashicorp/random" {
version = "3.9.0"
constraints = "~> 3.8"
hashes = [
"h1:8EQU5KSxezcjo/phRSe69rDOI0lk4pSaggj7FsskYp8=",
"h1:Lw9im2VBBJQ3RyAbHPQ0rcvcmmcZWm3x+kIOpN+Tv9s=",
"h1:U8KXqGCoNI9/guYbTvzgdtVk3fRthoG0UXwm1JoEpIs=",
"h1:YXaVd4p6qXPPVaxIBaIDNXmBwT02ZqDn0qD+tYpw8sA=",
"h1:cOpc03fphEt/G9Rfc4jLL/fW0D7tgvlXqiDKPF4vuww=",
"h1:g09RR7T1xWkeGrZwWvWMT9ncJrFGr1k3CBD585UmO7w=",
"h1:gGDdPPibmw2EWROx+sh1RGLjR5+nPwZyrf6/N9jXfeM=",
"h1:haE7/nXCOhXKP4oXeEnER3t5CaVQWqujz4nBnpeTUv4=",
"h1:ieSVpfZS2lKuMr05ph0QsOVpCzg7uk3cgKBaXR+Ikug=",
"h1:ig2s1IS9IzehorRjvVAnKIsUUj8fkgyxct1L/kswcc4=",
"h1:j3lS+ZEERFnoab8t1ppDrScGVP/cgWbzlCrEYKTCXYw=",
"h1:lxezrKmOiQIySHAM+os8qLVq7hqufDr8h3Hpzvsk+78=",
"h1:lzRqBJAG+NETxHbEZUJ/YP3RMEjZBinTX7VmgH3lw60=",
"h1:tdSNWK5ApqUsgbdYieyeYLTu6nIZUV3hR1oFqUfAuGo=",
"h1:xedet8yH/zI2CfdxsGlK0nlFWc/Bp61yrWsEa3fHB8g=",
"zh:03f1114cc20b8913523735ab76e0f0a2b16ce13c92923a53304bf85f07fc0dbc",
"zh:105b678ee72322a3067f105d7e05e940f6143238f377f6e87ff4ec909246ac2a",
"zh:55f3bbf13ea18cbace61a706566a80f25f33fe2b1780b6f3d7b582af2a05b6d2",
"zh:63adf996db48f082f7a6351eb485e219cd88795fc71e6ec60a837263ab0d2cb1",
"zh:7e99550738a4e3cc68b8a467714b0d69371025fe95e3326d5323d026d55653e9",
"zh:8342b54af3a18a37e075eeae61be57f4de2ba71b35d95c5075d402dd2c1f289d",
"zh:83ee18e32ac9dd5fc91298554b7c4cfa4c3a1db50f4c797945637cc93c0844ae",
"zh:993ecc0adbf6bd535a59fbc9b735d8c33950e6f6eb5e621d750da9b71d65d80a",
"zh:ad722bc59d4edbf1415e827fc007c0efe6e0e9462d5568bae20b34be1058a261",
"zh:ae9448e1f87b2f9a6c5197a0e9862162ec6b137cb3a3835e11522995d8939e7c",
"zh:bc9cdd3aac784f759125c6627f6f6416e8726a1c184eb9cf3e55b9edbc94c627",
"zh:c8e35b89572ba1c40a9b20022e033a3395fb8d42e7604d50c900f193ba10382e",
"zh:e2deaa8a9975ef81d9f62baed12c41286918b0a10908e0e031f13f69a3b730a1",
"zh:ee39707557210a0ab1098aa357d2cdfe502e5a312d0dbdffb09d08facc4d3fc5",
"zh:f81afe4eb63e8aa9e0ea71be6c990f0dc69cb360e7191c0742a991f4a5081b64",
]
}
provider "registry.opentofu.org/hashicorp/vault" {
version = "5.8.0"
constraints = "~> 5.0"
hashes = [
"h1:/OZCcd0X7F9y846SOlVUMItMtLDBwasYe+Mv3HF2I1Q=",
"h1:68t9TdU3ap9L+Ayewb478BIyv8rpEDIQxYq1xsq99fs=",
"h1:6MmwOTMo0TpjoMs27LNKfxD+kZypRclB2WrV1OgbWD4=",
"h1:8DpFTiNTio1Yal+kozfxkPabRvN8ucRyjVkRcKuCnCo=",
"h1:B1DuZ215BVQbK/70wahsWR6Wy16LMicdRCuThNgeBOg=",
"h1:EhuYG2XYdV7d3RuOB/r/KKv0k5XXtDoBMgrzPraH8yQ=",
"h1:FIRP0mpZpyDppetTZxzmaH2kd2E0o8As9w2CANYsEhE=",
"h1:KuT1QZ3kyjqZ/YENs5ABfhSHEBLr/01ZWzWbJrS+vDo=",
"h1:SMqA7BA2Nv/R4O8oVi8arc1E2NXz56dcnp54vFG9sfg=",
"h1:TO99KdU92KULAbT4D0CJSeGFLuEsOgR10d6qFnR3dDk=",
"h1:Y3HaoTjTuH4UePPja1vDrRxrSTNhR7wq//qJsJA7/rQ=",
"h1:bd5YeFG+ok71bEUWdvksBLPWBWVpeosSl0rbbDJAigE=",
"h1:eSjvykpglWlaCF/77dluIgXQ/SbPlh7RSSuLjAYNZUs=",
"h1:lUnHYFRPEO+C+DwumShhX7va0iY9ApkhoSSj/P7vCsk=",
"h1:qFAesj3MEaAzGFL2tvV+uy/biNwGJUculuN5CFtTh1E=",
"zh:1c9d0b740520a88a16ccc57c6bcb1a58e47b6ba19676428b2a0f5b9fe4004788",
"zh:274ad5bad107a845927773a6a31a45cd0ed7916fa52b86d85c86c535b59fcea4",
"zh:2b2eef2cb6840c3cf41134b1d1b6d3d50d6edbe3b2af2318bf7101843072c83b",
"zh:42b607a2983e77a47099b5ac8550a25ecd5ba03760be19749a4ad63617ea6085",
"zh:544877ac091e54b2954f5638edf06d76f344eb6833fb68db961c0edb5b0fdc83",
"zh:812bd7730efb3781fcfc7442650551d30b015f931a132ae9acdffdd625526908",
"zh:8798d164f8e25f3b211be45e79d3bd5272e9237e728942c0026300b25798e5fe",
"zh:91201a2e5dbc88013977960f0d1139fb584cdcda1cfd27577980d8cf0a31ead3",
"zh:a532662249b28e10a065bbab467d4ea9a12719638fe0f7e0add05d8d11142366",
"zh:ab91b32cbcc71cd9e89cca7d88957d9410f54750f7ea60f1416d2d77b478006a",
"zh:ae5658b04277318c8b91c0a9ed2ec05dd324a7501c1a057d53e79b0e1978a1f6",
"zh:e02581fca392f46738a95999d5fbccca7d82500df3a88e16ae9668633eb16f9b",
"zh:e89f59329ee939b59158c6173484ef4376da44296f6153d2fbafd3e28bd650c5",
"zh:e9ac49b94293cb5c8b31eb6e5afddc6134ffff0481ec195b242e0175e1f6766f",
"zh:ef60d5715c3b82ae64643dea9b4c1a82c10f72151c77af2749d3ceb060bb2adb",
]
}
provider "registry.opentofu.org/zitadel/zitadel" {
version = "3.4.0"
constraints = "~> 3.4"
hashes = [
"h1:yOyFsAgb3eBX/Ca13i7X88pmEXAp3+idVDjDilZtXYw=",
"zh:011a42ed078750c87c3a1d58f3090518603d837e29a8dea5badad36fb804309e",
"zh:1a6df05721c68e586110fd7c144d38379857fde7754598bb0364d1ca98b904c4",
"zh:32745b9ae6d6259d3eb49aeb2be34c1d6c48de475ab9e70302353af518f9381d",
"zh:3644e04087b01e63c8118c3dd4da02c6289ef9ec12ad9d4a32c9448884e6d5ff",
"zh:46a505939c0b9c137ab002e8d7d15a7ee8598d2d32d75ce858f1da5ee7f5dc19",
"zh:67dff0fe46d064ebf8aa352f065db964450dc5fd1159aa6e634340efcda54461",
"zh:9266c35fcd280c4b55eb4e702df974943372ac8bd691bfa8994c9dfa59021be1",
"zh:946419f83cc23418fcb95c93fb9345ed29222a64c24307262a17624223c6f001",
"zh:a33d53acc640dc93b81352ba633cf392bc8c7614a72d320d59d3dcdb22d73fc4",
"zh:a5e6410dd02e614ef699d3451d3f28dc48da0607acedcab306dfa5468d4ec9fd",
"zh:aa811343722e7b8d65cfe208fb15b382f386b7a9db052e09a78e6289908ee67b",
"zh:acd2f07a8df7911a9ab63a57dec9717656207f84de988ff64cd7195e73820d12",
"zh:c9f1b60d9c4245a96dac2a3a3f56f7a1637f0585af68747c6d7762e3c1b7179d",
"zh:e3c33dd2335712ce811bf84414a3da76cb143c0118d385389028032ff68b6c9d",
]
}

View file

@ -0,0 +1,44 @@
locals {
forgejo_public_oidc_redirect_uri = "${trim(var.forgejo_public_base_url, "/")}/user/oauth2/${var.forgejo_public_oauth_provider_name}/callback"
}
resource "zitadel_application_oidc" "forgejo_public" {
org_id = zitadel_org.primary.id
project_id = zitadel_project.services.id
name = "Public Forgejo"
redirect_uris = [local.forgejo_public_oidc_redirect_uri]
response_types = ["OIDC_RESPONSE_TYPE_CODE"]
grant_types = ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE", "OIDC_GRANT_TYPE_REFRESH_TOKEN"]
post_logout_redirect_uris = [trim(var.forgejo_public_base_url, "/")]
app_type = "OIDC_APP_TYPE_WEB"
auth_method_type = "OIDC_AUTH_METHOD_TYPE_BASIC"
version = "OIDC_VERSION_1_0"
clock_skew = "0s"
dev_mode = false
access_token_type = "OIDC_TOKEN_TYPE_BEARER"
access_token_role_assertion = false
id_token_role_assertion = true
id_token_userinfo_assertion = true
additional_origins = []
skip_native_app_success_page = false
}
resource "vault_kv_secret_v2" "forgejo_public_oidc" {
mount = var.vault_kv_mount
name = var.forgejo_public_oidc_vault_path
data_json = jsonencode({
auth_name = var.forgejo_public_oauth_provider_name
key = zitadel_application_oidc.forgejo_public.client_id
secret = zitadel_application_oidc.forgejo_public.client_secret
client_id = zitadel_application_oidc.forgejo_public.client_id
client_secret = zitadel_application_oidc.forgejo_public.client_secret
discovery_url = "https://${var.zitadel_domain}/.well-known/openid-configuration"
roles_claim = local.forgejo_roles_claim
redirect_uri = local.forgejo_public_oidc_redirect_uri
})
lifecycle {
ignore_changes = [data_json]
}
}

View file

@ -0,0 +1,71 @@
locals {
forgejo_oidc_redirect_uri = "${trim(var.forgejo_base_url, "/")}/user/oauth2/${var.forgejo_oauth_provider_name}/callback"
forgejo_roles_claim = "groups"
forgejo_zitadel_roles = {
forgejo_user = {
display_name = "Forgejo user"
group = "Forgejo"
}
forgejo_admin = {
display_name = "Forgejo administrator"
group = "Forgejo"
}
forgejo_restricted = {
display_name = "Forgejo restricted user"
group = "Forgejo"
}
}
forgejo_zitadel_role_keys = keys(local.forgejo_zitadel_roles)
}
resource "zitadel_project_role" "forgejo" {
for_each = local.forgejo_zitadel_roles
org_id = zitadel_org.primary.id
project_id = zitadel_project.services.id
role_key = each.key
display_name = each.value.display_name
group = each.value.group
}
resource "zitadel_application_oidc" "forgejo" {
org_id = zitadel_org.primary.id
project_id = zitadel_project.services.id
name = "Forgejo"
redirect_uris = [local.forgejo_oidc_redirect_uri]
response_types = ["OIDC_RESPONSE_TYPE_CODE"]
grant_types = ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE", "OIDC_GRANT_TYPE_REFRESH_TOKEN"]
post_logout_redirect_uris = [trim(var.forgejo_base_url, "/")]
app_type = "OIDC_APP_TYPE_WEB"
auth_method_type = "OIDC_AUTH_METHOD_TYPE_BASIC"
version = "OIDC_VERSION_1_0"
clock_skew = "0s"
dev_mode = false
access_token_type = "OIDC_TOKEN_TYPE_BEARER"
access_token_role_assertion = false
id_token_role_assertion = true
id_token_userinfo_assertion = true
additional_origins = []
skip_native_app_success_page = false
}
resource "vault_kv_secret_v2" "forgejo_oidc" {
mount = var.vault_kv_mount
name = var.forgejo_oidc_vault_path
data_json = jsonencode({
auth_name = var.forgejo_oauth_provider_name
key = zitadel_application_oidc.forgejo.client_id
secret = zitadel_application_oidc.forgejo.client_secret
client_id = zitadel_application_oidc.forgejo.client_id
client_secret = zitadel_application_oidc.forgejo.client_secret
discovery_url = "https://${var.zitadel_domain}/.well-known/openid-configuration"
roles_claim = local.forgejo_roles_claim
redirect_uri = local.forgejo_oidc_redirect_uri
})
# Vault is source of truth after first write. OIDC credential rotation is explicit.
lifecycle { ignore_changes = [data_json] }
}

View file

@ -0,0 +1,47 @@
locals {
frigate_oauth2_proxy_redirect_uri = "${trim(var.frigate_base_url, "/")}/oauth2/callback"
frigate_zitadel_roles = {
frigate_viewer = {
display_name = "Frigate viewer"
group = "Frigate"
}
frigate_admin = {
display_name = "Frigate administrator"
group = "Frigate"
}
}
frigate_zitadel_role_keys = keys(local.frigate_zitadel_roles)
}
resource "zitadel_project_role" "frigate" {
for_each = local.frigate_zitadel_roles
org_id = zitadel_org.primary.id
project_id = zitadel_project.services.id
role_key = each.key
display_name = each.value.display_name
group = each.value.group
}
resource "zitadel_application_oidc" "frigate" {
org_id = zitadel_org.primary.id
project_id = zitadel_project.services.id
name = "Frigate"
redirect_uris = [local.frigate_oauth2_proxy_redirect_uri]
response_types = ["OIDC_RESPONSE_TYPE_CODE"]
grant_types = ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE", "OIDC_GRANT_TYPE_REFRESH_TOKEN"]
post_logout_redirect_uris = [trim(var.frigate_base_url, "/")]
app_type = "OIDC_APP_TYPE_WEB"
auth_method_type = "OIDC_AUTH_METHOD_TYPE_BASIC"
version = "OIDC_VERSION_1_0"
clock_skew = "0s"
dev_mode = false
access_token_type = "OIDC_TOKEN_TYPE_BEARER"
access_token_role_assertion = false
id_token_role_assertion = true
id_token_userinfo_assertion = true
additional_origins = []
skip_native_app_success_page = false
}

View file

@ -0,0 +1,68 @@
locals {
grafana_oidc_redirect_uri = "${trim(var.grafana_base_url, "/")}/login/generic_oauth"
grafana_zitadel_roles = {
grafana_viewer = {
display_name = "Grafana viewer"
group = "Grafana"
}
grafana_editor = {
display_name = "Grafana editor"
group = "Grafana"
}
grafana_admin = {
display_name = "Grafana administrator"
group = "Grafana"
}
}
grafana_zitadel_role_keys = keys(local.grafana_zitadel_roles)
}
resource "zitadel_project_role" "grafana" {
for_each = local.grafana_zitadel_roles
org_id = zitadel_org.primary.id
project_id = zitadel_project.services.id
role_key = each.key
display_name = each.value.display_name
group = each.value.group
}
resource "zitadel_application_oidc" "grafana" {
org_id = zitadel_org.primary.id
project_id = zitadel_project.services.id
name = "Grafana"
redirect_uris = [local.grafana_oidc_redirect_uri]
response_types = ["OIDC_RESPONSE_TYPE_CODE"]
grant_types = ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE", "OIDC_GRANT_TYPE_REFRESH_TOKEN"]
post_logout_redirect_uris = [trim(var.grafana_base_url, "/")]
app_type = "OIDC_APP_TYPE_WEB"
auth_method_type = "OIDC_AUTH_METHOD_TYPE_BASIC"
version = "OIDC_VERSION_1_0"
clock_skew = "0s"
dev_mode = false
access_token_type = "OIDC_TOKEN_TYPE_BEARER"
access_token_role_assertion = false
id_token_role_assertion = true
id_token_userinfo_assertion = true
additional_origins = []
skip_native_app_success_page = false
}
resource "vault_kv_secret_v2" "grafana_oidc" {
mount = var.vault_kv_mount
name = var.grafana_oidc_vault_path
data_json = jsonencode({
GF_AUTH_GENERIC_OAUTH_CLIENT_ID = zitadel_application_oidc.grafana.client_id
GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET = zitadel_application_oidc.grafana.client_secret
client_id = zitadel_application_oidc.grafana.client_id
client_secret = zitadel_application_oidc.grafana.client_secret
discovery_url = "https://${var.zitadel_domain}/.well-known/openid-configuration"
redirect_uri = local.grafana_oidc_redirect_uri
})
# Vault is source of truth after first write. OIDC credential rotation is explicit.
lifecycle { ignore_changes = [data_json] }
}

View file

@ -0,0 +1,42 @@
locals {
service_admin_zitadel_user_grants = {
(var.service_admin_zitadel_user_id) = var.service_admin_zitadel_role_keys
}
service_grant_user_ids = setunion(
keys(local.service_admin_zitadel_user_grants),
keys(var.frigate_zitadel_user_grants),
keys(var.forgejo_zitadel_user_grants),
keys(var.grafana_zitadel_user_grants),
keys(var.longhorn_zitadel_user_grants),
keys(var.quay_zitadel_user_grants),
)
service_zitadel_user_grants = {
for user_id in local.service_grant_user_ids : user_id => toset(flatten([
tolist(lookup(local.service_admin_zitadel_user_grants, user_id, toset([]))),
tolist(lookup(var.frigate_zitadel_user_grants, user_id, toset([]))),
tolist(lookup(var.forgejo_zitadel_user_grants, user_id, toset([]))),
tolist(lookup(var.grafana_zitadel_user_grants, user_id, toset([]))),
tolist(lookup(var.longhorn_zitadel_user_grants, user_id, toset([]))),
tolist(lookup(var.quay_zitadel_user_grants, user_id, toset([]))),
]))
}
}
resource "zitadel_user_grant" "services" {
for_each = local.service_zitadel_user_grants
org_id = zitadel_org.primary.id
project_id = zitadel_project.services.id
user_id = each.key
role_keys = each.value
depends_on = [
zitadel_project_role.forgejo,
zitadel_project_role.frigate,
zitadel_project_role.grafana,
zitadel_project_role.longhorn,
zitadel_project_role.quay,
]
}

View file

@ -0,0 +1,69 @@
locals {
hf_callback = "https://chat.quorate.internal/login/callback"
}
resource "zitadel_project" "hf_chat_ui" {
org_id = zitadel_org.primary.id
name = "HF Chat UI"
project_role_assertion = true
project_role_check = true
has_project_check = false
private_labeling_setting = "PRIVATE_LABELING_SETTING_UNSPECIFIED"
}
resource "zitadel_project_role" "hf_chat_ui" {
org_id = zitadel_org.primary.id
project_id = zitadel_project.hf_chat_ui.id
role_key = "hf_chat_ui_user"
display_name = "HF Chat UI user"
group = "HF Chat UI"
}
resource "zitadel_application_oidc" "hf_chat_ui" {
org_id = zitadel_org.primary.id
project_id = zitadel_project.hf_chat_ui.id
name = "HF Chat UI"
redirect_uris = [local.hf_callback]
response_types = ["OIDC_RESPONSE_TYPE_CODE"]
grant_types = ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE", "OIDC_GRANT_TYPE_REFRESH_TOKEN"]
post_logout_redirect_uris = ["https://chat.quorate.internal"]
app_type = "OIDC_APP_TYPE_WEB"
auth_method_type = "OIDC_AUTH_METHOD_TYPE_BASIC"
version = "OIDC_VERSION_1_0"
clock_skew = "0s"
dev_mode = false
access_token_type = "OIDC_TOKEN_TYPE_BEARER"
access_token_role_assertion = false
id_token_role_assertion = false
id_token_userinfo_assertion = true
additional_origins = []
skip_native_app_success_page = false
}
resource "vault_kv_secret_v2" "hf_chat_ui_oidc" {
mount = var.vault_kv_mount
name = "hf-chat-ui/oidc/zitadel"
data_json = jsonencode({
OPENID_CLIENT_ID = zitadel_application_oidc.hf_chat_ui.client_id
OPENID_CLIENT_SECRET = zitadel_application_oidc.hf_chat_ui.client_secret
client_id = zitadel_application_oidc.hf_chat_ui.client_id
client_secret = zitadel_application_oidc.hf_chat_ui.client_secret
discovery_url = "https://${var.zitadel_domain}/.well-known/openid-configuration"
redirect_uri = local.hf_callback
})
lifecycle {
ignore_changes = [data_json]
replace_triggered_by = [
zitadel_application_oidc.hf_chat_ui,
]
}
}
resource "zitadel_user_grant" "hf_chat_ui" {
org_id = zitadel_org.primary.id
project_id = zitadel_project.hf_chat_ui.id
user_id = var.service_admin_zitadel_user_id
role_keys = [zitadel_project_role.hf_chat_ui.role_key]
}

View file

@ -0,0 +1,13 @@
resource "zitadel_machine_user" "iam_admin" {
org_id = "369418441481847110"
user_name = "iam-admin"
name = "Automatically Initialized IAM Admin"
description = "IAM admin"
access_token_type = "ACCESS_TOKEN_TYPE_BEARER"
with_secret = false
}
import {
to = zitadel_machine_user.iam_admin
id = "369418441482371398:false:369418441481847110"
}

View file

@ -0,0 +1,21 @@
resource "zitadel_default_login_policy" "instance" {
user_login = true
allow_register = false
allow_external_idp = true
force_mfa = false
force_mfa_local_only = false
passwordless_type = "PASSWORDLESS_TYPE_ALLOWED"
hide_password_reset = false
ignore_unknown_usernames = false
allow_domain_discovery = false
disable_login_with_email = false
disable_login_with_phone = false
default_redirect_uri = "https://${var.zitadel_domain}/ui/console"
password_check_lifetime = "240h0m0s"
external_login_check_lifetime = "240h0m0s"
multi_factor_check_lifetime = "12h0m0s"
mfa_init_skip_lifetime = "720h0m0s"
second_factor_check_lifetime = "18h0m0s"
second_factors = ["SECOND_FACTOR_TYPE_OTP", "SECOND_FACTOR_TYPE_U2F"]
multi_factors = ["MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION"]
}

View file

@ -0,0 +1,66 @@
locals {
longhorn_oauth2_proxy_redirect_uri = "${trim(var.longhorn_base_url, "/")}/oauth2/callback"
longhorn_zitadel_roles = {
longhorn_admin = {
display_name = "Longhorn administrator"
group = "Longhorn"
}
}
longhorn_zitadel_role_keys = keys(local.longhorn_zitadel_roles)
}
resource "zitadel_project_role" "longhorn" {
for_each = local.longhorn_zitadel_roles
org_id = zitadel_org.primary.id
project_id = zitadel_project.services.id
role_key = each.key
display_name = each.value.display_name
group = each.value.group
}
resource "zitadel_application_oidc" "longhorn" {
org_id = zitadel_org.primary.id
project_id = zitadel_project.services.id
name = "Longhorn"
redirect_uris = [local.longhorn_oauth2_proxy_redirect_uri]
response_types = ["OIDC_RESPONSE_TYPE_CODE"]
grant_types = ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE", "OIDC_GRANT_TYPE_REFRESH_TOKEN"]
post_logout_redirect_uris = [trim(var.longhorn_base_url, "/")]
app_type = "OIDC_APP_TYPE_WEB"
auth_method_type = "OIDC_AUTH_METHOD_TYPE_BASIC"
version = "OIDC_VERSION_1_0"
clock_skew = "0s"
dev_mode = false
access_token_type = "OIDC_TOKEN_TYPE_BEARER"
access_token_role_assertion = false
id_token_role_assertion = true
id_token_userinfo_assertion = true
additional_origins = []
skip_native_app_success_page = false
}
resource "random_password" "longhorn_oauth2_proxy_cookie_secret" {
length = 32
special = false
}
resource "vault_kv_secret_v2" "longhorn_oauth2_proxy" {
mount = var.vault_kv_mount
name = var.longhorn_oauth2_proxy_vault_path
data_json = jsonencode({
OAUTH2_PROXY_CLIENT_ID = zitadel_application_oidc.longhorn.client_id
OAUTH2_PROXY_CLIENT_SECRET = zitadel_application_oidc.longhorn.client_secret
OAUTH2_PROXY_COOKIE_SECRET = random_password.longhorn_oauth2_proxy_cookie_secret.result
client_id = zitadel_application_oidc.longhorn.client_id
client_secret = zitadel_application_oidc.longhorn.client_secret
discovery_url = "https://${var.zitadel_domain}/.well-known/openid-configuration"
redirect_uri = local.longhorn_oauth2_proxy_redirect_uri
})
# Vault is source of truth after first write. OIDC credential rotation is explicit.
lifecycle { ignore_changes = [data_json] }
}

View file

@ -0,0 +1,14 @@
moved {
from = zitadel_action.forgejo_roles_claim
to = zitadel_action.service_roles_claim
}
moved {
from = zitadel_trigger_actions.forgejo_roles_claim_userinfo
to = zitadel_trigger_actions.service_roles_claim_userinfo
}
moved {
from = zitadel_trigger_actions.forgejo_roles_claim_access_token
to = zitadel_trigger_actions.service_roles_claim_access_token
}

View file

@ -0,0 +1,4 @@
resource "zitadel_org" "primary" {
name = var.zitadel_org_name
org_id = var.zitadel_org_id
}

View file

@ -0,0 +1,129 @@
output "zitadel_org_id" {
description = "ID of the primary service ZITADEL organization."
value = zitadel_org.primary.id
}
output "zitadel_org_primary_domain" {
description = "Primary domain assigned by ZITADEL for the primary service organization."
value = zitadel_org.primary.primary_domain
}
output "zitadel_project_id" {
description = "ID of the ZITADEL project that groups service clients."
value = zitadel_project.services.id
}
output "forgejo_oidc_authentication_name" {
description = "Authentication source name to use in Forgejo."
value = var.forgejo_oauth_provider_name
}
output "forgejo_oidc_discovery_url" {
description = "OpenID Connect discovery URL for Forgejo."
value = "https://${var.zitadel_domain}/.well-known/openid-configuration"
}
output "forgejo_oidc_redirect_uri" {
description = "Redirect URI registered in the Forgejo ZITADEL application."
value = local.forgejo_oidc_redirect_uri
}
output "forgejo_oidc_vault_secret_path" {
description = "Vault KV v2 path containing Forgejo's ZITADEL OIDC client material."
value = "${var.vault_kv_mount}/data/${var.forgejo_oidc_vault_path}"
}
output "forgejo_zitadel_roles_claim" {
description = "Flat ZITADEL custom claim reserved for Forgejo group/admin mapping."
value = local.forgejo_roles_claim
}
output "forgejo_zitadel_role_keys" {
description = "ZITADEL project role keys used to authorize Forgejo sign-in."
value = local.forgejo_zitadel_role_keys
}
output "frigate_oauth2_proxy_redirect_uri" {
description = "Redirect URI registered in the Frigate ZITADEL application."
value = local.frigate_oauth2_proxy_redirect_uri
}
output "frigate_oauth2_client_id" {
description = "Client ID generated for the Frigate ZITADEL application."
value = nonsensitive(zitadel_application_oidc.frigate.client_id)
}
output "frigate_zitadel_role_keys" {
description = "ZITADEL project role keys used for Frigate role mapping."
value = local.frigate_zitadel_role_keys
}
output "forgejo_public_oidc_redirect_uri" {
description = "Redirect URI registered in the public Forgejo ZITADEL application."
value = local.forgejo_public_oidc_redirect_uri
}
output "forgejo_public_oidc_vault_secret_path" {
description = "Vault KV v2 path containing public Forgejo's ZITADEL OIDC client material."
value = "${var.vault_kv_mount}/data/${var.forgejo_public_oidc_vault_path}"
}
output "grafana_oidc_redirect_uri" {
description = "Redirect URI registered in the Grafana ZITADEL application."
value = local.grafana_oidc_redirect_uri
}
output "grafana_oidc_vault_secret_path" {
description = "Vault KV v2 path containing Grafana's ZITADEL OIDC client material."
value = "${var.vault_kv_mount}/data/${var.grafana_oidc_vault_path}"
}
output "grafana_zitadel_role_keys" {
description = "ZITADEL project role keys used for Grafana role mapping."
value = local.grafana_zitadel_role_keys
}
output "longhorn_oauth2_proxy_redirect_uri" {
description = "Redirect URI registered in the Longhorn ZITADEL application."
value = local.longhorn_oauth2_proxy_redirect_uri
}
output "longhorn_oauth2_proxy_vault_secret_path" {
description = "Vault KV v2 path containing Longhorn's oauth2-proxy OIDC client material."
value = "${var.vault_kv_mount}/data/${var.longhorn_oauth2_proxy_vault_path}"
}
output "longhorn_zitadel_role_keys" {
description = "ZITADEL project role keys used to authorize Longhorn UI access."
value = local.longhorn_zitadel_role_keys
}
output "stalwart_zitadel_role_keys" {
description = "ZITADEL project role keys used to authorize Stalwart administration."
value = local.stalwart_zitadel_role_keys
}
output "stalwart_oidc_vault_secret_path" {
description = "Vault KV v2 path containing Stalwart's ZITADEL resource-server metadata."
value = "${var.vault_kv_mount}/data/${var.stalwart_oidc_vault_path}"
}
output "stalwart_account_webui_client_id" {
description = "Generated public OIDC client ID embedded in the Stalwart account WebUI build."
value = nonsensitive(zitadel_application_oidc.stalwart_account_webui.client_id)
}
output "quay_oidc_redirect_uri" {
description = "Redirect URI registered in the Quay ZITADEL application."
value = local.quay_oidc_redirect_uri
}
output "quay_oidc_vault_secret_path" {
description = "Vault KV v2 path containing Quay's ZITADEL OIDC client material."
value = "${var.vault_kv_mount}/data/${var.quay_oidc_vault_path}"
}
output "quay_zitadel_role_keys" {
description = "ZITADEL project role keys used to authorize Quay sign-in."
value = local.quay_zitadel_role_keys
}

View file

@ -0,0 +1,61 @@
terraform {
required_version = "< 2.0.0"
backend "pg" {
conn_str = "postgres://10.234.158.153:5432/tf_state?sslmode=require"
schema_name = "internal_zitadel"
}
required_providers {
zitadel = {
source = "zitadel/zitadel"
version = "~> 3.4"
}
vault = {
source = "hashicorp/vault"
version = "~> 5.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.8"
}
}
}
# VAULT_ADDR and VAULT_TOKEN are read from the environment.
provider "vault" {}
provider "random" {}
ephemeral "vault_kv_secret_v2" "zitadel_iam_admin" {
mount = var.vault_kv_mount
name = var.zitadel_iam_admin_vault_path
}
locals {
zitadel_iam_admin_machine_key_json = ephemeral.vault_kv_secret_v2.zitadel_iam_admin.data[var.zitadel_iam_admin_machine_key_key]
}
provider "zitadel" {
domain = var.zitadel_domain
port = "443"
insecure = false
jwt_profile_json = local.zitadel_iam_admin_machine_key_json
}
variable "zitadel_domain" {
description = "External DNS name for the ZITADEL instance."
type = string
default = "auth.quorate.dev"
}
variable "zitadel_iam_admin_vault_path" {
description = "Vault KV path containing the ZITADEL IAM admin machine-key JSON."
type = string
default = "zitadel/iam-admin"
}
variable "zitadel_iam_admin_machine_key_key" {
description = "Key in zitadel_iam_admin_vault_path containing the ZITADEL IAM admin machine-key JSON."
type = string
default = "iam-admin.json"
}

View file

@ -0,0 +1,67 @@
locals {
quay_oidc_redirect_uri = "${trim(var.quay_base_url, "/")}/oauth2/zitadel/callback"
quay_oidc_redirect_uris = [
local.quay_oidc_redirect_uri,
"${local.quay_oidc_redirect_uri}/attach",
"${local.quay_oidc_redirect_uri}/cli",
]
quay_zitadel_roles = {
quay_user = {
display_name = "Quay user"
group = "Quay"
}
quay_admin = {
display_name = "Quay administrator"
group = "Quay"
}
}
quay_zitadel_role_keys = keys(local.quay_zitadel_roles)
}
resource "zitadel_project_role" "quay" {
for_each = local.quay_zitadel_roles
org_id = zitadel_org.primary.id
project_id = zitadel_project.services.id
role_key = each.key
display_name = each.value.display_name
group = each.value.group
}
resource "zitadel_application_oidc" "quay" {
org_id = zitadel_org.primary.id
project_id = zitadel_project.services.id
name = "Quay"
redirect_uris = local.quay_oidc_redirect_uris
response_types = ["OIDC_RESPONSE_TYPE_CODE"]
grant_types = ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE", "OIDC_GRANT_TYPE_REFRESH_TOKEN"]
post_logout_redirect_uris = [trim(var.quay_base_url, "/")]
app_type = "OIDC_APP_TYPE_WEB"
auth_method_type = "OIDC_AUTH_METHOD_TYPE_BASIC"
version = "OIDC_VERSION_1_0"
clock_skew = "0s"
dev_mode = false
access_token_type = "OIDC_TOKEN_TYPE_BEARER"
access_token_role_assertion = false
id_token_role_assertion = true
id_token_userinfo_assertion = true
additional_origins = []
skip_native_app_success_page = false
}
resource "vault_kv_secret_v2" "quay_oidc" {
mount = var.vault_kv_mount
name = var.quay_oidc_vault_path
data_json = jsonencode({
client_id = zitadel_application_oidc.quay.client_id
client_secret = zitadel_application_oidc.quay.client_secret
discovery_url = "https://${var.zitadel_domain}/.well-known/openid-configuration"
redirect_uri = local.quay_oidc_redirect_uri
})
# Vault is source of truth after first write. OIDC credential rotation is explicit.
lifecycle { ignore_changes = [data_json] }
}

View file

@ -0,0 +1,46 @@
locals {
quay_personas = {
candidates-bot = {
display_name = "Quay Candidates Bot"
email = "[email protected]"
last_name = "Candidates"
}
mirror-bot = {
display_name = "Quay Mirror Bot"
email = "[email protected]"
last_name = "Mirror"
}
}
}
ephemeral "vault_kv_secret_v2" "quay_persona" {
for_each = local.quay_personas
mount = var.vault_kv_mount
name = "quay/personas/${each.key}"
}
resource "zitadel_human_user" "quay_persona" {
for_each = local.quay_personas
org_id = zitadel_org.primary.id
user_name = each.key
first_name = "Quay"
last_name = each.value.last_name
display_name = each.value.display_name
email = each.value.email
is_email_verified = true
initial_password = ephemeral.vault_kv_secret_v2.quay_persona[each.key].data["password"]
initial_skip_password_change = true
}
resource "zitadel_user_grant" "quay_persona" {
for_each = local.quay_personas
org_id = zitadel_org.primary.id
project_id = zitadel_project.services.id
user_id = zitadel_human_user.quay_persona[each.key].id
role_keys = ["quay_user"]
depends_on = [zitadel_project_role.quay]
}

View file

@ -0,0 +1,75 @@
resource "zitadel_project" "services" {
org_id = zitadel_org.primary.id
name = var.zitadel_project_name
project_role_assertion = true
project_role_check = true
has_project_check = false
private_labeling_setting = "PRIVATE_LABELING_SETTING_UNSPECIFIED"
}
resource "zitadel_action" "service_roles_claim" {
org_id = zitadel_org.primary.id
name = "service_roles_claim"
timeout = "10s"
allowed_to_fail = true
script = <<-EOT
function service_roles_claim(ctx, api) {
api.v1.claims.setClaim('zitadel_action_ran', 'true');
var grants = ctx.v1.user && ctx.v1.user.grants;
if (!grants || !grants.grants || grants.count == 0) {
api.v1.claims.setClaim('groups', []);
return;
}
var roles = [];
var serviceRoles = [
'forgejo_user',
'forgejo_admin',
'forgejo_restricted',
'grafana_viewer',
'grafana_editor',
'grafana_admin',
'frigate_viewer',
'frigate_admin',
'longhorn_admin',
'stalwart_admin'
];
grants.grants.forEach(function(grant) {
if (grant.roles && Array.isArray(grant.roles)) {
grant.roles.forEach(function(role) {
if (serviceRoles.indexOf(role) !== -1 && roles.indexOf(role) === -1) {
roles.push(role);
}
});
}
});
var hasForgejoRole = roles.some(function(role) {
return role.indexOf('forgejo_') === 0;
});
if (hasForgejoRole && roles.indexOf('forgejo_login') === -1) {
roles.push('forgejo_login');
}
api.v1.claims.setClaim('groups', roles);
}
EOT
depends_on = [zitadel_project_role.forgejo, zitadel_project_role.frigate, zitadel_project_role.grafana, zitadel_project_role.longhorn, zitadel_project_role.stalwart]
}
resource "zitadel_trigger_actions" "service_roles_claim_userinfo" {
org_id = zitadel_org.primary.id
flow_type = "FLOW_TYPE_CUSTOMISE_TOKEN"
trigger_type = "TRIGGER_TYPE_PRE_USERINFO_CREATION"
action_ids = [zitadel_action.service_roles_claim.id]
}
resource "zitadel_trigger_actions" "service_roles_claim_access_token" {
org_id = zitadel_org.primary.id
flow_type = "FLOW_TYPE_CUSTOMISE_TOKEN"
trigger_type = "TRIGGER_TYPE_PRE_ACCESS_TOKEN_CREATION"
action_ids = [zitadel_action.service_roles_claim.id]
}

View file

@ -0,0 +1,7 @@
resource "zitadel_webkey" "eddsa" {
ed25519 {}
}
resource "zitadel_active_webkey" "signing" {
key_id = zitadel_webkey.eddsa.id
}

View file

@ -0,0 +1,187 @@
locals {
account_login_uri = "${trim(var.stalwart_account_webui_base_url, "/")}/account/oauth/callback"
account_logout_uri = "${trim(var.stalwart_account_webui_base_url, "/")}/account/oauth/logout"
stalwart_zitadel_roles = {
stalwart_admin = {
display_name = "Stalwart administrator"
group = "Stalwart"
}
stalwart_controller = {
display_name = "Stalwart configuration controller"
group = "Stalwart"
}
stalwart_submitter = {
display_name = "Stalwart alert submitter"
group = "Stalwart"
}
stalwart_notifier = {
display_name = "Stalwart notification sender"
group = "Stalwart"
}
stalwart_test = {
display_name = "Stalwart authenticated client test"
group = "Stalwart"
}
}
stalwart_zitadel_role_keys = keys(local.stalwart_zitadel_roles)
}
resource "zitadel_application_oidc" "stalwart_account_webui" {
org_id = zitadel_org.primary.id
project_id = zitadel_project.stalwart.id
name = "Mail app passwords"
redirect_uris = [local.account_login_uri]
response_types = ["OIDC_RESPONSE_TYPE_CODE"]
grant_types = ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE"]
post_logout_redirect_uris = [local.account_logout_uri]
app_type = "OIDC_APP_TYPE_USER_AGENT"
auth_method_type = "OIDC_AUTH_METHOD_TYPE_NONE"
version = "OIDC_VERSION_1_0"
clock_skew = "0s"
dev_mode = false
access_token_type = "OIDC_TOKEN_TYPE_BEARER"
access_token_role_assertion = true
id_token_role_assertion = true
id_token_userinfo_assertion = true
}
resource "zitadel_project" "stalwart" {
org_id = zitadel_org.primary.id
name = var.stalwart_project_name
project_role_assertion = true
project_role_check = true
has_project_check = true
private_labeling_setting = "PRIVATE_LABELING_SETTING_UNSPECIFIED"
}
resource "zitadel_project_role" "stalwart" {
for_each = local.stalwart_zitadel_roles
org_id = zitadel_org.primary.id
project_id = zitadel_project.stalwart.id
role_key = each.key
display_name = each.value.display_name
group = each.value.group
}
resource "zitadel_machine_user" "stalwart_controller" {
org_id = zitadel_org.primary.id
user_name = "stalwart-controller"
name = "Stalwart configuration controller"
description = "Credentialless Stalwart desired-state owner"
access_token_type = "ACCESS_TOKEN_TYPE_JWT"
with_secret = true
}
resource "zitadel_machine_user" "stalwart_sender" {
org_id = zitadel_org.primary.id
user_name = "stalwart-submitter"
name = "Stalwart alert sender"
description = "Zitadel-owned Stalwart cluster-local SMTP identity"
access_token_type = "ACCESS_TOKEN_TYPE_JWT"
with_secret = true
}
resource "zitadel_machine_user" "stalwart_notifier" {
org_id = zitadel_org.primary.id
user_name = "stalwart-notifier"
name = "Stalwart notification sender"
description = "Zitadel-owned Stalwart notification identity"
access_token_type = "ACCESS_TOKEN_TYPE_JWT"
with_secret = true
}
resource "zitadel_machine_user" "stalwart_test" {
org_id = zitadel_org.primary.id
user_name = "stalwart-test-client"
name = "Stalwart authenticated client test"
description = "Zitadel-owned authenticated mail client test identity"
access_token_type = "ACCESS_TOKEN_TYPE_JWT"
with_secret = true
}
resource "zitadel_user_grant" "stalwart_admin" {
org_id = zitadel_org.primary.id
project_id = zitadel_project.stalwart.id
user_id = var.stalwart_admin_zitadel_user_id
role_keys = ["stalwart_admin"]
depends_on = [zitadel_project_role.stalwart]
}
resource "zitadel_user_grant" "stalwart_controller" {
org_id = zitadel_org.primary.id
project_id = zitadel_project.stalwart.id
user_id = zitadel_machine_user.stalwart_controller.id
role_keys = ["stalwart_controller"]
depends_on = [zitadel_project_role.stalwart]
}
resource "zitadel_user_grant" "stalwart_sender" {
org_id = zitadel_org.primary.id
project_id = zitadel_project.stalwart.id
user_id = zitadel_machine_user.stalwart_sender.id
role_keys = ["stalwart_submitter"]
depends_on = [zitadel_project_role.stalwart]
}
resource "zitadel_user_grant" "stalwart_notifier" {
org_id = zitadel_org.primary.id
project_id = zitadel_project.stalwart.id
user_id = zitadel_machine_user.stalwart_notifier.id
role_keys = ["stalwart_notifier"]
depends_on = [zitadel_project_role.stalwart]
}
resource "zitadel_user_grant" "stalwart_test" {
org_id = zitadel_org.primary.id
project_id = zitadel_project.stalwart.id
user_id = zitadel_machine_user.stalwart_test.id
role_keys = ["stalwart_test"]
depends_on = [zitadel_project_role.stalwart]
}
resource "vault_kv_secret_v2" "stalwart_oidc" {
mount = var.vault_kv_mount
name = var.stalwart_oidc_vault_path
data_json = jsonencode({
account_client_id = zitadel_application_oidc.stalwart_account_webui.client_id
audience = zitadel_project.stalwart.id
client_id = zitadel_machine_user.stalwart_controller.client_id
client_secret = zitadel_machine_user.stalwart_controller.client_secret
controller_subject = zitadel_machine_user.stalwart_controller.id
human_admin_subject = var.stalwart_admin_zitadel_user_id
issuer_url = "https://${var.zitadel_domain}"
notifier_client_id = zitadel_machine_user.stalwart_notifier.client_id
notifier_client_secret = zitadel_machine_user.stalwart_notifier.client_secret
notifier_subject = zitadel_machine_user.stalwart_notifier.id
sender_client_id = zitadel_machine_user.stalwart_sender.client_id
sender_client_secret = zitadel_machine_user.stalwart_sender.client_secret
sender_subject = zitadel_machine_user.stalwart_sender.id
test_client_id = zitadel_machine_user.stalwart_test.client_id
test_client_secret = zitadel_machine_user.stalwart_test.client_secret
test_subject = zitadel_machine_user.stalwart_test.id
})
}
data "vault_kv_secret_v2" "stalwart_zitadel_notification" {
mount = var.vault_kv_mount
name = var.stalwart_zitadel_notification_vault_path
}
resource "zitadel_email_provider_smtp" "stalwart" {
sender_address = "[email protected]"
sender_name = "Quorate"
host = "stalwart-submission.stalwart.svc.cluster.local:587"
tls = false
user = "[email protected]"
password = data.vault_kv_secret_v2.stalwart_zitadel_notification.data["smtp_password"]
description = "Stalwart cluster-local notification submission"
set_active = true
}

View file

@ -0,0 +1,259 @@
variable "zitadel_org_name" {
description = "Name of the primary service organization to create in ZITADEL."
type = string
default = "Quorate"
}
variable "zitadel_org_id" {
description = "Optional stable custom ID for the primary service organization. Leave null to let ZITADEL generate one."
type = string
default = null
}
variable "zitadel_project_name" {
description = "Name of the ZITADEL project that groups service clients."
type = string
default = "Services"
}
variable "forgejo_base_url" {
description = "Canonical Forgejo URL. This must match the Forgejo HTTPRoute hostname and ROOT_URL."
type = string
default = "https://forge.quorate.internal"
}
variable "forgejo_oauth_provider_name" {
description = "Forgejo OAuth2 authentication source name; this value is part of the callback URL."
type = string
default = "Zitadel"
}
variable "vault_kv_mount" {
description = "Vault KV v2 mount where generated service integration secrets are mirrored."
type = string
default = "secret"
}
variable "forgejo_oidc_vault_path" {
description = "Vault KV path for Forgejo's ZITADEL OIDC client material."
type = string
default = "forgejo/oidc/zitadel"
}
variable "stalwart_oidc_vault_path" {
description = "Vault KV path containing Stalwart's ZITADEL resource-server metadata."
type = string
default = "stalwart/oidc/zitadel"
}
variable "stalwart_zitadel_notification_vault_path" {
description = "Vault KV path containing Stalwart's ZITADEL notification SMTP credentials."
type = string
default = "stalwart/zitadel-notification"
}
variable "stalwart_project_name" {
description = "Name of the dedicated ZITADEL project for Stalwart tokens."
type = string
default = "Stalwart"
}
variable "stalwart_admin_zitadel_user_id" {
description = "ZITADEL human user ID granted Stalwart administrative access."
type = string
default = "********"
}
variable "forgejo_public_base_url" {
description = "Canonical authenticated URL for the public Forgejo database."
type = string
default = "https://git.quorate.dev"
}
variable "forgejo_public_oauth_provider_name" {
description = "Public Forgejo OAuth2 authentication source name."
type = string
default = "Zitadel"
}
variable "forgejo_public_oidc_vault_path" {
description = "Vault KV path for public Forgejo's ZITADEL OIDC client material."
type = string
default = "forgejo-public/oidc/zitadel"
}
variable "service_admin_zitadel_user_id" {
description = "ZITADEL human user ID granted administrative access to GitOps-managed service applications."
type = string
default = "********"
}
variable "service_admin_zitadel_role_keys" {
description = "Service project role keys assigned to service_admin_zitadel_user_id."
type = set(string)
default = [
"forgejo_admin",
"frigate_admin",
"grafana_admin",
"longhorn_admin",
"quay_admin",
]
validation {
condition = alltrue([
for role_key in var.service_admin_zitadel_role_keys : contains([
"forgejo_user",
"forgejo_admin",
"forgejo_restricted",
"frigate_viewer",
"frigate_admin",
"grafana_viewer",
"grafana_editor",
"grafana_admin",
"longhorn_admin",
"quay_user",
"quay_admin",
], role_key)
])
error_message = "Service admin ZITADEL grants may only use known Forgejo, Frigate, Grafana, Longhorn, and Quay role keys."
}
}
variable "frigate_base_url" {
description = "Canonical Frigate URL. This must match the Frigate HTTPRoute hostname and oauth2-proxy redirect URL."
type = string
default = "https://frigate.quorate.internal"
}
variable "frigate_zitadel_user_grants" {
description = "Map of ZITADEL user IDs to Frigate role keys to grant. Valid role keys are frigate_viewer and frigate_admin."
type = map(set(string))
default = {}
validation {
condition = alltrue(flatten([
for _, role_keys in var.frigate_zitadel_user_grants : [
for role_key in role_keys : contains([
"frigate_viewer",
"frigate_admin",
], role_key)
]
]))
error_message = "Frigate ZITADEL grants may only use frigate_viewer and frigate_admin."
}
}
variable "forgejo_zitadel_user_grants" {
description = "Map of ZITADEL user IDs to Forgejo role keys to grant. Valid role keys are forgejo_user, forgejo_admin, and forgejo_restricted."
type = map(set(string))
default = {}
validation {
condition = alltrue(flatten([
for _, role_keys in var.forgejo_zitadel_user_grants : [
for role_key in role_keys : contains([
"forgejo_user",
"forgejo_admin",
"forgejo_restricted",
], role_key)
]
]))
error_message = "Forgejo ZITADEL grants may only use forgejo_user, forgejo_admin, and forgejo_restricted."
}
}
variable "grafana_base_url" {
description = "Canonical Grafana URL. This must match the Grafana HTTPRoute hostname and root URL."
type = string
default = "https://grafana.quorate.internal"
}
variable "grafana_oidc_vault_path" {
description = "Vault KV path for Grafana's ZITADEL OIDC client material."
type = string
default = "grafana/oidc/zitadel"
}
variable "grafana_zitadel_user_grants" {
description = "Map of ZITADEL user IDs to Grafana role keys to grant. Valid role keys are grafana_viewer, grafana_editor, and grafana_admin."
type = map(set(string))
default = {}
validation {
condition = alltrue(flatten([
for _, role_keys in var.grafana_zitadel_user_grants : [
for role_key in role_keys : contains([
"grafana_viewer",
"grafana_editor",
"grafana_admin",
], role_key)
]
]))
error_message = "Grafana ZITADEL grants may only use grafana_viewer, grafana_editor, and grafana_admin."
}
}
variable "longhorn_base_url" {
description = "Canonical Longhorn URL. This must match the Longhorn HTTPRoute hostname and oauth2-proxy redirect URL."
type = string
default = "https://longhorn.quorate.internal"
}
variable "stalwart_account_webui_base_url" {
description = "Public Stalwart account WebUI base URL."
type = string
default = "https://mail.quorate.dev"
}
variable "longhorn_oauth2_proxy_vault_path" {
description = "Vault KV path for Longhorn's oauth2-proxy OIDC client material."
type = string
default = "longhorn/oauth2-proxy"
}
variable "longhorn_zitadel_user_grants" {
description = "Map of ZITADEL user IDs to Longhorn role keys to grant. Valid role key is longhorn_admin."
type = map(set(string))
default = {}
validation {
condition = alltrue(flatten([
for _, role_keys in var.longhorn_zitadel_user_grants : [
for role_key in role_keys : contains([
"longhorn_admin",
], role_key)
]
]))
error_message = "Longhorn ZITADEL grants may only use longhorn_admin."
}
}
variable "quay_base_url" {
description = "Canonical Quay URL. This must match the Quay HTTPRoute hostname and SERVER_HOSTNAME."
type = string
default = "https://registry.quorate.internal"
}
variable "quay_oidc_vault_path" {
description = "Vault KV path for Quay's ZITADEL OIDC client material."
type = string
default = "quay/oidc/zitadel"
}
variable "quay_zitadel_user_grants" {
description = "Map of ZITADEL user IDs to Quay role keys to grant. Valid role keys are quay_user and quay_admin."
type = map(set(string))
default = {}
validation {
condition = alltrue(flatten([
for _, role_keys in var.quay_zitadel_user_grants : [
for role_key in role_keys : contains([
"quay_user",
"quay_admin",
], role_key)
]
]))
error_message = "Quay ZITADEL grants may only use quay_user and quay_admin."
}
}