mirror of
https://github.com/immich-app/devtools.git
synced 2026-09-30 13:23:00 +08:00
fix: route zitadel connections through stable base domain (#1847)
This commit is contained in:
@@ -17,6 +17,7 @@ export TF_VAR_zitadel_github_client_secret="op://tf/GITHUB_OAUTH_APP_IMMICH_ZITA
|
||||
export TF_VAR_futo_internal_zitadel_github_client_id="op://tf/IMMICH_GITHUB_OAUTH_APP_INTERNAL_FUTO_ZITADEL_CLIENT_ID/password"
|
||||
export TF_VAR_futo_internal_zitadel_github_client_secret="op://tf/IMMICH_GITHUB_OAUTH_APP_INTERNAL_FUTO_ZITADEL_CLIENT_SECRET/password"
|
||||
export TF_VAR_futo_zitadel_profile_json="op://tf/FUTO_ZITADEL_PROFILE_JSON/password"
|
||||
export TF_VAR_futo_zitadel_base_domain="op://tf/FUTO_ZITADEL_BASE_DOMAIN/password"
|
||||
export TF_VAR_zitadel_gitlab_client_id="op://tf/GITLAB_OAUTH_APP_FUTO_ZITADEL_CLIENT_ID/password"
|
||||
export TF_VAR_zitadel_gitlab_client_secret="op://tf/GITLAB_OAUTH_APP_FUTO_ZITADEL_CLIENT_SECRET/password"
|
||||
export TF_VAR_zitadel_gitlab_issuer="op://tf/GITLAB_OAUTH_APP_FUTO_ZITADEL_ISSUER/password"
|
||||
|
||||
@@ -32,6 +32,7 @@ before Terraform can run. Do this in the ZITADEL Cloud console and 1Password:
|
||||
seeds stub items in `yucca_tf_${env}_manual` with `REPLACE_ME` passwords
|
||||
for:
|
||||
- `CUSTOMER_ZITADEL_DOMAIN`
|
||||
- `CUSTOMER_ZITADEL_BASE_DOMAIN`
|
||||
- `CUSTOMER_ZITADEL_PROFILE_JSON`
|
||||
- `CUSTOMER_ZITADEL_SMTP_HOST`
|
||||
- `CUSTOMER_ZITADEL_SMTP_USER`
|
||||
@@ -39,9 +40,17 @@ before Terraform can run. Do this in the ZITADEL Cloud console and 1Password:
|
||||
- `CUSTOMER_ZITADEL_SMTP_SENDER_ADDRESS`
|
||||
|
||||
Replace each stub password in `yucca_tf_${env}_manual` with the real value
|
||||
(`CUSTOMER_ZITADEL_DOMAIN` = the instance URL `<name>.zitadel.cloud`, profile
|
||||
JSON from step 3, SMTP credentials for the chosen provider, and sender
|
||||
address e.g. `no-reply@futo.cloud`).
|
||||
(`CUSTOMER_ZITADEL_DOMAIN` = the public-facing auth URL — prod: the
|
||||
`auth.futo.cloud` vanity domain, dev/staging: the generated
|
||||
`<name>.zitadel.cloud` URL — consumed by app-side config, not this module;
|
||||
`CUSTOMER_ZITADEL_BASE_DOMAIN` = the generated `<name>.zitadel.cloud` instance
|
||||
host as a **bare hostname** (no `https://`, no trailing slash) in every env,
|
||||
which this module both connects through (provider + hosted login) and targets
|
||||
with the `auth.futo.cloud` CNAME, so the connection never depends on the
|
||||
mutable vanity domain. Stored bare to match the internal instance's
|
||||
`FUTO_ZITADEL_BASE_DOMAIN`. Profile JSON from step 3, SMTP
|
||||
credentials for the chosen provider, and sender address e.g.
|
||||
`no-reply@futo.cloud`).
|
||||
Re-apply `shared/1password/futo-account` so the copy-secrets module
|
||||
mirrors each value into the `yucca_tf_${env}` vault that this module
|
||||
reads from via `data "onepassword_item"` lookups at plan/apply time. No
|
||||
|
||||
@@ -2,9 +2,13 @@ data "onepassword_vault" "tf_env" {
|
||||
name = "yucca_tf_${var.env}"
|
||||
}
|
||||
|
||||
data "onepassword_item" "customer_zitadel_domain" {
|
||||
# NOTE: CUSTOMER_ZITADEL_DOMAIN (the public auth.futo.cloud vanity domain) is
|
||||
# intentionally not read here — this module connects to the instance via the
|
||||
# stable <name>.zitadel.cloud base domain below. The vanity domain remains the
|
||||
# public-facing auth URL consumed by app-side config.
|
||||
data "onepassword_item" "customer_zitadel_base_domain" {
|
||||
vault = data.onepassword_vault.tf_env.uuid
|
||||
title = "CUSTOMER_ZITADEL_DOMAIN"
|
||||
title = "CUSTOMER_ZITADEL_BASE_DOMAIN"
|
||||
}
|
||||
|
||||
data "onepassword_item" "customer_zitadel_profile_json" {
|
||||
|
||||
@@ -13,22 +13,20 @@ data "cloudflare_zone" "futo_cloud" {
|
||||
}
|
||||
}
|
||||
|
||||
locals {
|
||||
# CUSTOMER_ZITADEL_DOMAIN is stored as a URL (https://host/); Cloudflare needs
|
||||
# a bare hostname as CNAME content.
|
||||
customer_zitadel_host = trimsuffix(trimprefix(data.onepassword_item.customer_zitadel_domain.password, "https://"), "/")
|
||||
}
|
||||
|
||||
# Prod only: auth.futo.cloud -> the prod ZITADEL Cloud instance host from
|
||||
# CUSTOMER_ZITADEL_DOMAIN. dev/staging use the generated <name>.zitadel.cloud URL
|
||||
# directly with no custom domain. DNS-only so ZITADEL terminates TLS.
|
||||
# Prod only: auth.futo.cloud -> the prod ZITADEL Cloud instance's base
|
||||
# <name>.zitadel.cloud host from CUSTOMER_ZITADEL_BASE_DOMAIN, which is stored as
|
||||
# a bare hostname (no scheme) — exactly what a CNAME wants. This is NOT the
|
||||
# auth.futo.cloud vanity domain that CUSTOMER_ZITADEL_DOMAIN holds; pointing the
|
||||
# record at the vanity domain would make the CNAME reference itself. dev/staging
|
||||
# have no custom domain and use the generated URL directly. DNS-only so ZITADEL
|
||||
# terminates TLS.
|
||||
resource "cloudflare_dns_record" "customer_auth" {
|
||||
count = var.env == "prod" ? 1 : 0
|
||||
|
||||
zone_id = data.cloudflare_zone.futo_cloud.id
|
||||
name = "auth.futo.cloud"
|
||||
type = "CNAME"
|
||||
content = local.customer_zitadel_host
|
||||
content = data.onepassword_item.customer_zitadel_base_domain.password
|
||||
ttl = 1
|
||||
proxied = false
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ resource "null_resource" "hosted_login_translations" {
|
||||
triggers = {
|
||||
translations_hash = filemd5(local.hosted_login_translations_file)
|
||||
script_hash = filemd5(local.hosted_login_translations_script)
|
||||
domain = data.onepassword_item.customer_zitadel_domain.password
|
||||
domain = data.onepassword_item.customer_zitadel_base_domain.password
|
||||
locale = local.hosted_login_translations_locale
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ resource "null_resource" "hosted_login_translations" {
|
||||
command = local.hosted_login_translations_script
|
||||
|
||||
environment = {
|
||||
ZITADEL_DOMAIN = data.onepassword_item.customer_zitadel_domain.password
|
||||
# Connect to the API via the stable base domain, not the vanity domain.
|
||||
ZITADEL_DOMAIN = data.onepassword_item.customer_zitadel_base_domain.password
|
||||
ZITADEL_PROFILE_JSON = data.onepassword_item.customer_zitadel_profile_json.password
|
||||
TRANSLATIONS_FILE = local.hosted_login_translations_file
|
||||
LOCALE = local.hosted_login_translations_locale
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Connect via the stable <name>.zitadel.cloud base domain, not the auth.futo.cloud
|
||||
# vanity domain (CUSTOMER_ZITADEL_DOMAIN) which is an alias that could change.
|
||||
provider "zitadel" {
|
||||
domain = data.onepassword_item.customer_zitadel_domain.password
|
||||
domain = data.onepassword_item.customer_zitadel_base_domain.password
|
||||
insecure = false
|
||||
jwt_profile_json = data.onepassword_item.customer_zitadel_profile_json.password
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ resource "cloudflare_worker_version" "zitadel_actions" {
|
||||
}]
|
||||
|
||||
bindings = [
|
||||
{ name = "ZITADEL_DOMAIN", type = "plain_text", text = "auth.internal.futo.org" },
|
||||
{ name = "ZITADEL_DOMAIN", type = "plain_text", text = var.futo_zitadel_base_domain },
|
||||
{ name = "GITHUB_IDP_ID", type = "plain_text", text = zitadel_idp_github.github.id },
|
||||
{ name = "GITLAB_IDP_ID", type = "plain_text", text = zitadel_idp_gitlab_self_hosted.gitlab.id },
|
||||
{ name = "ZITADEL_TOKEN", type = "secret_text", text = zitadel_personal_access_token.zitadel_actions.token },
|
||||
|
||||
@@ -53,7 +53,7 @@ resource "zitadel_default_privacy_policy" "default" {
|
||||
locals {
|
||||
hosted_login_translations_file = "${path.module}/translations/en.json"
|
||||
hosted_login_translations_script = "${path.module}/scripts/set-hosted-login-translations.sh"
|
||||
hosted_login_translations_domain = "auth.internal.futo.org"
|
||||
hosted_login_translations_domain = var.futo_zitadel_base_domain
|
||||
hosted_login_translations_locale = "en"
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ resource "terraform_data" "idp_link" {
|
||||
idp_id = each.value.idp_id
|
||||
external_user_id = each.value.external_user_id
|
||||
user_name = each.value.user_name
|
||||
domain = "auth.internal.futo.org"
|
||||
domain = var.futo_zitadel_base_domain
|
||||
token = zitadel_personal_access_token.zitadel_actions.token
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ resource "cloudflare_worker_version" "outline_role_sync" {
|
||||
|
||||
bindings = [
|
||||
{ name = "OUTLINE_BASE_URL", type = "plain_text", text = "https://outline.immich.cloud" },
|
||||
{ name = "ZITADEL_BASE_URL", type = "plain_text", text = "https://auth.internal.futo.org" },
|
||||
{ name = "ZITADEL_BASE_URL", type = "plain_text", text = "https://${var.futo_zitadel_base_domain}" },
|
||||
{ name = "ZITADEL_OUTLINE_PROJECT_ID", type = "plain_text", text = zitadel_project.projects["Outline"].id },
|
||||
{ name = "ZITADEL_OUTLINE_PROJECT_ROLES", type = "plain_text", text = join(",", local.outline_role_keys) },
|
||||
{ name = "ZITADEL_SERVICE_ACCOUNT_TOKEN", type = "secret_text", text = zitadel_personal_access_token.outline_role_sync.token },
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Connect via the stable <name>.zitadel.cloud base domain, not the
|
||||
# auth.internal.futo.org custom domain which is a mutable alias.
|
||||
provider "zitadel" {
|
||||
domain = "auth.internal.futo.org"
|
||||
domain = var.futo_zitadel_base_domain
|
||||
insecure = false
|
||||
jwt_profile_json = var.futo_zitadel_profile_json
|
||||
}
|
||||
|
||||
@@ -8,6 +8,11 @@ variable "op_connect_token" {
|
||||
variable "zitadel_profile_json" {}
|
||||
variable "futo_zitadel_profile_json" {}
|
||||
|
||||
# The stable <name>.zitadel.cloud base host for the FUTO internal instance. Used
|
||||
# for all API connections (provider, hosted-login + idp-link scripts) instead of
|
||||
# the auth.internal.futo.org custom domain, which is a mutable alias.
|
||||
variable "futo_zitadel_base_domain" {}
|
||||
|
||||
variable "users_data_file_path" {
|
||||
description = "The path to the JSON file containing user data. This path should be resolvable from the Terragrunt execution directory or be an absolute path."
|
||||
type = string
|
||||
|
||||
Reference in New Issue
Block a user