diff --git a/infra/modules/argocd/main.tf b/infra/modules/argocd/main.tf index 091ab39..805e029 100644 --- a/infra/modules/argocd/main.tf +++ b/infra/modules/argocd/main.tf @@ -59,6 +59,7 @@ resource "helm_release" "argocd" { oauth_client_id = var.oauth_client_id, oauth_client_secret = var.oauth_client_secret, oauth_redirect_uri = var.oauth_redirect_uri + tls = var.tls }) ] } diff --git a/infra/modules/argocd/values.yaml b/infra/modules/argocd/values.yaml index d838682..0041e9f 100644 --- a/infra/modules/argocd/values.yaml +++ b/infra/modules/argocd/values.yaml @@ -42,14 +42,27 @@ server: hostname: ${ service_uri } annotations: kubernetes.io/ingress.class: traefik + %{ if tls } traefik.ingress.kubernetes.io/router.entrypoints: web,websecure traefik.ingress.kubernetes.io/router.middlewares: default-redirect-to-https@kubernetescrd,default-preserve-host-headers@kubernetescrd + %{ else } + traefik.ingress.kubernetes.io/router.entrypoints: web + traefik.ingress.kubernetes.io/router.middlewares: default-preserve-host-headers@kubernetescrd + %{ endif } + %{ if tls } extraTls: - hosts: - ${ service_uri } secretName: argocd-tls + %{ endif } config: + rbac: | + scopes: '[groups]' + "policy.csv": | + g, admin, role:admin + g, user, role:readonly + "policy.default": '' %{ if oauth_uri != null } dex.config: | connectors: @@ -57,9 +70,9 @@ server: id: oidc name: OIDC config: - issuer: ${ oauth_issuer } - clientID: ${ oauth_client_id } - clientSecret: ${ oauth_client_secret } + issuer: "${ oauth_issuer }" + clientID: "${ oauth_client_id }" + clientSecret: "${ oauth_client_secret }" insecureSkipEmailVerified: true insecureEnableGroups: true scopes: @@ -67,6 +80,7 @@ server: - email - openid - groups + logoutURL: "${ oauth_redirect_uri }" claimMapping: name: fullName # ArgoCD expects 'name', FusionAuth provides 'fullName' preferred_username: email diff --git a/infra/modules/argocd/variables.tf b/infra/modules/argocd/variables.tf index 69cc207..4311573 100644 --- a/infra/modules/argocd/variables.tf +++ b/infra/modules/argocd/variables.tf @@ -64,3 +64,8 @@ variable "oauth_redirect_uri" { description = "OAuth redirect URI" default = null } + +variable "tls" { + type = bool + default = false +} diff --git a/infra/modules/minio/main.tf b/infra/modules/minio/main.tf index ae332c1..cf2be2f 100644 --- a/infra/modules/minio/main.tf +++ b/infra/modules/minio/main.tf @@ -67,3 +67,13 @@ output "installed" { value = true depends_on = [helm_release.minio] } + +output "access_key" { + value = random_password.minio_access_key.result + sensitive = true +} + +output "secret_key" { + value = random_password.minio_secret_key.result + sensitive = true +} diff --git a/infra/modules/mongodb/main.tf b/infra/modules/mongodb/main.tf index 5b16d72..eb5b271 100644 --- a/infra/modules/mongodb/main.tf +++ b/infra/modules/mongodb/main.tf @@ -56,3 +56,8 @@ output "installed" { value = true depends_on = [helm_release.mongodb] } + +output "connection_string" { + value = format("mongodb://%s:%s@%s:%s/%s?", "root", random_password.mongodb_root_password.result, "mongodb-headless.mongodb.svc.cluster.local", "27017", "admin") + sensitive = true +} diff --git a/infra/modules/rabbitmq/main.tf b/infra/modules/rabbitmq/main.tf index ea2d5e1..36fd0a8 100644 --- a/infra/modules/rabbitmq/main.tf +++ b/infra/modules/rabbitmq/main.tf @@ -41,3 +41,8 @@ output "installed" { value = true depends_on = [helm_release.rabbitmq] } + +output "connection_string" { + value = "rabbitmq://user:${random_password.password.result}@rabbitmq-headless.${var.namespace}.svc.cluster.local:5672/" + sensitive = true +} diff --git a/infra/modules/zitadel/api-m2m-swagger/main.tf b/infra/modules/zitadel/api-m2m-swagger/main.tf new file mode 100644 index 0000000..325cb2d --- /dev/null +++ b/infra/modules/zitadel/api-m2m-swagger/main.tf @@ -0,0 +1,117 @@ +terraform { + required_providers { + slugify = { + source = "public-cloud-wl/slugify" + version = "0.1.1" + } + } +} + +locals { + authority = "https://${var.zitadel_domain}" + slug_project = provider::slugify::slug(var.project) + slug_name = provider::slugify::slug(var.name) + + cluster = "${local.slug_project}.${var.cluster_domain}" + uri = "https://${local.slug_name}.${local.cluster}" +} + +module "zitadel_project_application_api" { + source = "../project/application/api" + wait_on = var.wait_on + + org_id = var.org_id + project_id = var.project_id + + name = "${var.name} API" +} + +module "zitadel_project_application_ua" { + source = "../project/application/user-agent" + wait_on = module.zitadel_project_application_api.installed + + org_id = var.org_id + project_id = var.project_id + + name = "${ var.name } (Swagger)" + + redirect_uris = ["${local.uri}/swagger/oauth2-redirect.html"] + post_logout_redirect_uris = [local.uri] +} + + +resource "kubernetes_secret" "user-agent" { + type = "Opaque" + depends_on = [module.zitadel_project_application_ua] + + metadata { + name = "${local.slug_name}-user-agent" + namespace = var.namespace + } + + data = { + "authority" = local.authority + "audience" = "urn:zitadel:iam:org:project:id:${ var.project_id }:aud" + "client_id" = module.zitadel_project_application_ua.client_id + } +} + +resource "kubernetes_secret" "api" { + type = "Opaque" + depends_on = [module.zitadel_project_application_api] + + metadata { + name = "${local.slug_name}-api" + namespace = var.namespace + } + + data = { + "authority" = local.authority + "client_id" = module.zitadel_project_application_api.client_id + "client_secret" = module.zitadel_project_application_api.client_secret + } +} + +module "zitadel_service_account" { + count = var.service_account ? 1 : 0 + wait_on = module.zitadel_project_application_api.installed + source = "../service-account" + + org_id = var.org_id + + user_name = "${local.slug_name}@${ local.cluster }" + name = "${var.name} @ ${var.project}" + + with_secret = true + access_token_type = "ACCESS_TOKEN_TYPE_JWT" +} + +module "zitadel_project_user_grant" { + count = var.service_account ? 1 : 0 + source = "../project/user-grant" + + org_id = var.org_id + + project_id = var.project_id + user_id = module.zitadel_service_account[0].user_id + + roles = var.roles +} + +resource "kubernetes_secret" "service-account" { + count = var.service_account ? 1 : 0 + type = "Opaque" + depends_on = [module.zitadel_service_account] + + metadata { + name = "${local.slug_name}-service-account" + namespace = var.namespace + } + + data = { + "authority" = local.authority + "audience" = "urn:zitadel:iam:org:project:id:${ var.project_id }:aud" + "client_id" = module.zitadel_service_account[count.index].client_id + "client_secret" = module.zitadel_service_account[count.index].client_secret + } +} diff --git a/infra/modules/zitadel/api-m2m-swagger/variables.tf b/infra/modules/zitadel/api-m2m-swagger/variables.tf new file mode 100644 index 0000000..6afa548 --- /dev/null +++ b/infra/modules/zitadel/api-m2m-swagger/variables.tf @@ -0,0 +1,44 @@ +variable "wait_on" { + type = any + description = "Resources to wait on" + default = true +} + +variable "org_id" { + type = string +} + +variable "project_id" { + type = string +} + +variable "name" { + type = string +} + +variable "project" { + type = string +} + + +variable "roles" { + type = list(string) + description = "Roles to be granted" +} + +variable "namespace" { + type = string +} + +variable "service_account" { + type = bool + default = true +} + +variable "zitadel_domain" { + type = string +} + +variable "cluster_domain" { + type = string +} diff --git a/infra/modules/zitadel/identity-provider/google/main.tf b/infra/modules/zitadel/identity-provider/google/main.tf index e116eeb..2dd838a 100644 --- a/infra/modules/zitadel/identity-provider/google/main.tf +++ b/infra/modules/zitadel/identity-provider/google/main.tf @@ -1,3 +1,11 @@ +terraform { + required_providers { + zitadel = { + source = "zitadel/zitadel" + } + } +} + resource "zitadel_org_idp_google" "default" { depends_on = [var.wait_on] org_id = var.org_id @@ -16,7 +24,7 @@ resource "zitadel_login_policy" "default" { depends_on = [zitadel_org_idp_google.default] org_id = var.org_id - user_login = true + user_login = false allow_register = true allow_external_idp = true force_mfa = false @@ -38,7 +46,37 @@ resource "zitadel_login_policy" "default" { disable_login_with_phone = true } +#resource "zitadel_action" "verify-email-from-google-idp" { +# org_id = var.org_id +# name = "trustEmailVerification" +# script = templatefile("${path.module}/verify-email.action.tftpl", { +# trusted_idp = zitadel_org_idp_google.default.id, +# }) +# allowed_to_fail = false +# timeout = "10s" +#} + +#resource "zitadel_trigger_actions" "verify-email-from-google-idp" { +# org_id = var.org_id +# flow_type = "FLOW_TYPE_EXTERNAL_AUTHENTICATION" +# trigger_type = "TRIGGER_TYPE_PRE_CREATION" +# action_ids = [zitadel_action.verify-email-from-google-idp.id] +#} +# +#resource "zitadel_trigger_actions" "internal" { +# org_id = var.org_id +# flow_type = "FLOW_TYPE_INTERNAL_AUTHENTICATION" +# trigger_type = "TRIGGER_TYPE_PRE_CREATION" +# action_ids = [zitadel_action.verify-email-from-google-idp.id] +#} + output "installed" { value = true - depends_on = [zitadel_org_idp_google.default, zitadel_login_policy.default] + depends_on = [ + zitadel_org_idp_google.default, zitadel_login_policy.default, + ] +} + +output "idp_id" { + value = zitadel_org_idp_google.default.id } diff --git a/infra/modules/zitadel/identity-provider/google/variables.tf b/infra/modules/zitadel/identity-provider/google/variables.tf index c648d93..dda30d5 100644 --- a/infra/modules/zitadel/identity-provider/google/variables.tf +++ b/infra/modules/zitadel/identity-provider/google/variables.tf @@ -1,20 +1,9 @@ -variable "domain" { - type = string - description = "Domain for the zitadel instance" - default = "localhost" -} - variable "wait_on" { type = any description = "Resources to wait on" default = true } -variable "jwt_profile_file" { - type = string - description = "Path to the jwt profile file" -} - variable "org_id" { type = string description = "Organisation Id" @@ -41,10 +30,14 @@ variable "options" { }) default = { scopes = ["openid", "profile", "email"], - is_linking_allowed = false + is_linking_allowed = true is_creation_allowed = true - is_auto_creation = false + is_auto_creation = true is_auto_update = true auto_linking = "AUTO_LINKING_OPTION_USERNAME" } } + +variable "domain" { + type = string +} diff --git a/infra/modules/zitadel/identity-provider/google/verify-email.action.tftpl b/infra/modules/zitadel/identity-provider/google/verify-email.action.tftpl new file mode 100644 index 0000000..8af6be4 --- /dev/null +++ b/infra/modules/zitadel/identity-provider/google/verify-email.action.tftpl @@ -0,0 +1,15 @@ +/** + * Set first and lastname of a user on just in time provisioning for okta. + * Useful if you like to fill the first and lastname with the name stored on okta, so the user doesn't have to fill himself. + * Also set email to verified, so the user doesn't get a verification email + * + * Flow: External Authentication, Trigger: Post Authentication + * + * @param ctx + * @param api + */ +let logger = require("zitadel/log") + +function trustEmailVerification(ctx, api) { + api.setEmailVerified(true); +} diff --git a/infra/modules/zitadel/project/application/api/main.tf b/infra/modules/zitadel/project/application/api/main.tf index dbd13e8..489bd93 100644 --- a/infra/modules/zitadel/project/application/api/main.tf +++ b/infra/modules/zitadel/project/application/api/main.tf @@ -1,3 +1,12 @@ +terraform { + required_providers { + zitadel = { + source = "zitadel/zitadel" + version = "2.0.2" + } + } +} + resource "zitadel_application_api" "default" { depends_on = [var.wait_on] diff --git a/infra/modules/zitadel/project/application/api/provider.tf b/infra/modules/zitadel/project/application/api/provider.tf deleted file mode 100644 index 653009c..0000000 --- a/infra/modules/zitadel/project/application/api/provider.tf +++ /dev/null @@ -1,14 +0,0 @@ -terraform { - required_providers { - zitadel = { - source = "zitadel/zitadel" - version = "2.0.2" - } - } -} - -provider "zitadel" { - domain = var.domain - insecure = "false" - jwt_profile_file = var.jwt_profile_file -} diff --git a/infra/modules/zitadel/project/application/api/variables.tf b/infra/modules/zitadel/project/application/api/variables.tf index 622dd71..f0c82e7 100644 --- a/infra/modules/zitadel/project/application/api/variables.tf +++ b/infra/modules/zitadel/project/application/api/variables.tf @@ -1,20 +1,9 @@ -variable "domain" { - type = string - description = "Domain for the zitadel instance" - default = "localhost" -} - variable "wait_on" { type = any description = "Resources to wait on" default = true } -variable "jwt_profile_file" { - type = string - description = "Path to the jwt profile file" -} - variable "org_id" { type = string description = "Organisation Id" diff --git a/infra/modules/zitadel/project/application/user-agent/main.tf b/infra/modules/zitadel/project/application/user-agent/main.tf index 4391c5b..923bef5 100644 --- a/infra/modules/zitadel/project/application/user-agent/main.tf +++ b/infra/modules/zitadel/project/application/user-agent/main.tf @@ -1,3 +1,12 @@ +terraform { + required_providers { + zitadel = { + source = "zitadel/zitadel" + version = "2.0.2" + } + } +} + resource "zitadel_application_oidc" "default" { depends_on = [var.wait_on] diff --git a/infra/modules/zitadel/project/application/user-agent/provider.tf b/infra/modules/zitadel/project/application/user-agent/provider.tf deleted file mode 100644 index 653009c..0000000 --- a/infra/modules/zitadel/project/application/user-agent/provider.tf +++ /dev/null @@ -1,14 +0,0 @@ -terraform { - required_providers { - zitadel = { - source = "zitadel/zitadel" - version = "2.0.2" - } - } -} - -provider "zitadel" { - domain = var.domain - insecure = "false" - jwt_profile_file = var.jwt_profile_file -} diff --git a/infra/modules/zitadel/project/application/user-agent/variables.tf b/infra/modules/zitadel/project/application/user-agent/variables.tf index 2459275..3220518 100644 --- a/infra/modules/zitadel/project/application/user-agent/variables.tf +++ b/infra/modules/zitadel/project/application/user-agent/variables.tf @@ -1,19 +1,9 @@ -variable "domain" { - type = string - description = "Domain for the zitadel instance" - default = "localhost" -} - variable "wait_on" { type = any description = "Resources to wait on" default = true } -variable "jwt_profile_file" { - type = string - description = "Path to the jwt profile file" -} variable "org_id" { type = string diff --git a/infra/modules/zitadel/project/application/web/main.tf b/infra/modules/zitadel/project/application/web/main.tf new file mode 100644 index 0000000..2bc0429 --- /dev/null +++ b/infra/modules/zitadel/project/application/web/main.tf @@ -0,0 +1,61 @@ +terraform { + required_providers { + zitadel = { + source = "zitadel/zitadel" + version = "2.0.2" + } + } +} + +resource "zitadel_application_oidc" "default" { + depends_on = [var.wait_on] + + org_id = var.org_id + + grant_types = ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE"] + name = var.name + project_id = var.project_id + + redirect_uris = var.redirect_uris + response_types = ["OIDC_RESPONSE_TYPE_CODE"] + + # // If selected, the requested roles of the authenticated user are added to the access token. + #access_token_type = "OIDC_TOKEN_TYPE_JWT" + #access_token_role_assertion = true + + # BEARER uses an Opaque token, which needs the introspection endpoint and `urn:zitadel:iam:org:project:id::aud` scope + access_token_type = "OIDC_TOKEN_TYPE_BEARER" + + # // If you want to add additional Origins to your app which is not used as a redirect you can do that here. + #additional_origins = [] + + app_type = "OIDC_APP_TYPE_WEB" + auth_method_type = var.auth_method_type + + # // Redirect URIs must begin with https:// unless dev_mode is true + #dev_mode = false + + id_token_role_assertion = var.id_token_role_assertion + id_token_userinfo_assertion = var.id_token_userinfo_assertion + + post_logout_redirect_uris = var.post_logout_redirect_uris +} + +output "installed" { + value = true + depends_on = [zitadel_application_oidc.default] +} + +output "application_id" { + value = zitadel_application_oidc.default.id +} + +output "client_id" { + value = zitadel_application_oidc.default.client_id + sensitive = true +} + +output "client_secret" { + value = zitadel_application_oidc.default.client_secret + sensitive = true +} diff --git a/infra/modules/zitadel/project/application/web/variables.tf b/infra/modules/zitadel/project/application/web/variables.tf new file mode 100644 index 0000000..8af3651 --- /dev/null +++ b/infra/modules/zitadel/project/application/web/variables.tf @@ -0,0 +1,47 @@ +variable "wait_on" { + type = any + description = "Resources to wait on" + default = true +} + + +variable "org_id" { + type = string + description = "Organisation Id" +} + +variable "project_id" { + type = string + description = "Project Id" +} + +variable "name" { + type = string + description = "Application name" +} + +variable "redirect_uris" { + type = list(string) +} + +variable "post_logout_redirect_uris" { + type = list(string) + default = [] +} + +variable "auth_method_type" { + type = string + default = "OIDC_AUTH_METHOD_TYPE_NONE" +} + +variable "id_token_role_assertion" { + type = bool + default = false + description = "If selected, the requested roles of the authenticated user are added to the ID token." +} + +variable "id_token_userinfo_assertion" { + type = bool + default = false + description = "Enables clients to retrieve profile, email, phone and address claims from ID token." +} diff --git a/infra/modules/zitadel/project/main.tf b/infra/modules/zitadel/project/main.tf index 611f668..534a075 100644 --- a/infra/modules/zitadel/project/main.tf +++ b/infra/modules/zitadel/project/main.tf @@ -1,3 +1,12 @@ +terraform { + required_providers { + zitadel = { + source = "zitadel/zitadel" + version = "2.0.2" + } + } +} + resource "zitadel_project" "default" { depends_on = [var.wait_on] org_id = var.org_id diff --git a/infra/modules/zitadel/project/member/main.tf b/infra/modules/zitadel/project/member/main.tf deleted file mode 100644 index 9895069..0000000 --- a/infra/modules/zitadel/project/member/main.tf +++ /dev/null @@ -1,17 +0,0 @@ -resource "zitadel_project_member" "default" { - depends_on = [var.wait_on] - - org_id = var.org_id - project_id = var.project_id - user_id = var.user_id - roles = var.roles -} - -output "installed" { - value = true - depends_on = [zitadel_project_member.default] -} - -output "project_member_id" { - value = zitadel_project_member.default.id -} diff --git a/infra/modules/zitadel/project/member/provider.tf b/infra/modules/zitadel/project/member/provider.tf deleted file mode 100644 index 653009c..0000000 --- a/infra/modules/zitadel/project/member/provider.tf +++ /dev/null @@ -1,14 +0,0 @@ -terraform { - required_providers { - zitadel = { - source = "zitadel/zitadel" - version = "2.0.2" - } - } -} - -provider "zitadel" { - domain = var.domain - insecure = "false" - jwt_profile_file = var.jwt_profile_file -} diff --git a/infra/modules/zitadel/project/provider.tf b/infra/modules/zitadel/project/provider.tf deleted file mode 100644 index 653009c..0000000 --- a/infra/modules/zitadel/project/provider.tf +++ /dev/null @@ -1,14 +0,0 @@ -terraform { - required_providers { - zitadel = { - source = "zitadel/zitadel" - version = "2.0.2" - } - } -} - -provider "zitadel" { - domain = var.domain - insecure = "false" - jwt_profile_file = var.jwt_profile_file -} diff --git a/infra/modules/zitadel/project/roles/main.tf b/infra/modules/zitadel/project/roles/main.tf index e8132b5..8a94852 100644 --- a/infra/modules/zitadel/project/roles/main.tf +++ b/infra/modules/zitadel/project/roles/main.tf @@ -1,3 +1,12 @@ +terraform { + required_providers { + zitadel = { + source = "zitadel/zitadel" + version = "2.0.2" + } + } +} + resource "zitadel_project_role" "default" { count = length(var.roles) depends_on = [var.wait_on] diff --git a/infra/modules/zitadel/project/roles/provider.tf b/infra/modules/zitadel/project/roles/provider.tf deleted file mode 100644 index 653009c..0000000 --- a/infra/modules/zitadel/project/roles/provider.tf +++ /dev/null @@ -1,14 +0,0 @@ -terraform { - required_providers { - zitadel = { - source = "zitadel/zitadel" - version = "2.0.2" - } - } -} - -provider "zitadel" { - domain = var.domain - insecure = "false" - jwt_profile_file = var.jwt_profile_file -} diff --git a/infra/modules/zitadel/project/roles/variables.tf b/infra/modules/zitadel/project/roles/variables.tf index 0b5b575..b18fb0f 100644 --- a/infra/modules/zitadel/project/roles/variables.tf +++ b/infra/modules/zitadel/project/roles/variables.tf @@ -1,20 +1,9 @@ -variable "domain" { - type = string - description = "Domain for the zitadel instance" - default = "localhost" -} - variable "wait_on" { type = any description = "Resources to wait on" default = true } -variable "jwt_profile_file" { - type = string - description = "Path to the jwt profile file" -} - variable "org_id" { type = string description = "Organisation Id" diff --git a/infra/modules/zitadel/project/user-grant/main.tf b/infra/modules/zitadel/project/user-grant/main.tf new file mode 100644 index 0000000..4d1838a --- /dev/null +++ b/infra/modules/zitadel/project/user-grant/main.tf @@ -0,0 +1,26 @@ +terraform { + required_providers { + zitadel = { + source = "zitadel/zitadel" + version = "2.0.2" + } + } +} + +resource "zitadel_user_grant" "default" { + depends_on = [var.wait_on] + + org_id = var.org_id + project_id = var.project_id + user_id = var.user_id + role_keys = var.roles +} + +output "installed" { + value = true + depends_on = [zitadel_user_grant.default] +} + +output "user_grant_id" { + value = zitadel_user_grant.default.id +} diff --git a/infra/modules/zitadel/project/member/variables.tf b/infra/modules/zitadel/project/user-grant/variables.tf similarity index 65% rename from infra/modules/zitadel/project/member/variables.tf rename to infra/modules/zitadel/project/user-grant/variables.tf index a4b6976..f4cd4af 100644 --- a/infra/modules/zitadel/project/member/variables.tf +++ b/infra/modules/zitadel/project/user-grant/variables.tf @@ -1,20 +1,9 @@ -variable "domain" { - type = string - description = "Domain for the zitadel instance" - default = "localhost" -} - variable "wait_on" { type = any description = "Resources to wait on" default = true } -variable "jwt_profile_file" { - type = string - description = "Path to the jwt profile file" -} - variable "org_id" { type = string description = "Organisation Id" diff --git a/infra/modules/zitadel/project/variables.tf b/infra/modules/zitadel/project/variables.tf index 28d991a..7670a95 100644 --- a/infra/modules/zitadel/project/variables.tf +++ b/infra/modules/zitadel/project/variables.tf @@ -1,20 +1,9 @@ -variable "domain" { - type = string - description = "Domain for the zitadel instance" - default = "localhost" -} - variable "wait_on" { type = any description = "Resources to wait on" default = true } -variable "jwt_profile_file" { - type = string - description = "Path to the jwt profile file" -} - variable "org_id" { type = string description = "Organisation Id" diff --git a/infra/modules/zitadel/service-account/main.tf b/infra/modules/zitadel/service-account/main.tf index 316714c..c8109f5 100644 --- a/infra/modules/zitadel/service-account/main.tf +++ b/infra/modules/zitadel/service-account/main.tf @@ -1,3 +1,12 @@ +terraform { + required_providers { + zitadel = { + source = "zitadel/zitadel" + version = "2.0.2" + } + } +} + resource "zitadel_machine_user" "default" { depends_on = [var.wait_on] diff --git a/infra/modules/zitadel/service-account/provider.tf b/infra/modules/zitadel/service-account/provider.tf deleted file mode 100644 index 653009c..0000000 --- a/infra/modules/zitadel/service-account/provider.tf +++ /dev/null @@ -1,14 +0,0 @@ -terraform { - required_providers { - zitadel = { - source = "zitadel/zitadel" - version = "2.0.2" - } - } -} - -provider "zitadel" { - domain = var.domain - insecure = "false" - jwt_profile_file = var.jwt_profile_file -} diff --git a/infra/modules/zitadel/service-account/variables.tf b/infra/modules/zitadel/service-account/variables.tf index b80205d..70d1676 100644 --- a/infra/modules/zitadel/service-account/variables.tf +++ b/infra/modules/zitadel/service-account/variables.tf @@ -1,20 +1,9 @@ -variable "domain" { - type = string - description = "Domain for the zitadel instance" - default = "localhost" -} - variable "wait_on" { type = any description = "Resources to wait on" default = true } -variable "jwt_profile_file" { - type = string - description = "Path to the jwt profile file" -} - variable "org_id" { type = string description = "Organisation Id" diff --git a/infra/modules/zitadel/tenant/main.tf b/infra/modules/zitadel/tenant/main.tf index 82c80b0..6200890 100644 --- a/infra/modules/zitadel/tenant/main.tf +++ b/infra/modules/zitadel/tenant/main.tf @@ -1,3 +1,12 @@ +terraform { + required_providers { + zitadel = { + source = "zitadel/zitadel" + version = "2.0.2" + } + } +} + resource "zitadel_org" "default" { depends_on = [var.wait_on] name = var.name diff --git a/infra/modules/zitadel/tenant/provider.tf b/infra/modules/zitadel/tenant/provider.tf deleted file mode 100644 index 653009c..0000000 --- a/infra/modules/zitadel/tenant/provider.tf +++ /dev/null @@ -1,14 +0,0 @@ -terraform { - required_providers { - zitadel = { - source = "zitadel/zitadel" - version = "2.0.2" - } - } -} - -provider "zitadel" { - domain = var.domain - insecure = "false" - jwt_profile_file = var.jwt_profile_file -} diff --git a/infra/modules/zitadel/tenant/role-owner/main.tf b/infra/modules/zitadel/tenant/role-owner/main.tf index 4605410..98e2c37 100644 --- a/infra/modules/zitadel/tenant/role-owner/main.tf +++ b/infra/modules/zitadel/tenant/role-owner/main.tf @@ -1,3 +1,12 @@ +terraform { + required_providers { + zitadel = { + source = "zitadel/zitadel" + version = "2.0.2" + } + } +} + resource "zitadel_org_member" "default" { depends_on = [var.wait_on] org_id = var.org_id diff --git a/infra/modules/zitadel/tenant/role-owner/provider.tf b/infra/modules/zitadel/tenant/role-owner/provider.tf deleted file mode 100644 index 653009c..0000000 --- a/infra/modules/zitadel/tenant/role-owner/provider.tf +++ /dev/null @@ -1,14 +0,0 @@ -terraform { - required_providers { - zitadel = { - source = "zitadel/zitadel" - version = "2.0.2" - } - } -} - -provider "zitadel" { - domain = var.domain - insecure = "false" - jwt_profile_file = var.jwt_profile_file -} diff --git a/infra/modules/zitadel/tenant/role-owner/variables.tf b/infra/modules/zitadel/tenant/role-owner/variables.tf index c5318a6..0a3dc1a 100644 --- a/infra/modules/zitadel/tenant/role-owner/variables.tf +++ b/infra/modules/zitadel/tenant/role-owner/variables.tf @@ -1,9 +1,3 @@ -variable "domain" { - type = string - description = "Domain for the zitadel instance" - default = "localhost" -} - variable "wait_on" { type = any description = "Resources to wait on" @@ -19,8 +13,3 @@ variable "user_id" { type = string description = "Zitadel User ID" } - -variable "jwt_profile_file" { - type = string - description = "Path to the jwt profile file" -} diff --git a/infra/modules/zitadel/tenant/variables.tf b/infra/modules/zitadel/tenant/variables.tf index f7674df..9faf38d 100644 --- a/infra/modules/zitadel/tenant/variables.tf +++ b/infra/modules/zitadel/tenant/variables.tf @@ -1,9 +1,3 @@ -variable "domain" { - type = string - description = "Domain for the zitadel instance" - default = "localhost" -} - variable "wait_on" { type = any description = "Resources to wait on" @@ -15,8 +9,3 @@ variable "name" { description = "Name of the tenant" default = "fourlights" } - -variable "jwt_profile_file" { - type = string - description = "Path to the jwt profile file" -} diff --git a/infra/modules/zitadel/user/main.tf b/infra/modules/zitadel/user/main.tf new file mode 100644 index 0000000..dc19396 --- /dev/null +++ b/infra/modules/zitadel/user/main.tf @@ -0,0 +1,31 @@ +terraform { + required_providers { + zitadel = { + source = "zitadel/zitadel" + version = "2.0.2" + } + } +} + +resource "zitadel_human_user" "default" { + depends_on = [var.wait_on] + + org_id = var.org_id + + email = var.email + user_name = var.user_name + first_name = var.first_name + last_name = var.last_name + + is_email_verified = true + initial_password = "Password1!" +} + +output "installed" { + value = true + depends_on = [zitadel_human_user.default] +} + +output "user_id" { + value = zitadel_human_user.default.id +} diff --git a/infra/modules/zitadel/user/variables.tf b/infra/modules/zitadel/user/variables.tf new file mode 100644 index 0000000..d449141 --- /dev/null +++ b/infra/modules/zitadel/user/variables.tf @@ -0,0 +1,26 @@ +variable "wait_on" { + type = any + description = "Resources to wait on" + default = true +} + +variable "org_id" { + type = string + description = "Organisation Id" +} + +variable "user_name" { + type = string +} + +variable "first_name" { + type = string +} + +variable "last_name" { + type = string +} + +variable "email" { + type = string +} diff --git a/infra/modules/zot/main.tf b/infra/modules/zot/main.tf new file mode 100644 index 0000000..4ee84b3 --- /dev/null +++ b/infra/modules/zot/main.tf @@ -0,0 +1,16 @@ +resource "helm_release" "zot" { + name = "zot" + repository = "https://zotregistry.dev/helm-charts" + chart = "zot" + namespace = "registry" + create_namespace = true + + values = [ + templatefile("${path.module}/values.yaml.tftpl", { service_uri = var.service_uri }) + ] +} + +output "installed" { + value = true + depends_on = [helm_release.zot] +} diff --git a/infra/modules/zot/values.yaml.tftpl b/infra/modules/zot/values.yaml.tftpl new file mode 100644 index 0000000..9183d3c --- /dev/null +++ b/infra/modules/zot/values.yaml.tftpl @@ -0,0 +1,11 @@ +ingress: + enabled: true + className: "traefik" + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: web + traefik.ingress.kubernetes.io/router.middlewares: default-preserve-host-headers@kubernetescrd + traefik.ingress.kubernetes.io/proxy-body-size: "0" + hosts: + - host: ${ service_uri } + paths: + - path: / diff --git a/infra/modules/zot/variables.tf b/infra/modules/zot/variables.tf new file mode 100644 index 0000000..850394c --- /dev/null +++ b/infra/modules/zot/variables.tf @@ -0,0 +1 @@ +variable "service_uri" { type = string } diff --git a/infra/tenants/365zon/main.tf b/infra/tenants/365zon/main.tf new file mode 100644 index 0000000..cb8eb99 --- /dev/null +++ b/infra/tenants/365zon/main.tf @@ -0,0 +1,56 @@ +locals { + name = "365Zon" +} + +resource "kubernetes_namespace" "tenant" { + metadata { + name = lower(local.name) + } + + lifecycle { + ignore_changes = [metadata] + } +} + +module "bootstrap-zitadel" { + source = "./zitadel" + + namespace = kubernetes_namespace.tenant.metadata[0].name + org_id = var.org_id + user_id = var.user_id + name = local.name +} + +// create uploads bucket in minio + +// create minio secret +resource "kubernetes_secret" "storage" { + metadata { + name = "storage" + namespace = kubernetes_namespace.tenant.metadata[0].name + } + + data = { + Storage__AccountName = var.minio_access_key + Storage__AccountKey = var.minio_secret_key + Storage__BlobUri = var.minio_service_uri + Storage__S3BucketName = "uploads" + } +} + +resource "kubernetes_secret" "connection_strings" { + metadata { + name = "connection-strings" + namespace = kubernetes_namespace.tenant.metadata[0].name + } + + data = { + ConnectionStrings__DocumentDb = var.mongodb_connection_string + ConnectionStrings__ServiceBus = var.rabbitmq_connection_string + } +} + +// okay, so now we have the identity stuff in order, and we have secrets to use for that +// next, we need to set-up: +// - the wildcard tls (*.365zon.venus.fourlights.dev) +// - argocd for all relevant apps diff --git a/infra/tenants/365zon/provider.tf b/infra/tenants/365zon/provider.tf new file mode 100644 index 0000000..2864e32 --- /dev/null +++ b/infra/tenants/365zon/provider.tf @@ -0,0 +1,50 @@ +terraform { + required_providers { + zitadel = { + source = "zitadel/zitadel" + version = "2.0.2" + } + } +} + +provider "zitadel" { + domain = var.domain + insecure = "false" + jwt_profile_file = var.jwt_profile_file +} + +locals { + k8s_config_path = format("%s/%s", path.root, "../kubeconfig") + k8s_config_yaml = file(local.k8s_config_path) + k8s_config = yamldecode(local.k8s_config_yaml) + k8s_host = local.k8s_config.clusters[0].cluster.server + k8s_auth = try( + { + token = local.k8s_config.users[0].user.token + using_token = true + }, + { + client_certificate = base64decode(local.k8s_config.users[0].user["client-certificate-data"]) + client_key = base64decode(local.k8s_config.users[0].user["client-key-data"]) + using_token = false + } + ) +} + +provider "kubernetes" { + host = local.k8s_host + insecure = true + token = local.k8s_auth.using_token ? local.k8s_auth.token : null + client_certificate = local.k8s_auth.using_token ? null : local.k8s_auth.client_certificate + client_key = local.k8s_auth.using_token ? null : local.k8s_auth.client_key +} + +provider "helm" { + kubernetes { + host = local.k8s_host + insecure = true + token = local.k8s_auth.using_token ? local.k8s_auth.token : null + client_certificate = local.k8s_auth.using_token ? null : local.k8s_auth.client_certificate + client_key = local.k8s_auth.using_token ? null : local.k8s_auth.client_key + } +} diff --git a/infra/tenants/365zon/variables.tf b/infra/tenants/365zon/variables.tf new file mode 100644 index 0000000..e6b03f3 --- /dev/null +++ b/infra/tenants/365zon/variables.tf @@ -0,0 +1,21 @@ +variable "domain" { type = string } +variable "jwt_profile_file" { type = string } +variable "org_id" { type = string } +variable "user_id" { type = string } +variable "minio_access_key" { + type = string + sensitive = true +} +variable "minio_secret_key" { + type = string + sensitive = true +} +variable "minio_service_uri" { type = string } +variable "mongodb_connection_string" { + type = string + sensitive = true +} +variable "rabbitmq_connection_string" { + type = string + sensitive = true +} diff --git a/infra/tenants/365zon/zitadel/main.tf b/infra/tenants/365zon/zitadel/main.tf new file mode 100644 index 0000000..082d492 --- /dev/null +++ b/infra/tenants/365zon/zitadel/main.tf @@ -0,0 +1,132 @@ +locals { + tld = "fourlights.dev" + cluster_dns = "venus.${local.tld}" + domain = "zitadel.${local.cluster_dns}" + org_domain = "fourlights.${local.domain}" +} + +module "zitadel_project" { + source = "../../../modules/zitadel/project" + + org_id = var.org_id + name = var.name + owners = [var.user_id] +} + +// TODO: add action for setting roles as scopes + +module "zitadel_project_operator_roles" { + source = "../../../modules/zitadel/project/roles" + + org_id = var.org_id + project_id = module.zitadel_project.project_id + group = "Operator" + roles = [ + "manage:profiles", "manage:contacts", "manage:addresses", "manage:enquiries", "manage:flowstates", + "manage:flowevents", "manage:files" + ] +} + +module "zitadel_project_configurator_roles" { + source = "../../../modules/zitadel/project/roles" + + org_id = var.org_id + project_id = module.zitadel_project.project_id + group = "Configurator" + roles = [ + "manage:brands", "manage:flows" + ] +} + +module "zitadel_project_developer_roles" { + source = "../../../modules/zitadel/project/roles" + + org_id = var.org_id + project_id = module.zitadel_project.project_id + group = "Developer" + roles = [ + "manage:jobs", "manage:infrastructure" + ] +} + +// TODO: Move External (and 365zon Push service account) to own project +// TODO: Add grant for external project +// TODO: Add read roles + +module "zitadel_project_application_core" { + source = "../../../modules/zitadel/api-m2m-swagger" + + org_id = var.org_id + project_id = module.zitadel_project.project_id + + name = "Core" + zitadel_domain = local.domain + cluster_domain = local.cluster_dns + + namespace = var.namespace + project = var.name + + service_account = false + roles = [] +} + +module "zitadel_project_application_salesforce" { + source = "../../../modules/zitadel/api-m2m-swagger" + + org_id = var.org_id + project_id = module.zitadel_project.project_id + + name = "Salesforce" + zitadel_domain = local.domain + cluster_domain = local.cluster_dns + + namespace = var.namespace + project = var.name + + roles = module.zitadel_project_operator_roles.roles +} + +module "zitadel_project_application_external" { + source = "../../../modules/zitadel/api-m2m-swagger" + + org_id = var.org_id + project_id = module.zitadel_project.project_id + + name = "External" + zitadel_domain = local.domain + cluster_domain = local.cluster_dns + + namespace = var.namespace + project = var.name + + roles = module.zitadel_project_operator_roles.roles +} + +module "zitadel_project_application_module_internal" { + source = "../../../modules/zitadel/api-m2m-swagger" + + org_id = var.org_id + project_id = module.zitadel_project.project_id + + name = "Internal" + zitadel_domain = local.domain + cluster_domain = local.cluster_dns + + namespace = var.namespace + project = var.name + + roles = module.zitadel_project_operator_roles.roles +} + +// TODO: Application for Front-End End (implicit, authorization_code, refresh_token) +// TODO: Update API applications with callback apiDomain/swagger/oauth2-redirect.html to allow logging in for swagger (and probably hangire?) +// TODO: Put all the relevant secrets into secret manager +// TODO: Set up opentelemetry and update appinsights shit to use that. + +output "org_id" { + value = var.org_id +} + +output "project_id" { + value = module.zitadel_project.project_id +} diff --git a/infra/tenants/365zon/zitadel/provider.tf b/infra/tenants/365zon/zitadel/provider.tf new file mode 100644 index 0000000..9d0da41 --- /dev/null +++ b/infra/tenants/365zon/zitadel/provider.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + zitadel = { + source = "zitadel/zitadel" + version = "2.0.2" + } + } +} diff --git a/infra/tenants/365zon/zitadel/variables.tf b/infra/tenants/365zon/zitadel/variables.tf new file mode 100644 index 0000000..d41d6e2 --- /dev/null +++ b/infra/tenants/365zon/zitadel/variables.tf @@ -0,0 +1,15 @@ +variable "org_id" { + type = string +} + +variable "user_id" { + type = string +} + +variable "namespace" { + type = string +} + +variable "name" { + type = string +} diff --git a/infra/tenants/argocd/zitadel/groupsClaim.action.tftpl b/infra/tenants/argocd/zitadel/groupsClaim.action.tftpl new file mode 100644 index 0000000..308dc51 --- /dev/null +++ b/infra/tenants/argocd/zitadel/groupsClaim.action.tftpl @@ -0,0 +1,27 @@ +/** + * sets the roles an additional claim in the token with roles as value an project as key + * + * The role claims of the token look like the following: + * + * // added by the code below + * "groups": ["{roleName}", "{roleName}", ...], + * + * Flow: Complement token, Triggers: Pre Userinfo creation, Pre access token creation + * + * @param ctx + * @param api + */ +function groupsClaim(ctx, api) { + if (ctx.v1.user.grants === undefined || ctx.v1.user.grants.count == 0) { + return; + } + + let grants = []; + ctx.v1.user.grants.grants.forEach((claim) => { + claim.roles.forEach((role) => { + grants.push(role); + }); + }); + + api.v1.claims.setClaim("groups", grants); +} diff --git a/infra/tenants/argocd/zitadel/main.tf b/infra/tenants/argocd/zitadel/main.tf new file mode 100644 index 0000000..516edc5 --- /dev/null +++ b/infra/tenants/argocd/zitadel/main.tf @@ -0,0 +1,113 @@ +locals { + argocd_uri = "https://${var.argocd_service_domain}" +} + +module "zitadel_project" { + source = "../../../modules/zitadel/project" + + org_id = var.org_id + name = var.name + owners = [var.user_id] +} + +module "zitadel_project_roles_user" { + source = "../../../modules/zitadel/project/roles" + + org_id = var.org_id + project_id = module.zitadel_project.project_id + group = "Users" + roles = ["user"] +} + +module "zitadel_project_roles_admin" { + source = "../../../modules/zitadel/project/roles" + + org_id = var.org_id + project_id = module.zitadel_project.project_id + group = "Admins" + roles = ["admin"] +} + +module "zitadel_application_argocd" { + source = "../../../modules/zitadel/project/application/web" + + name = "ArgoCD" + org_id = var.org_id + project_id = module.zitadel_project.project_id + + redirect_uris = ["${ local.argocd_uri}/api/dex/callback"] + post_logout_redirect_uris = [local.argocd_uri] + + auth_method_type = "OIDC_AUTH_METHOD_TYPE_BASIC" + id_token_role_assertion = true + id_token_userinfo_assertion = true +} + +resource "zitadel_action" "groups-claim" { + org_id = var.org_id + name = "groupsClaim" + script = templatefile("${path.module}/groupsClaim.action.tftpl", {}) + allowed_to_fail = true + timeout = "10s" +} + +resource "zitadel_trigger_actions" "groups-claim-pre-user-info" { + org_id = var.org_id + flow_type = "FLOW_TYPE_CUSTOMISE_TOKEN" + trigger_type = "TRIGGER_TYPE_PRE_USERINFO_CREATION" + action_ids = [zitadel_action.groups-claim.id] +} + +resource "zitadel_trigger_actions" "groups-claim-pre-access-token" { + org_id = var.org_id + flow_type = "FLOW_TYPE_CUSTOMISE_TOKEN" + trigger_type = "TRIGGER_TYPE_PRE_ACCESS_TOKEN_CREATION" + action_ids = [zitadel_action.groups-claim.id] +} + +module "zitadel_project_user_grant" { + source = "../../../modules/zitadel/project/user-grant" + + org_id = var.org_id + + project_id = module.zitadel_project.project_id + user_id = var.user_id + + roles = module.zitadel_project_roles_admin.roles +} + +output "client_id" { + value = module.zitadel_application_argocd.client_id +} + +output "client_secret" { + value = module.zitadel_application_argocd.client_secret +} + +output "scopes" { + value = ["openid", "profile", "email", "groups"] +} + +output "logoutSuffix" { + value = "oidc/v1/end_session" +} + +output "user_roles" { + value = module.zitadel_project_roles_user.roles +} + +output "admin_roles" { + value = module.zitadel_project_roles_admin.roles +} + +output "project_id" { + value = module.zitadel_project.project_id +} + +output "installed" { + value = true + depends_on = [ + module.zitadel_project_user_grant.installed, + zitadel_trigger_actions.groups-claim-pre-access-token, zitadel_trigger_actions.groups-claim-pre-user-info + ] +} diff --git a/infra/modules/zitadel/identity-provider/google/provider.tf b/infra/tenants/argocd/zitadel/provider.tf similarity index 100% rename from infra/modules/zitadel/identity-provider/google/provider.tf rename to infra/tenants/argocd/zitadel/provider.tf diff --git a/infra/tenants/argocd/zitadel/variables.tf b/infra/tenants/argocd/zitadel/variables.tf new file mode 100644 index 0000000..6f6cdd8 --- /dev/null +++ b/infra/tenants/argocd/zitadel/variables.tf @@ -0,0 +1,17 @@ +variable "org_id" { + type = string +} + +variable "user_id" { + type = string +} + +variable "name" { + type = string + default = "argocd" +} + +variable "domain" { type = string } +variable "jwt_profile_file" { type = string } + +variable "argocd_service_domain" { type = string } diff --git a/infra/tenants/fourlights/zitadel/main.tf b/infra/tenants/fourlights/zitadel/main.tf new file mode 100644 index 0000000..7b0100c --- /dev/null +++ b/infra/tenants/fourlights/zitadel/main.tf @@ -0,0 +1,54 @@ +module "zitadel-tenant" { + source = "../../../modules/zitadel/tenant" + + name = "fourlights" +} + +module "zitadel-idp-google" { + source = "../../../modules/zitadel/identity-provider/google" + wait_on = module.zitadel-tenant.installed + + org_id = module.zitadel-tenant.org_id + client_id = "783390190667-quvko2l2kr9ksgeo3pn6pn6t8c1mai9n.apps.googleusercontent.com" + client_secret = "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" + + domain = var.domain + + options = { + scopes = ["openid", "profile", "email"] + is_auto_creation = true + is_auto_update = true + is_creation_allowed = true + is_linking_allowed = true + + auto_linking = "AUTO_LINKING_OPTION_USERNAME" + } +} + +module "zitadel-user" { + source = "../../../modules/zitadel/user" + wait_on = module.zitadel-tenant.installed + + org_id = module.zitadel-tenant.org_id + + first_name = "Thomas" + last_name = "Rijpstra" + user_name = "thomas@fourlights.nl" + email = "thomas@fourlights.nl" +} + +module "zitadel-org-owner" { + source = "../../../modules/zitadel/tenant/role-owner" + wait_on = module.zitadel-user.installed + + org_id = module.zitadel-tenant.org_id + user_id = module.zitadel-user.user_id +} + +output "org_id" { + value = module.zitadel-tenant.org_id +} + +output "user_id" { + value = module.zitadel-user.user_id +} diff --git a/infra/tenants/fourlights/zitadel/provider.tf b/infra/tenants/fourlights/zitadel/provider.tf new file mode 100644 index 0000000..2864e32 --- /dev/null +++ b/infra/tenants/fourlights/zitadel/provider.tf @@ -0,0 +1,50 @@ +terraform { + required_providers { + zitadel = { + source = "zitadel/zitadel" + version = "2.0.2" + } + } +} + +provider "zitadel" { + domain = var.domain + insecure = "false" + jwt_profile_file = var.jwt_profile_file +} + +locals { + k8s_config_path = format("%s/%s", path.root, "../kubeconfig") + k8s_config_yaml = file(local.k8s_config_path) + k8s_config = yamldecode(local.k8s_config_yaml) + k8s_host = local.k8s_config.clusters[0].cluster.server + k8s_auth = try( + { + token = local.k8s_config.users[0].user.token + using_token = true + }, + { + client_certificate = base64decode(local.k8s_config.users[0].user["client-certificate-data"]) + client_key = base64decode(local.k8s_config.users[0].user["client-key-data"]) + using_token = false + } + ) +} + +provider "kubernetes" { + host = local.k8s_host + insecure = true + token = local.k8s_auth.using_token ? local.k8s_auth.token : null + client_certificate = local.k8s_auth.using_token ? null : local.k8s_auth.client_certificate + client_key = local.k8s_auth.using_token ? null : local.k8s_auth.client_key +} + +provider "helm" { + kubernetes { + host = local.k8s_host + insecure = true + token = local.k8s_auth.using_token ? local.k8s_auth.token : null + client_certificate = local.k8s_auth.using_token ? null : local.k8s_auth.client_certificate + client_key = local.k8s_auth.using_token ? null : local.k8s_auth.client_key + } +} diff --git a/infra/tenants/fourlights/zitadel/variables.tf b/infra/tenants/fourlights/zitadel/variables.tf new file mode 100644 index 0000000..7046469 --- /dev/null +++ b/infra/tenants/fourlights/zitadel/variables.tf @@ -0,0 +1,2 @@ +variable "domain" { type = string } +variable "jwt_profile_file" { type = string } diff --git a/shuttles/terraform-zitadel-365zon/main.tf b/shuttles/terraform-zitadel-365zon/main.tf deleted file mode 100644 index b638b47..0000000 --- a/shuttles/terraform-zitadel-365zon/main.tf +++ /dev/null @@ -1,295 +0,0 @@ -locals { - tld = "fourlights.dev" - cluster_dns = "venus.${local.tld}" - domain = "zitadel.${local.cluster_dns}" - org_domain = "fourlights.${local.domain}" - jwt_profile_file = "../terraform/zitadel-admin-sa.json" - name = "365Zon" - - user_id = "308083708882059797" -} - -terraform { - required_providers { - zitadel = { - source = "zitadel/zitadel" - version = "2.0.2" - } - } -} - -provider "zitadel" { - domain = local.domain - insecure = "false" - jwt_profile_file = local.jwt_profile_file -} - -data "zitadel_orgs" "default" { - domain = local.domain -} - -data "zitadel_org" "default" { - for_each = toset(data.zitadel_orgs.default.ids) - id = each.value -} - -module "zitadel_project" { - count = data.zitadel_org.default.count - source = "../../infra/modules/zitadel/project" - - domain = local.domain - org_id = data.zitadel_org.default[count.index].id - jwt_profile_file = local.jwt_profile_file - name = local.name - owners = [local.user_id] -} - -// TODO: add action for setting roles as scopes - -module "zitadel_project_operator_roles" { - count = data.zitadel_org.default.count - source = "../../infra/modules/zitadel/project/roles" - - domain = local.domain - org_id = data.zitadel_org.default[count.index].id - project_id = module.zitadel_project[count.index].project_id - jwt_profile_file = local.jwt_profile_file - group = "Operator" - roles = [ - "manage:profiles", "manage:contacts", "manage:addresses", "manage:enquiries", "manage:flowstates", - "manage:flowevents", "manage:files" - ] -} - -module "zitadel_project_configurator_roles" { - count = data.zitadel_org.default.count - source = "../../infra/modules/zitadel/project/roles" - - domain = local.domain - org_id = data.zitadel_org.default[count.index].id - project_id = module.zitadel_project[count.index].project_id - jwt_profile_file = local.jwt_profile_file - group = "Configurator" - roles = [ - "manage:brands", "manage:flows" - ] -} - -module "zitadel_project_developer_roles" { - count = data.zitadel_org.default.count - source = "../../infra/modules/zitadel/project/roles" - - domain = local.domain - org_id = data.zitadel_org.default[count.index].id - project_id = module.zitadel_project[count.index].project_id - jwt_profile_file = local.jwt_profile_file - group = "Developer" - roles = [ - "manage:jobs", "manage:infrastructure" - ] -} - -// TODO: Move External (and 365zon Push service account) to own project -// TODO: Add grant for external project -// TODO: Add read roles - -module "zitadel_project_application_core_api" { - count = data.zitadel_org.default.count - source = "../../infra/modules/zitadel/project/application/api" - - domain = local.domain - org_id = data.zitadel_org.default[count.index].id - project_id = module.zitadel_project[count.index].project_id - jwt_profile_file = local.jwt_profile_file - - name = "Core API" -} - -module "zitadel_project_application_core_ua" { - count = data.zitadel_org.default.count - source = "../../infra/modules/zitadel/project/applicaitn/user-agent" - - domain = local.domain - org_id = data.zitadel_org.default[count.index].id - project_id = module.zitadel_project[count.index].project_id - jwt_profile_file = local.jwt_profile_file - - name = "Core (Swagger)" - - -} - -module "zitadel_project_application_module_365zon_api" { - count = data.zitadel_org.default.count - source = "../../infra/modules/zitadel/project/application/api" - - domain = local.domain - org_id = data.zitadel_org.default[count.index].id - project_id = module.zitadel_project[count.index].project_id - jwt_profile_file = local.jwt_profile_file - - name = "Module: Salesforce Pull API" -} - -module "zitadel_project_application_module_365zon_ua" { - count = data.zitadel_org.default.count - source = "../../infra/modules/zitadel/project/application/user-agent" - - domain = local.domain - org_id = data.zitadel_org.default[count.index].id - project_id = module.zitadel_project[count.index].project_id - jwt_profile_file = local.jwt_profile_file - - name = "Module: Salesforce Pull (Swagger)" -} - -module "zitadel_project_application_module_external_api" { - count = data.zitadel_org.default.count - source = "../../infra/modules/zitadel/project/application/api" - - domain = local.domain - org_id = data.zitadel_org.default[count.index].id - project_id = module.zitadel_project[count.index].project_id - jwt_profile_file = local.jwt_profile_file - - name = "Module: External API" -} - -module "zitadel_project_application_module_external_ua" { - count = data.zitadel_org.default.count - source = "../../infra/modules/zitadel/project/application/user-agent" - - domain = local.domain - org_id = data.zitadel_org.default[count.index].id - project_id = module.zitadel_project[count.index].project_id - jwt_profile_file = local.jwt_profile_file - - name = "Module: External (Swagger)" -} - -module "zitadel_project_application_module_internal_api" { - count = data.zitadel_org.default.count - source = "../../infra/modules/zitadel/project/application/api" - - domain = local.domain - org_id = data.zitadel_org.default[count.index].id - project_id = module.zitadel_project[count.index].project_id - jwt_profile_file = local.jwt_profile_file - - name = "Module: Internal API" -} - -module "zitadel_project_application_module_internal_ua" { - count = data.zitadel_org.default.count - source = "../../infra/modules/zitadel/project/application/user-agent" - - domain = local.domain - org_id = data.zitadel_org.default[count.index].id - project_id = module.zitadel_project[count.index].project_id - jwt_profile_file = local.jwt_profile_file - - name = "Module: Internal swagger" -} - -module "zitadel_service_account_module_internal" { - count = data.zitadel_org.default.count - source = "../../infra/modules/zitadel/service-account" - - domain = local.domain - org_id = data.zitadel_org.default[count.index].id - jwt_profile_file = local.jwt_profile_file - - user_name = "${local.name}-module-internal@${ local.org_domain }" - name = "Module Internal @ ${local.name}" - - with_secret = true - access_token_type = "ACCESS_TOKEN_TYPE_JWT" -} - -module "zitadel_project_member_module_internal" { - wait_on = module.zitadel_project_operator_roles[count.index].installed - count = data.zitadel_org.default.count - source = "../../infra/modules/zitadel/project/member" - - domain = local.domain - org_id = data.zitadel_org.default[count.index].id - jwt_profile_file = local.jwt_profile_file - - project_id = module.zitadel_project[count.index].project_id - user_id = module.zitadel_service_account_module_internal[count.index].user_id - - roles = module.zitadel_project_operator_roles[count.index].roles -} - -module "zitadel_service_account_module_external" { - count = data.zitadel_org.default.count - source = "../../infra/modules/zitadel/service-account" - - domain = local.domain - org_id = data.zitadel_org.default[count.index].id - jwt_profile_file = local.jwt_profile_file - - user_name = "${local.name}-module-external@${ local.org_domain }" - name = "Module External @ ${local.name}" - - with_secret = true - access_token_type = "ACCESS_TOKEN_TYPE_JWT" -} - -module "zitadel_project_member_module_external" { - wait_on = module.zitadel_project_operator_roles[count.index].installed - count = data.zitadel_org.default.count - source = "../../infra/modules/zitadel/project/member" - - domain = local.domain - org_id = data.zitadel_org.default[count.index].id - jwt_profile_file = local.jwt_profile_file - - project_id = module.zitadel_project[count.index].project_id - user_id = module.zitadel_service_account_module_external[count.index].user_id - - roles = module.zitadel_project_operator_roles[count.index].roles -} - -module "zitadel_service_account_module_365zon" { - count = data.zitadel_org.default.count - source = "../../infra/modules/zitadel/service-account" - - domain = local.domain - org_id = data.zitadel_org.default[count.index].id - jwt_profile_file = local.jwt_profile_file - - user_name = "${local.name}-module-365zon@${ local.org_domain }" - name = "Module 365Zon @ ${local.name}" - - with_secret = true - access_token_type = "ACCESS_TOKEN_TYPE_JWT" -} - -module "zitadel_project_member_module_365zon" { - wait_on = module.zitadel_project_operator_roles[count.index].installed - count = data.zitadel_org.default.count - source = "../../infra/modules/zitadel/project/member" - - domain = local.domain - org_id = data.zitadel_org.default[count.index].id - jwt_profile_file = local.jwt_profile_file - - project_id = module.zitadel_project[count.index].project_id - user_id = module.zitadel_service_account_module_365zon[count.index].user_id - - roles = module.zitadel_project_operator_roles[count.index].roles -} - -// TODO: Application for Front-End End (implicit, authorization_code, refresh_token) -// TODO: Update API applications with callback apiDomain/swagger/oauth2-redirect.html to allow logging in for swagger (and probably hangire?) -// TODO: Put all the relevant secrets into secret manager -// TODO: Set up opentelemetry and update appinsights shit to use that. - -output "org_ids" { - value = data.zitadel_orgs.default.ids -} - -output "project_ids" { - value = [for project in module.zitadel_project : project.project_id] -} diff --git a/shuttles/terraform-zitadel-bootstrap/.terraform.lock.hcl b/shuttles/terraform-zitadel-bootstrap/.terraform.lock.hcl deleted file mode 100644 index 287ac57..0000000 --- a/shuttles/terraform-zitadel-bootstrap/.terraform.lock.hcl +++ /dev/null @@ -1,25 +0,0 @@ -# This file is maintained automatically by "terraform init". -# Manual edits may be lost in future updates. - -provider "registry.terraform.io/zitadel/zitadel" { - version = "2.0.2" - constraints = "2.0.2" - hashes = [ - "h1:iymeaNBrZ4smcr7eHrxO4gbXQ6bx/enKyj3RQ6xZRYA=", - "zh:01e16af0dda9372696b5e1d43ec709aed79829b49ee69a4f9606a248752f672d", - "zh:048c4e726fb846cfe9ab0a0a1f86d3f8922442154b086e2bd8e389b32f69f2f0", - "zh:3a3f6bea621c9d480f1f288cffebace8620979b9260cfeae8f9af5d9a25ed490", - "zh:4d349e584786589bc2037cee691ff1678296f5351e6491aa34dcb08ecbe1dcb7", - "zh:80741c78179788be8d7e33e471e1311197cd4e1067803d438463d0a8ac871a60", - "zh:89178d30f5ec49551e6a6ebc5eb589ab6631012dcec0d03ea7130b1029890e51", - "zh:94cd3b1fe3d1d39bcb3b70208b044bde4c5ce5152e12b29f0fa0ff1085e12863", - "zh:97299c172ada852705f8ca9fa91eeee12c6259263baae3ca53cf41e3130b1731", - "zh:a33d53acc640dc93b81352ba633cf392bc8c7614a72d320d59d3dcdb22d73fc4", - "zh:a95c15960baf8157f79a6490361455767d48e4dd3ce2ef1d0051743f6152733b", - "zh:ae66ad95c7039e6ef844c39389c9077ce7dbb501b6af02afb26a223fd289dbcb", - "zh:b8a9cb3b53653c06d52607368c406112ee1abc6d66dc4aedaedddbb46a66ea8f", - "zh:d48693ecdc985bb4167af0c3164240c13c4ea48167d28f706e7893cbdb20540a", - "zh:f6db1ec30bfbcf4423ab2d29979b775423ba37008fd48a766b5a1cf87a131859", - "zh:fed4e95dc9aaf361c8ff57f819d31fa25152b9e6cb90b7202d8be9ab1446b081", - ] -} diff --git a/shuttles/terraform-zitadel-bootstrap/main.tf b/shuttles/terraform-zitadel-bootstrap/main.tf deleted file mode 100644 index aa8550f..0000000 --- a/shuttles/terraform-zitadel-bootstrap/main.tf +++ /dev/null @@ -1,115 +0,0 @@ -locals { - tld = "fourlights.dev" - cluster_dns = "venus.${local.tld}" - domain = "zitadel.${local.cluster_dns}" - jwt_profile_file = "../terraform/zitadel-admin-sa.json" -} - - -module "zitadel-tenant" { - source = "../../infra/modules/zitadel/tenant" - - domain = local.domain - name = "fourlights" - jwt_profile_file = local.jwt_profile_file -} - -module "zitadel-idp-google" { - source = "../../infra/modules/zitadel/identity-provider/google" - wait_on = module.zitadel-tenant.installed - - domain = local.domain - jwt_profile_file = local.jwt_profile_file - org_id = module.zitadel-tenant.org_id - client_id = "783390190667-quvko2l2kr9ksgeo3pn6pn6t8c1mai9n.apps.googleusercontent.com" - client_secret = "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" - - options = { - scopes = ["openid", "profile", "email"] - is_auto_creation = true - is_auto_update = true - is_creation_allowed = true - is_linking_allowed = false - - auto_linking = "AUTO_LINKING_OPTION_USERNAME" - } -} - -// TODO: Create default user thomas@fourlights.nl - -# module "zitadel-user" { -# source = "../../infra/modules/zitadel/user" -# wait_on = module.zitadel-idp-google.installed -# -# domain = local.domain -# org_id = module.zitadel-tenant.org_id -# jwt_profile_file = local.jwt_profile_file -# -# email = "thomas@fourlights.nl" -# idp_id = module.zitadel-idp-google.idp_id -# idp_user_id = "105558048282918392820" -# user_id = "308083708882059797" -# } -module "zitadel-owner" { - source = "../../infra/modules/zitadel/owner" - #wait_on = module.zitadel-user.installed - wait_on = module.zitadel-idp-google.installed - - domain = local.domain - org_id = module.zitadel-tenant.org_id - jwt_profile_file = local.jwt_profile_file - #user_id = module.zitadel-user.user_id - user_id = "308083708882059797" -} - -module "zitadel-org-owner" { - source = "../../infra/modules/zitadel/tenant/role-owner" - #wait_on = module.zitadel-user.installed - wait_on = module.zitadel-idp-google.installed - - domain = local.domain - org_id = module.zitadel-tenant.org_id - jwt_profile_file = local.jwt_profile_file - #user_id = module.zitadel-user.user_id - user_id = "308083708882059797" -} - -module "zitadel-project" { - source = "../../infra/modules/zitadel/tenant/role-owner" - #wait_on = module.zitadel-user.installed - wait_on = module.zitadel-idp-google.installed - - domain = local.domain - org_id = module.zitadel-tenant.org_id - jwt_profile_file = local.jwt_profile_file - #user_id = module.zitadel-user.user_id - user_id = "308083708882059797" -} - -#resource "zitadel_project" "default" { -# name = "365zon" -# org_id = module.zitadel-tenant.org_id -# project_role_assertion = true -# project_role_check = true -# has_project_check = true -# private_labeling_setting = "PRIVATE_LABELING_SETTING_ENFORCE_PROJECT_RESOURCE_OWNER_POLICY" -#} -# -#resource "zitadel_user_grant" "default" { -# project_id = data.zitadel_project.default.id -# org_id = data.zitadel_org.default.id -# role_keys = ["super-user"] -# user_id = data.zitadel_human_user.default.id -#} - -// module "zitadel-machine-user" { -// source = "../../infra/modules/zitadel/tenant" -// wait_on = module.zitadel.installed -// k8s_config_yaml = local.k8s_config_yaml -// -// domain = module.zitadel.server -// secret = "zitadel-admin-sa" -// namespace = "zitadel" -// name = "fourlights" -// } - diff --git a/shuttles/terraform/.terraform.lock.hcl b/shuttles/terraform/.terraform.lock.hcl index e232ef7..4b5692f 100644 --- a/shuttles/terraform/.terraform.lock.hcl +++ b/shuttles/terraform/.terraform.lock.hcl @@ -96,6 +96,29 @@ provider "registry.terraform.io/hashicorp/random" { ] } +provider "registry.terraform.io/public-cloud-wl/slugify" { + version = "0.1.1" + constraints = "0.1.1" + hashes = [ + "h1:iOJEMYX1bLfUnKjSxluQkKijr5NgWSqb2lU9Ag2Q12w=", + "zh:13f77dedcc74256053ac51512372510d722116bf58e119fac203fe599d667720", + "zh:2223be634f684f76e265efdaafdf95a948ba9e44f09f8a89540bdb564eff17f1", + "zh:73e8b763c796d57186756cf0bab75323e2d92c873f1df8eccd8a7e336a2e3e81", + "zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f", + "zh:9f83adcf17de03afb5c27111cb26c580dc5296dffd40fca4571e81ad0bad3bad", + "zh:a5414ade8cbae9aea10dee79e43da247ceecb7e4a54e76d39906ee60b7365a7d", + "zh:bd118ead731e129c92c0dfe3c9a2ebbd8fa25ba6508deaaaccb9ac3a7f70af2d", + "zh:c8ce48ad921956edcee0643cb6184442f3deb438e5605a53794dfd6e8f89a559", + "zh:d96da8a32ef2b807ed3bd943294c6e1d0bd5fc3a793deb762f74d0c54aeff335", + "zh:e30a218b474afe082e005faf51c323ed8747d46845bfacab4cd3adc0c51704ec", + "zh:e3cd265c38da6e65974ac1b9b6be608ba0534178f16f059ad13672de6846e32e", + "zh:f2ded7f8c771a603ad3e2df84986b5f175c38049b7a9ab4a3cd384abafb33dff", + "zh:f2ece1996cf686583afd19384041204a32e08389dc6f4f105501584e653e797d", + "zh:fa2418b74cea55d29dad24f5095aaf30d6253d63ebac3c0c47949b3de8087c88", + "zh:fdc8d3fbca6a19db203802e7a7337075e39b9ffb7a3887a7583e379be61bde17", + ] +} + provider "registry.terraform.io/zitadel/zitadel" { version = "2.0.2" constraints = "2.0.2" diff --git a/shuttles/terraform/main.tf b/shuttles/terraform/main.tf index 5a25254..5c2241e 100644 --- a/shuttles/terraform/main.tf +++ b/shuttles/terraform/main.tf @@ -1,11 +1,16 @@ locals { tld = "fourlights.dev" cluster_dns = "venus.${local.tld}" - bridge_dns = "bridge.${local.cluster_dns}" is_installed = true node_count = 3 } +module "registry" { + source = "../../infra/modules/zot" + + service_uri = "registry.${local.cluster_dns}" +} + resource "kubernetes_manifest" "preserve-host-middleware" { depends_on = [local.is_installed] manifest = { @@ -130,3 +135,72 @@ module "zitadel" { display_on_homepage = true } + +module "zitadel-bootstrap" { + source = "../../infra/tenants/fourlights/zitadel" + + domain = module.zitadel.server + jwt_profile_file = module.zitadel.jwt_profile_file +} + + +module "redis" { + source = "../../infra/modules/redis" + + namespace = "redis" + k8s_config_yaml = local.k8s_config_yaml +} + +module "tenant-365zon" { + source = "../../infra/tenants/365zon" + + org_id = module.zitadel-bootstrap.org_id + user_id = module.zitadel-bootstrap.user_id + domain = module.zitadel.server + jwt_profile_file = module.zitadel.jwt_profile_file + + minio_access_key = module.minio.minio_access_key + minio_secret_key = module.minio.minio_secret_key + minio_service_uri = module.minio.minio_api_uri + + mongodb_connection_string = module.mongodb.connection_string + rabbitmq_connection_string = module.rabbitmq.connection_string +} + +module "zitadel-argocd" { + source = "../../infra/tenants/argocd/zitadel" + + org_id = module.zitadel-bootstrap.org_id + user_id = module.zitadel-bootstrap.user_id + domain = module.zitadel.server + jwt_profile_file = module.zitadel.jwt_profile_file + + argocd_service_domain = "argocd.${ local.cluster_dns}" +} + +module "argocd" { + source = "../../infra/modules/argocd" + wait_on = module.zitadel-argocd.installed + + namespace = "argocd" + k8s_config_yaml = local.k8s_config_yaml + + redis_db_start_index = 0 + redis_password = module.redis.password + server_dns = local.cluster_dns + + oauth_uri = module.zitadel.server + oauth_client_id = module.zitadel-argocd.client_id + oauth_client_secret = module.zitadel-argocd.client_secret + oauth_redirect_uri = "https://${module.zitadel.server}/${module.zitadel-argocd.logoutSuffix}" + oauth_issuer = "https://${module.zitadel.server}" +} + +/* + argocd project + */ + +output "argocd-root-password" { + value = module.argocd.admin_password + sensitive = true +} diff --git a/shuttles/terraform/zitadel-admin-sa.json b/shuttles/terraform/zitadel-admin-sa.json index 2f69f87..129b06a 100755 --- a/shuttles/terraform/zitadel-admin-sa.json +++ b/shuttles/terraform/zitadel-admin-sa.json @@ -1 +1 @@ -{"type":"serviceaccount","keyId":"308082423731192134","key":"-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA6/zPM5/433GWDALxbB5O7XlLWvdjEy1lBJITx73XZaoo23o1\nt89QOcACGCR4gIHUzN45GDsEF46n8QFzsZknYiL7Rz4din4q0UShxASD960hgmHT\ni443UgpvJBSoZdByzsXJq4BraoCQAQe0MiFagV+ELvF3Gl6vE/a24TpgfFrk+kLf\nHykXccllUNM+43RfjM+GI0GX5Ru5PUwsUMlgg2PRLHh70Yy+r/U9M9nRWuB4d2pR\neDs9WUM+LtsoyGcYLSIqzsj+dzD/zPOWDjDQjSK+QnsisWN86fZG4E+8fSEM6Vti\nv2gwQCBrquHFjYJx0M8IHkyMhmTUWO/X1/bDdwIDAQABAoIBAQCqo8K8BLfz8nFa\nPCKQWJYf924619nHOzLpjCtLaoW/WLx0f7kqMXlEWQfqpyyPV9IlVacZU0tRMXFU\nvwUGbcVhnAPw7wRzaWnSqnsCf3VhecpaOhy6s9pis3LMNzfRb1olFDYQkiqZ916p\nsAVnnmCQXh35x9aktcSJfgOddxXftIKARZdGzuPFL6q8xbk5TeJHqkrH5yh2bwH2\nDGdzv2Uet3DlNbTxj74LmFB08DbFZejWYko0iPSQTexZIF1OfgVzsrSyEzRUzVKS\nGyWSA1rt+pAiU/6OXzQziC9pp2prUleaiiMaDX1eKk7+Wnt2QYFgt2cj9rOwnjMX\nokzhhdH5AoGBAPu7TaXpAqOrlFeeNLR4oo0CuFhJouzlFS3nqN+7CdN0GGgG3fBJ\nwACPHJJ1nyAc+em1YW33adiBIaopl/McD1dWBjR6PHYTxcVhwy8ZR6zd2+ANjTxt\n4xWzesTo43HaL9CDgxijFuhu9p2K8qibPUH3bvt76BIY4LIi7q4wi4PTAoGBAO/9\nKk6THS88x+pT7Gaxa0bSbq4L08TD0Q+2bkI3rq5UpimkR5WOKOTPl2otBGtGozVb\nZ60VWmgjXRFnKTDZlAOUTPZoNo/EgHecgZ/cjDJVVw1UcGSmMDpAqc8yHMGN5aS3\n53Xkwla5Kd0eDTFlDH5upwzF14dQwum43OKFJk9NAoGAcLwzTAgMPIxMxOh/JguY\nkJoVQMTJiFoXoBVNVdUH04D8pSneHwyIRjfRTe4LM3CGo5mtZngVGPbLws8Z07BP\nVoeaA8pyZVb5jSn5JZCmvs4k+DRZeiSkPEXCx4icw5I5M6KSBWiUi8eV/nwJrGTJ\njE9pjoqxJdmqV1y5k5z9/i0CgYBqRJRjTslxjVyI6riXkKCML8pKz3nqezsr1gBk\n2ftDvsAFWHVKSDTbfQDzRxPZ3OGQUvVJs1bhHier+R5IP1BHdXfO9L6UUtMAJBFS\nFHsQHrH5eRZ7uwtIADIxSWNqYcb6dWTM04sqk7tx6ki1QqWRprXZ9hFGHgDxNaa5\nt3QLUQKBgQC2lAXCfWng3CXMLaLLOlc6zGe4M3wIfAiG4DCS8alFfkkxqnfYe628\n6kNlLwp8FUPdKrvWnQuTQMnn+Lleowi22Ii2yULVRK/KZPb+2GmrHxLUunlou2pm\noI5X9kOJafUP+Go+OiK0KbQnTE0UXUDwuihjQq59HGPO2AaEM9uYoA==\n-----END RSA PRIVATE KEY-----\n","expirationDate":"2026-01-01T00:00:00Z","userId":"308082423731126598"} +{"type":"serviceaccount","keyId":"308542807332225577","key":"-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAzjNVWZ/vYu2AyrYpDa36tOc7tdRfnTXxUUpUX4Yp8uVnZe2M\nlRJcMl+B4WXiVINhb2cJqKKHp+zzYiUJBCtLACXHWEPBsuNk+mmP+cot5q8lf9VY\nY7jKiUlKSFhuau0bkXTuxsest8Tau9kwt8z0f0N8Rh4Tsqg8uAMjxIfVg+NYOPrV\nVkyCZRowuquF7xyS+qm15HDIhao5y7QcuqSSr/V0DC46iVk/SiSAMSBCKwmgFL5e\naG/dcaT1euzxBxJX/ViAPiOL6rX1v3EjHm9HPhdQd7UuYiueqKPd8nnqBElTG8pV\n3JXcP00Rx8g89IGnYJFlaUrzOkatKBwonzVp0wIDAQABAoIBADr4tWUvd3AsipPu\n6ujNpBDOvOXwufOlNMHR5zV2klK0RxEAkh8kJBgH9oY29DbFaD1LE5kV+oQNIUIl\nY2G82/NL2qoknqYBoKR5QkLnDp+V4bygkGnctJf0zPjJybJs42CDN7LD8mKQOxpU\nMDmwtRAchdMr0OhccxkRVM5lJqxT+ixmwnD9hhhZQsfvrj4j9ALFTDYhdteJpC3t\n4KUMBmlV6PWwXw3gAstlZTXkHlp/BKrY407fOs9GYm5yAz0yoUgoSRawepQWX4Vp\n4qp8l5oI7veGIBE8H0Om9P8O7JoVWzH97iO3nHe0BYekInQgn60C0SshVaDVk8zc\nJ6UsmtkCgYEA7f5H2WI5yG4yO8IxRbx86S0xME+pjkw7GABLl3Ug8N7AKawh3BoU\n2ve5kPo6TA9l3MuUOqYRDAGTzvdhhgEC/V8oaYXKT/73CIcUgoF3cI642Ugo/UIo\n7LqaGy5qaoy2LqwQp+WVEefQ0qImz1c6sVXEBo1kPThxohbaCkYs/x8CgYEA3c1B\nSyX+xTGF8TY8EzyT+DguDmfOMZCb+fxw96J23m2svj7CGQvjM/ctQiGBMyOcdA4Z\nFAOtezuFNczJ6OIc3oiyBJ9/Anc9czZZNjH6ZDuCyIdzuLiWFwvZ+Yk1Gq5w9xJv\nvqIJkfuVsGvFbjY1qJcYTFo/nfPNW8hkIoJVIs0CgYEA0Czy0DXZcXbivd75lpRv\nds+vDSFBoVURA8eOV6d+7vMJh+onnA28XGUAjs4ynEGDyoTQ3hRRKP1gO1OsnLjZ\n0qOgB92dwCe4El+GEzoILg7JplY2dIGgpqH7Fvec4iK/YUflMdfic00hHn30EL2u\ne1wYIdsf6WUsEKqIgyArT+UCgYBMzhS8Fw4f7sQ5ANTQvHtoytt39Y47L54zdK0R\ns1qCL2xP/J9t2OX2SrOLYNrCgKRes7sPaS6bq8K3HJEWaaYhkShD3Y83pV7MFJfT\n4n6YUts44V67KoAevuXeORsAgENx6xpy4t15hasSCl+1iGQQWzH4zo+U/KWyELRM\nFuGwSQKBgFVWScjsWJnWyoZua/+F/r6FvPp8Bknm7N66Wl3Rb6h1q0CoQHnMx3HR\nPvJ2A6caaOt8QiBQDgu+23DaGBjRe8RxDkc4tVTDUWv4RCqub5gELkRW2DYtESdg\nvhMnXkYVubBZj8IRcYPyNddjV4SzlK67RIL/bMrYisAz+PHRYZFU\n-----END RSA PRIVATE KEY-----\n","expirationDate":"2026-01-01T00:00:00Z","userId":"308542807332160041"}