Configure OpenSearch SSO with Keycloak
ClusterNest Managed OpenSearch can delegate Dashboards login to Keycloak 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 client. 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
Keycloak's ID token doesn't include realm/client roles by default - roles_key only works once a matching mapper is attached to the client.
- Console
- Terraform
- In the Keycloak admin console, go to your realm's Clients > Create client.
- Client type: OpenID Connect, Client ID: e.g.
opensearch-dashboards - Client authentication: On (confidential client), Standard flow: enabled
- Valid redirect URIs:
https://<dashboards-hostname>/auth/openid/login
- Client type: OpenID Connect, Client ID: e.g.
- On the Credentials tab, note the Client secret.
- To get a claim for
roles_key, go to the client's Client scopes tab, open its dedicated scope, and add a Group Membership mapper with token claim namegroups.
connect_url is https://<keycloak-host>/realms/<realm>/.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/realms/myrealm/.well-known/openid-configuration - Client ID / Client Secret: from the client's Credentials tab
- Roles Key:
groups(or whatever your mapper's claim name is; leave blank if you skipped step 3)
Keycloak's official provider (keycloak/keycloak) manages both the client and the role-mapping claim in the same config as the ClusterNest cluster:
terraform {
required_providers {
keycloak = {
source = "keycloak/keycloak"
version = "~> 5.0"
}
clusternest = {
source = "tf.clusternest.com/clusternest/clusternest"
version = ">=1.1.0"
}
}
}
provider "keycloak" {
url = "https://auth.example.com"
client_id = "admin-cli"
}
provider "clusternest" {
email = "[email protected]"
password = var.clusternest_app_password
}
locals {
dashboards_hostname = "opensearch-dashboards.example.com"
}
data "keycloak_realm" "myrealm" {
realm = "myrealm"
}
resource "keycloak_openid_client" "opensearch" {
realm_id = data.keycloak_realm.myrealm.id
client_id = "opensearch-dashboards"
name = "OpenSearch Dashboards"
enabled = true
access_type = "CONFIDENTIAL"
standard_flow_enabled = true
valid_redirect_uris = ["https://${local.dashboards_hostname}/auth/openid/login"]
}
resource "keycloak_openid_group_membership_protocol_mapper" "groups" {
realm_id = data.keycloak_realm.myrealm.id
client_id = keycloak_openid_client.opensearch.id
name = "groups"
claim_name = "groups"
}
resource "clusternest_opensearch" "sso" {
name = "logs"
tier = "standard"
organization_id = 123
auth_sources = [
{
name = "keycloak"
type = "oidc"
connect_url = "https://auth.example.com/realms/myrealm/.well-known/openid-configuration"
client_id = keycloak_openid_client.opensearch.client_id
client_secret = keycloak_openid_client.opensearch.client_secret
roles_key = "groups"
dashboards_login = true
},
]
dashboards_custom_hostname = local.dashboards_hostname
}
client_secret is left unset on the client so Keycloak generates one, exposed as a sensitive computed attribute. Drop the keycloak_openid_group_membership_protocol_mapper resource and roles_key if you don't need role mapping.
SAML
roles_key defaults to an ADFS-style claim URI Keycloak never emits - it only works once a matching Role List mapper is attached to the client (Client scopes > dedicated scope > Add mapper in the console).
- Console
- Terraform
- In the Keycloak admin console, go to your realm's Clients > Create client.
- Client type: SAML, Client ID: the value you'll use as
sp_entity_idbelow - Valid redirect URIs / Master SAML Processing URL:
https://<dashboards-hostname>/_opendistro/_security/saml/acs - Name ID format: email (or whatever your subject_key mapping expects)
- Client type: SAML, Client ID: the value you'll use as
- To map roles into the assertion, go to the client's Client scopes, edit the dedicated scope, and add a Role list mapper - note the SAML attribute name it's configured to emit.
idp_metadata_url is Keycloak's realm SAML descriptor endpoint - it needs no authentication. idp_entity_id is the realm issuer URL, https://<keycloak-host>/realms/<realm>.
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/realms/myrealm/protocol/saml/descriptor - IDP Entity ID:
https://auth.example.com/realms/myrealm - SP Entity ID:
opensearch-dashboards - Roles Key:
Role(or whatever your mapper's attribute name is)
terraform {
required_providers {
keycloak = {
source = "keycloak/keycloak"
version = "~> 5.0"
}
clusternest = {
source = "tf.clusternest.com/clusternest/clusternest"
version = ">=1.1.0"
}
}
}
provider "keycloak" {
url = "https://auth.example.com"
client_id = "admin-cli"
}
provider "clusternest" {
email = "[email protected]"
password = var.clusternest_app_password
}
locals {
dashboards_hostname = "opensearch-dashboards.example.com"
}
data "keycloak_realm" "myrealm" {
realm = "myrealm"
}
resource "keycloak_saml_client" "opensearch" {
realm_id = data.keycloak_realm.myrealm.id
client_id = "opensearch-dashboards"
name = "OpenSearch Dashboards"
valid_redirect_uris = ["https://${local.dashboards_hostname}/_opendistro/_security/saml/acs"]
name_id_format = "email"
}
resource "clusternest_opensearch" "sso" {
name = "logs"
tier = "standard"
organization_id = 123
auth_sources = [
{
name = "keycloak"
type = "saml"
idp_metadata_url = "https://auth.example.com/realms/myrealm/protocol/saml/descriptor"
idp_entity_id = "https://auth.example.com/realms/myrealm"
sp_entity_id = keycloak_saml_client.opensearch.client_id
},
]
dashboards_custom_hostname = local.dashboards_hostname
}
For roles_key, add a Role List mapper resource to the client and set roles_key to the SAML attribute name it's configured to emit.
See the Terraform resource reference for the full field list.