From 9c5937776e26aadd5e600ba7a7cbe7a2175bd1b3 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Sun, 19 Jul 2026 02:22:23 +1000 Subject: [PATCH] Add Ceph dashboard SAML provider Ceph dashboard SSO is SAML 2.0 (no native OIDC), so onboard it via an Authentik SAML provider + application. Also resolve SAML authorization/invalidation flows by slug and the signing keypair by name (mirrors the oauth2 handling), since the SAML path had not been exercised before. - config/providers_saml/ceph.yaml: SP entity id/ACS derived from the dashboard base URL (audience .../auth/saml2/metadata, acs .../auth/saml2, HTTP-POST), signed with the built-in self-signed keypair. Ceph side (separate, Puppet): ceph dashboard sso setup saml2 https://dashboard.ceph.unkin.net Validated with `terragrunt plan`: 2 to add (provider + application). --- config/providers_saml/ceph.yaml | 15 +++++++++++++++ modules/authentik/main.tf | 23 ++++++++++++++++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 config/providers_saml/ceph.yaml diff --git a/config/providers_saml/ceph.yaml b/config/providers_saml/ceph.yaml new file mode 100644 index 0000000..60a5af5 --- /dev/null +++ b/config/providers_saml/ceph.yaml @@ -0,0 +1,15 @@ +# SAML provider + application for the Ceph dashboard (dashboard.ceph.unkin.net). +# Ceph dashboard SSO is SAML 2.0 (no native OIDC). The SP entity id and ACS URL +# are derived by Ceph from its base URL: +# entity id (audience): /auth/saml2/metadata +# ACS url (HTTP-POST): /auth/saml2 +# Configure the Ceph side (Puppet/mgr) with: +# ceph dashboard sso setup saml2 https://dashboard.ceph.unkin.net +name: Ceph Dashboard +authorization_flow: default-provider-authorization-implicit-consent +invalidation_flow: default-provider-invalidation-flow +acs_url: https://dashboard.ceph.unkin.net/auth/saml2 +audience: https://dashboard.ceph.unkin.net/auth/saml2/metadata +sp_binding: post +# Authentik's built-in self-signed keypair, resolved by name; signs assertions. +signing_kp: authentik Self-signed Certificate diff --git a/modules/authentik/main.tf b/modules/authentik/main.tf index fa84126..a92444e 100644 --- a/modules/authentik/main.tf +++ b/modules/authentik/main.tf @@ -52,17 +52,34 @@ resource "authentik_property_mapping_provider_scope" "groups_hierarchical" { EOT } +# Resolve SAML flows by slug and the signing keypair by name, so configs use +# human-readable names instead of Authentik UUIDs (mirrors the oauth2 handling). +data "authentik_flow" "saml_authorization" { + for_each = var.providers_saml + slug = each.value.authorization_flow +} + +data "authentik_flow" "saml_invalidation" { + for_each = var.providers_saml + slug = each.value.invalidation_flow +} + +data "authentik_certificate_key_pair" "saml_signing" { + for_each = { for k, v in var.providers_saml : k => v if v.signing_kp != null } + name = each.value.signing_kp +} + resource "authentik_provider_saml" "this" { for_each = var.providers_saml name = each.value.name - authorization_flow = each.value.authorization_flow - invalidation_flow = each.value.invalidation_flow + authorization_flow = data.authentik_flow.saml_authorization[each.key].id + invalidation_flow = data.authentik_flow.saml_invalidation[each.key].id acs_url = each.value.acs_url sp_binding = each.value.sp_binding audience = each.value.audience name_id_mapping = each.value.name_id_mapping - signing_kp = each.value.signing_kp + signing_kp = each.value.signing_kp != null ? data.authentik_certificate_key_pair.saml_signing[each.key].id : null } # Resolve oauth2 flows by slug and scope mappings by managed identifier, and -- 2.47.3