Using Keycloak as an IdP

16 views
Skip to first unread message

Jacob Molland

unread,
Jan 15, 2026, 1:33:53 PM (3 days ago) Jan 15
to Wazuh | Mailing List

Hi all,

I have been configuring Wazuh to use Keycloak as an identity provider. Both of the Wazuh and Keycloak deployments are kubernetes-based, existing on an air-gapped server. I access the UIs for these apps via ‘kubectl port-forward’ to forward Wazuh from the air-gapped server to my local machine on localhost:31000 and Keycloak to localhost:30000.

I have been following along with this Keycloak integration guide from Wazuh: https://documentation.wazuh.com/current/user-manual/user-administration/single-sign-on/administrator/keycloak.html. However, after setting ‘opensearch_security.auth.type: "saml"’ in the opensearch_dashboards.yml file and navigating to https://localhost:31000 in my browser to access the Wazuh UI, I get the following message:

{"statusCode":500,"error":"Internal Server Error","message":"Internal Error"}

When ‘opensearch_security.auth.type: "saml"’ is commented out, the dashboard is accessible.

The guide references ‘<WAZUH_DASHBOARD_URL>’, which Wazuh says to replace with the corresponding URL of the Wazuh dashboard instance. For me, this is ‘https://localhost:31000’, but I’m unsure of its inclusion in the Wazuh manifest due to Wazuh and Keycloak existing on an airgapped server – https://localhost:31000 is just what my browser uses on my local machine to access the UI.

All (4) Wazuh pods are in the READY state, but the wazuh-dashboard pod has the following logs inside:

{"type":"log","@timestamp":"2026-01-14T23:23:19Z","tags":["error","plugins","securityDashboards"],"pid":55,"message":"Failed to get saml header: Authentication Exception :: {\"path\":\"/_plugins/_security/authinfo\",\"query\":{\"auth_type\":\"saml\"},\"statusCode\":401,\"response\":\"Authentication finally failed\"}"}

{"type":"error","@timestamp":"2026-01-14T23:23:19Z","tags":[],"pid":55,"level":"error","error":{"message":"Internal Server Error","name":"Error","stack":"Error: Internal Server Error\n at HapiResponseAdapter.toError (/usr/share/wazuh-dashboard/src/core/server/http/router/response_adapter.js:127:19)\n    at HapiResponseAdapter.toHapiResponse (/usr/share/wazuh-dashboard/src/core/server/http/router/response_adapter.js:83:19)\n    at HapiResponseAdapter.handle (/usr/share/wazuh-dashboard/src/core/server/http/router/response_adapter.js:79:17)\n    at Router.handle (/usr/share/wazuh-dashboard/src/core/server/http/router/router.js:175:34)\n at processTicksAndRejections (node:internal/process/task_queues:95:5)\n at handler (/usr/share/wazuh-dashboard/src/core/server/http/router/router.js:140:50)\n at exports.Manager.execute (/usr/share/wazuh-dashboard/node_modules/@hapi/hapi/lib/toolkit.js:60:28)\n at Object.internals.handler (/usr/share/wazuh-dashboard/node_modules/@hapi/hapi/lib/handler.js:46:20)\n    at exports.execute (/usr/share/wazuh-dashboard/node_modules/@hapi/hapi/lib/handler.js:31:20)\n at Request._lifecycle (/usr/share/wazuh-dashboard/node_modules/@hapi/hapi/lib/request.js:371:32)\n    at Request._execute (/usr/share/wazuh-dashboard/node_modules/@hapi/hapi/lib/request.js:281:9)"},"url":"https://localhost:31000/auth/saml/login?redirectHash=false&nextUrl=%2F","message":"Internal Server Error"}


I have the following configured inside opensearch_dashboards.yml, mounted from a ConfigMap (I added the last (3) lines per Wazuh’s guide for Keycloak integration); when ‘opensearch_security.auth.type: "saml"’ is commented out, everything works fine:

opensearch_dashboards.yml: |2-

    server.host: 0.0.0.0

    server.port: 5601

    opensearch.hosts: https://indexer:9200

    opensearch.ssl.verificationMode: none

    opensearch.requestHeadersWhitelist: [ authorization,securitytenant ]

    opensearch_security.multitenancy.enabled: false

    opensearch_security.readonly_mode.roles: ["kibana_read_only"]

    server.ssl.enabled: true

    server.ssl.key: "/usr/share/wazuh-dashboard/certs/key.pem"

    server.ssl.certificate: "/usr/share/wazuh-dashboard/certs/cert.pem"

    opensearch.ssl.certificateAuthorities: ["/usr/share/wazuh-dashboard/certs/root-ca.pem"]

    uiSettings.overrides.defaultRoute: /app/wz-home

    # Session expiration settings

    opensearch_security.cookie.ttl: 900000

    opensearch_security.session.ttl: 900000

    opensearch_security.session.keepalive: false

    opensearch_security.auth.type: "saml"

    server.xsrf.allowlist: ["/_opendistro/_security/saml/acs", "/_opendistro/_security/saml/logout", "/_opendistro/_security/saml/acs/idpinitiated"]


These are my idp.metadata.xml and sp.metadata.xml files, downloaded from the Wazuh UI after configuring it per the Wazuh guide mentioned above. Since the Wazuh Dashboard URL is located at https://localhost:31000, idp.metadata.xml originally has ‘localhost:31000’ in every spot that has ‘keycloak.keycloak:8443’; I changed the original ‘localhost’ URL to the ‘keycloak’ URL because I thought it made sense for Wazuh and Keycloak to communicate with each other:

  idp.metadata.xml: |

    <md:EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="https://keycloak.keycloak:8443/realms/Wazuh">

    <md:IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">

    <md:KeyDescriptor use="signing">

    <ds:KeyInfo>

    <ds:KeyName>EL4xyYqoA-qpzxJ7O8PCu1gJNOQz-UaFo3QYKls4mCw</ds:KeyName>

    <ds:X509Data>

    <ds:X509Certificate>MIICmTCCAYECBgGbvfECHDANBgkqhkiG9w0BAQsFADAQMQ4wDAYDVQQDDAVXYXp1aDAeFw0yNjAxMTQxOTE1NDdaFw0zNjAxMTQxOTE3MjdaMBAxDjAMBgNVBAMMBVdhenVoMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAryufq09IzkdYZmTHx113vjeOYA8pfAgCmgsEEGiGuyoEqwO3QZS2JUoL7qG/BOdoDFtyGGX/HHR/xJT73ajKDpBeQVWGxn65sp5Kjnqo6CGDmV1EiK8yE6pA5JutXQibrsc6nSQ5RJlcke8w2zHWIR9v7qbPBfVJhN0z+uRq8rKMDqXeKiKphUeU5ylz8BwfubBGZ6G90+lO8a5x7FFPoDm8kpbB3dNoj6kM1hPsR83290p9ED+YoNzjKO4dNWsAYbdB2ipNylyWRali3j/XM2+lPa6DT8ZbYKm3Z3xN/EeQoYXufrq3k21nuVnxWx8GO2KOdyyuuj4+1l1R/HAFowIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQB96CWF1T/moB3b3/Q1zlBl/LrMt44JjtJ+5T5K1rSK0A9XG/6xQFmEwyrR3MX7iWF8k+5LazeOz6JSCs3r/rgBeN7oc8bN5IJCmN0eenMjKhiu2RAPsVDKPsY3n5YghNY3zDnrfzEaLyXebJJBW36MDWJMLlpyFAiVsJBDof3EFequOxQpQUSy9cfU5WnoVGRJHB758hdwrUy/Y3XFOR08vgijrevLlfSokrxxpE+xNgkyc7H77Rv/D5hXr9kXv/u5MAY9X0EYWSMteArZ7/9bDPM5ZX58PZdkX2GzHEi5SBLsg3sywAqMWvx2gyM3DOu97om3GkdYsLgRezOhWuQR</ds:X509Certificate>

    </ds:X509Data>

    </ds:KeyInfo>

    </md:KeyDescriptor>

    <md:ArtifactResolutionService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://keycloak.keycloak:8443/realms/Wazuh/protocol/saml/resolve" index="0"/>

    <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://keycloak.keycloak:8443/realms/Wazuh/protocol/saml"/>

    <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://keycloak.keycloak:8443/realms/Wazuh/protocol/saml"/>

    <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="https://keycloak.keycloak:8443/realms/Wazuh/protocol/saml"/>

    <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://keycloak.keycloak:8443/realms/Wazuh/protocol/saml"/>

    <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat>

    <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>

   <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>

   <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>

    <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://keycloak.keycloak:8443/realms/Wazuh/protocol/saml"/>

    <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://keycloak.keycloak:8443/realms/Wazuh/protocol/saml"/>

    <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://keycloak.keycloak:8443/realms/Wazuh/protocol/saml"/>

    <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="https://keycloak.keycloak:8443/realms/Wazuh/protocol/saml"/>

    </md:IDPSSODescriptor>

    </md:EntityDescriptor>

 

  sp.metadata.xml: |

    <md:EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="wazuh-saml" ID="ID_461f5781-5850-4268-97cb-013a2bdeb378">

    <md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" AuthnRequestsSigned="false" WantAssertionsSigned="true">

    <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://wazuh-indexer:9200/_plugins/_security/saml/logout"/>

    <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>

    <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://wazuh-indexer:9200/_plugins/_security/saml/acs" isDefault="true" index="1"/>

    </md:SPSSODescriptor>

    </md:EntityDescriptor>

 

Here is what I added to my opensearch-security/config.yml file:

        authc:

          kerberos_auth_domain:

            http_enabled: false

            transport_enabled: false

            order: 6

            http_authenticator:

              type: kerberos

              challenge: true

              config:

                # If true a lot of kerberos/security related debugging output will be logged to standard out

                krb_debug: false

                # If true then the realm will be stripped from the user name

                strip_realm_from_principal: true

            authentication_backend:

              type: noop

          basic_internal_auth_domain:

            description: "Authenticate via HTTP Basic against internal users database"

            http_enabled: true

            transport_enabled: true

            order: 0

            http_authenticator:

              type: basic

              challenge: false

            authentication_backend:

              type: intern

          saml_auth_domain:

            http_enabled: true

            transport_enabled: false

            order: 1

            http_authenticator:

              type: saml

              challenge: true

              config:

                idp:

                  metadata_file: '/usr/share/wazuh-indexer/opensearch-security/idp.metadata.xml'

                  entity_id: 'https://keycloak.keycloak:8443/realms/Wazuh'

                sp:

                  metadata_file: /usr/share/wazuh-indexer/opensearch-security/sp.metadata.xml

                  entity_id: wazuh-saml

                kibana_url: https://localhost:31000

                roles_key: Roles

                exchange_key: 'c32a1565000ddc483fa0f22c14462b950aeb0dcda3be3b0b451f67852320d9aa'

            authentication_backend:

              type: noop

 

I added the following to my roles_mapping.yml file:

all_access:

  reserved: false

  hidden: false

  backend_roles:

  - "admin"


I also confirmed that the value of ‘run_as’ in the config/wazuh.yml file is set to ‘false’.

Closing questions:
- What should <WAZUH_DASHBOARD_URL> be when Wazuh is running on an air-gapped server but the UI is running on a local machine on localhost:31000?
- Is a SAML block for OpensSearch Security need to be created in the Wazuh manifest?
- Does Wazuh's root-ca.pem cert have to be configured to trust Keycloak's cert? Keycloak is configured with self-signed tls.key and tls.crt secrets BTW.
- Anything else I might be missing while attempting to configure Keycloak as an identity provider for Wazuh?


Any help on this would be greatly appreciated. I've been working this for days and can't seem to figure out what's wrong here, and the LLMs are just throwing me in circles.

Best,


Jacob

raul....@wazuh.com

unread,
Jan 15, 2026, 11:36:47 PM (3 days ago) Jan 15
to Wazuh | Mailing List

Hi, 
We are carefully reviewing your case and taking into account all the information you’ve provided. We are currently performing several evaluations and will share a more detailed update with you soon. Thank you for your patience.

raul....@wazuh.com

unread,
Jan 15, 2026, 11:36:55 PM (3 days ago) Jan 15
to Wazuh | Mailing List
Hi Jacob, 


We are carefully reviewing your case and taking into account all the information you’ve provided. We are currently performing several evaluations and will share a more detailed update with you soon. Thank you for your patience.

On Thursday, January 15, 2026 at 12:33:53 PM UTC-6 Jacob Molland wrote:
Reply all
Reply to author
Forward
0 new messages