Add to an existing installation
View as MarkdownIf Materialize is already running, you can add the advanced SSO stack
without redeploying it. This guide assumes you manage Materialize with the
Materialize Terraform modules,
including the materialize-instance module.
Materialize is switched over in a separate apply at the end, so you can check the stack before any user signs in through it:
- Create the Ory databases.
- Add the
ory-stackmodule and check that it’s healthy. Materialize keeps using its current authentication. - Point Materialize at Hydra.
Before you begin
Complete the prerequisites.
Step 1: Create the Ory databases
Kratos and Hydra each need their own PostgreSQL database, and Polis needs one too if you plan to enable SAML. They can share a PostgreSQL server, either your existing one or a dedicated instance, as long as the Kubernetes cluster can reach it. The enterprise examples use a managed instance (Cloud SQL, Azure Database for PostgreSQL flexible server, or RDS) in the same network as the cluster.
-
Create a user and the databases. Each component runs its own schema migrations on startup, so the user must own its databases:
CREATE USER oryadmin WITH PASSWORD '<password>'; CREATE DATABASE kratos OWNER oryadmin; CREATE DATABASE hydra OWNER oryadmin; -- Only if you plan to enable SAML: CREATE DATABASE polis OWNER oryadmin; -
Build a connection string for each database. URL-encode the password, for example with Terraform’s
urlencode():locals { ory_kratos_dsn = "postgres://oryadmin:${urlencode(var.ory_db_password)}@<host>:5432/kratos?sslmode=require" ory_hydra_dsn = "postgres://oryadmin:${urlencode(var.ory_db_password)}@<host>:5432/hydra?sslmode=require" # uselibpqcompat=true keeps sslmode=require at libpq semantics (encrypt, # don't verify), which Polis's driver needs against managed servers. ory_polis_dsn = "postgres://oryadmin:${urlencode(var.ory_db_password)}@<host>:5432/polis?sslmode=require&uselibpqcompat=true" }
Step 2: Add the Ory stack
-
Add the
ory-stackmodule next to your existing modules, substituting your own hostnames andClusterIssuer:module "ory" { source = "github.com/MaterializeInc/materialize-terraform-self-managed//kubernetes/modules/ory-stack?ref=<RELEASE_TAG>" namespace = "ory" hydra_fqdn = "hydra.example.com" kratos_fqdn = "kratos.example.com" ui_fqdn = "auth.example.com" kratos_dsn = local.ory_kratos_dsn hydra_dsn = local.ory_hydra_dsn # Use the ory_oel_image_tag default from the enterprise example at the same release. oel_image_tag = "<ORY_IMAGE_TAG>" license_key_jwt = var.license_key cert_issuer_ref = { name = "letsencrypt-prod" kind = "ClusterIssuer" } # true for an in-cluster self-signed issuer, false for a public ACME issuer cert_issuer_signs_cluster_local = false # Registers the Materialize Console as an OAuth2 client in Hydra. materialize_namespace = "materialize-environment" materialize_console_fqdn = "console.example.com" } output "ory_lb_addresses" { value = module.ory.lb_addresses }The load balancer settings differ by cloud. Copy
lb_annotations, and on AWSlb_load_balancer_classandlb_external_traffic_policy, from themodule "ory"block in the enterprise example for your cloud (AWS, Azure, GCP). To enable SAML, also setenable_polis,polis_fqdn, andpolis_dsn = local.ory_polis_dsn; see Configure identity providers. -
Apply:
terraform init -upgrade terraform apply -
Create DNS records pointing the Hydra, Kratos, and selfservice UI hostnames at the addresses in
terraform output ory_lb_addresses: an A record for an IP (Azure, GCP) or a CNAME for a hostname (AWS). -
Check that Hydra serves its discovery document, with an
issuerthat matcheshydra_fqdn:curl -fsSL https://hydra.example.com/.well-known/openid-configuration | jq .issuer
Step 3: Point Materialize at Hydra
-
In your existing
materialize-instancemodule, switch authentication to OIDC and set the OIDC parameters from theory-stackoutputs:module "materialize_instance" { # ... your existing settings ... authenticator_kind = "Oidc" # Password for the mz_system admin user, kept as a fallback under OIDC. external_login_password_mz_system = var.external_login_password_mz_system # With network policies enabled, lets Materialize reach Hydra for its signing keys. ory_namespace = "ory" system_parameters = { oidc_issuer = module.ory.hydra_external_url oidc_audience = jsonencode([module.ory.oauth2_client_id]) oidc_authentication_claim = "email" console_oidc_client_id = module.ory.oauth2_client_id console_oidc_scopes = "openid email" # Optional: grant roles from IdP groups (see Enable role mapping). oidc_group_role_sync_enabled = "true" } # Set a new UUID so environmentd restarts with the new parameters. force_rollout = "<NEW_UUID>" }Users keep their existing SQL roles as long as the
emailclaim matches the role names. If your users currently sign in with passwords, see Migrate to SSO. -
Apply:
terraform apply
Step 4: Verify sign-in
Smoke-test each browser-facing endpoint. These commands assume a publicly trusted issuer (cert_issuer_ref set). With the default self-signed issuer, fetch its CA first and pass --cacert ca.crt to each curl:
kubectl -n cert-manager get secret <name_prefix>-root-ca -o jsonpath='{.data.ca\.crt}' | base64 -d > ca.crt
# Hydra OIDC discovery (issuer should match ory_hydra_fqdn)
curl -fsSL https://hydra.example.com/.well-known/openid-configuration | jq .issuer
# Kratos health
curl -fsSL https://kratos.example.com/health/ready
# Selfservice UI health
curl -fsSL https://auth.example.com/health/alive
# Polis health (only when enable_polis = true)
curl -fsSL https://polis.example.com/api/health
# Materialize console (expect HTTP 200)
curl -fsSL -o /dev/null -w "%{http_code}\n" https://console.example.com
Then sign in end to end, which is what proves SSO works:
- Open
https://console.example.com. You are redirected to the selfservice UI atauth.example.com, with one button perupstream_identity_providersentry and persaml_providersentry. Each button’s text comes from that entry’slabel; see Configure identity providers. - Sign in through one of them. You should land back in the Console as that user.
- Run
SELECT current_user;. It should return the user’s email.