Configure OpenSearch SSO with Authentik
ClusterNest Managed OpenSearch can delegate Dashboards login to Authentik over OIDC or SAML. A cluster takes several auth sources side by side, with at most one SAML source and one OIDC source offered on Dashboards.
You'll need your cluster's OpenSearch Dashboards hostname before registering the app in Authentik. Set dashboards_custom_hostname so you know it upfront, or create the cluster first without an auth source and note the generated opensearch_dashboards_url output before adding it.
- OIDC
- SAML
OIDC
Authentik's default ID token doesn't include a groups claim unless the groups scope is attached to the provider - without it, drop roles_key and rely on OpenSearch's internal role mapping by username (subject_key, default preferred_username) instead.
- Console
- Terraform
- In Authentik, go to Applications > Providers > Create, type OAuth2/OpenID Provider.
- Client type: Confidential
- Signing Key: an RSA key such as the built-in authentik Self-signed Certificate, so tokens are signed with RS256 and carry a usable JWKS
- Redirect URIs:
https://<dashboards-hostname>/auth/openid/login(Strict, or a regex match if the hostname isn't final yet) - Scopes: include
openid,email,profile, and addgroupsif you want role mapping via group membership. - Advanced protocol settings > Grant types: enable Authorization Code and Refresh Token. If the list is empty, every login fails with
invalid_requestand Dashboards loops with "too many redirects".
- Go to Applications > Applications > Create, give it a slug (e.g.
opensearch), and link it to the provider above. - Open the provider again - it now shows the Client ID and Client Secret.
connect_url is https://<authentik-host>/application/o/<app-slug>/.well-known/openid-configuration.
In the ClusterNest console, open the cluster's create/edit form and add an OIDC source under Authentication. Choose a Name (it becomes the text of the Dashboards button, "Login with" followed by this name), tick Offer on OpenSearch Dashboards, then fill in:
- Connect URL:
https://auth.example.com/application/o/opensearch/.well-known/openid-configuration - Client ID / Client Secret: from the provider page
- Roles Key:
groups(leave blank to skip role mapping)
Authentik has an official provider (goauthentik/authentik) that can create the OAuth2 provider and application, wired directly into the same config as the ClusterNest cluster:
terraform {
required_providers {
authentik = {
source = "goauthentik/authentik"
version = "~> 2026.5"
}
clusternest = {
source = "tf.clusternest.com/clusternest/clusternest"
version = ">=1.1.0"
}
}
}
provider "authentik" {
url = "https://auth.example.com"
token = var.authentik_token
}
provider "clusternest" {
email = "[email protected]"
password = var.clusternest_app_password
}
locals {
dashboards_hostname = "opensearch-dashboards.example.com"
}
data "authentik_flow" "authorization" {
slug = "default-provider-authorization-implicit-consent"
}
data "authentik_flow" "invalidation" {
slug = "default-provider-invalidation-flow"
}
data "authentik_certificate_key_pair" "signing" {
name = "authentik Self-signed Certificate"
}
resource "authentik_provider_oauth2" "opensearch" {
name = "opensearch"
client_id = "opensearch"
authorization_flow = data.authentik_flow.authorization.id
invalidation_flow = data.authentik_flow.invalidation.id
signing_key = data.authentik_certificate_key_pair.signing.id
allowed_redirect_uris = [
{
matching_mode = "strict"
url = "https://${local.dashboards_hostname}/auth/openid/login"
}
]
}
resource "authentik_application" "opensearch" {
name = "OpenSearch"
slug = "opensearch"
protocol_provider = authentik_provider_oauth2.opensearch.id
}
resource "clusternest_opensearch" "sso" {
name = "logs"
tier = "standard"
organization_id = 123
auth_sources = [
{
name = "authentik"
type = "oidc"
connect_url = "https://auth.example.com/application/o/${authentik_application.opensearch.slug}/.well-known/openid-configuration"
client_id = authentik_provider_oauth2.opensearch.client_id
client_secret = authentik_provider_oauth2.opensearch.client_secret
roles_key = "groups"
dashboards_login = true
},
]
dashboards_custom_hostname = local.dashboards_hostname
}
client_secret is left unset on the provider so Authentik generates one - it comes back as a sensitive computed attribute. Add the groups scope to authentik_provider_oauth2.opensearch if you're using roles_key, and make sure its grant types include Authorization Code and Refresh Token.
SAML
roles_key defaults to an ADFS-style claim URI - it won't match anything Authentik emits unless you add a matching Property Mapping to the provider. Point it at whatever SAML attribute name your Property Mappings actually produce, or drop it if you're not mapping roles.
- Console
- Terraform
- In Authentik, go to Applications > Providers > Create, type SAML Provider.
- ACS URL:
https://<dashboards-hostname>/_opendistro/_security/saml/acs - Issuer: the value you'll use as
sp_entity_idbelow (any identifier you choose, e.g.opensearch-dashboards) - Audience: the same value as the Issuer
- Signing Certificate: an RSA key such as the built-in authentik Self-signed Certificate, with Sign assertion and Sign response both enabled. OpenSearch rejects unsigned responses.
- Service Provider Binding: Post (the default, Redirect, returns the response as a GET and Dashboards answers 401)
- ACS URL:
- Go to Applications > Applications > Create, give it a slug (e.g.
opensearch), and link it to the provider above. - Open the provider's detail page - it shows a metadata URL in the form
https://<authentik-host>/api/v3/providers/saml/<id>/metadata/?download. That's youridp_metadata_url. - The entity ID Authentik issues for this app is
https://<authentik-host>/application/saml/<app-slug>/metadata/- use it asidp_entity_id. It must match exactly (including trailing slash) or OpenSearch Dashboards will reject the assertion with "Authentication finally failed".
In the ClusterNest console, open the cluster's create/edit form and add a SAML source under Authentication. Choose a Name (it becomes the text of the Dashboards button, "Login with" followed by this name), then fill in:
- IDP Metadata URL:
https://auth.example.com/api/v3/providers/saml/3/metadata/?download - IDP Entity ID:
https://auth.example.com/application/saml/opensearch/metadata/ - SP Entity ID:
opensearch-dashboards - Roles Key:
http://schemas.xmlsoap.org/claims/Group(only if you added a matching Property Mapping)
terraform {
required_providers {
authentik = {
source = "goauthentik/authentik"
version = "~> 2026.5"
}
clusternest = {
source = "tf.clusternest.com/clusternest/clusternest"
version = ">=1.1.0"
}
}
}
provider "authentik" {
url = "https://auth.example.com"
token = var.authentik_token
}
provider "clusternest" {
email = "[email protected]"
password = var.clusternest_app_password
}
locals {
dashboards_hostname = "opensearch-dashboards.example.com"
}
data "authentik_flow" "authorization" {
slug = "default-provider-authorization-implicit-consent"
}
data "authentik_flow" "invalidation" {
slug = "default-provider-invalidation-flow"
}
data "authentik_certificate_key_pair" "signing" {
name = "authentik Self-signed Certificate"
}
resource "authentik_provider_saml" "opensearch" {
name = "opensearch"
authorization_flow = data.authentik_flow.authorization.id
invalidation_flow = data.authentik_flow.invalidation.id
acs_url = "https://${local.dashboards_hostname}/_opendistro/_security/saml/acs"
sp_binding = "post"
audience = "opensearch-dashboards"
signing_kp = data.authentik_certificate_key_pair.signing.id
sign_assertion = true
sign_response = true
}
resource "authentik_application" "opensearch" {
name = "OpenSearch"
slug = "opensearch"
protocol_provider = authentik_provider_saml.opensearch.id
}
resource "clusternest_opensearch" "sso" {
name = "logs"
tier = "standard"
organization_id = 123
auth_sources = [
{
name = "authentik"
type = "saml"
idp_metadata_url = "https://auth.example.com/api/v3/providers/saml/${authentik_provider_saml.opensearch.id}/metadata/?download"
idp_entity_id = "https://auth.example.com/application/saml/${authentik_application.opensearch.slug}/metadata/"
sp_entity_id = "opensearch-dashboards"
roles_key = "http://schemas.xmlsoap.org/claims/Group"
},
]
dashboards_custom_hostname = local.dashboards_hostname
}
Add a property_mappings entry on authentik_provider_saml.opensearch (an authentik_property_mapping_provider_saml resource) if you want roles_key to actually carry group data - otherwise drop it.
Restrict who can log in
An Authentik application with no policy bindings is open to every Authentik user. To limit it, open the application, go to Policy / Group / User Bindings, and bind the groups or users who should have access. Anyone else is refused by Authentik before they reach OpenSearch.
Map roles in OpenSearch
A user who logs in successfully has no permissions yet. ClusterNest does not create role mappings, so map them yourself in OpenSearch Dashboards under Security > Roles > Mapped users, or through the security API.
- With
roles_keyset, map the values of that claim (for example an Authentik group name) as backend roles. - Without it, map the username as a user. The username comes from
subject_key.
Troubleshooting
- Already signed-in users get a 502 on SAML login (ingress-nginx logs
upstream sent too big header): this happens when Authentik runs behind ingress-nginx and the SAML request is large. Addnginx.ingress.kubernetes.io/proxy-buffer-size: "16k"andnginx.ingress.kubernetes.io/proxy-buffers-number: "4"annotations to the Ingress that serves the Authentik server.
See the Terraform resource reference for the full field list.