SAML not working "Failed to get saml header"

Im having trouble getting SAML to work with OD/Kibana.
Ive used a known good configuration (from what I can tell, Its copied from a working server in a different environment)
The error that im getting in the Kibana logs as as follows (repeats every few secons, regardless of login attempts):

 log   [21:37:10.980] [error][opendistroSecurityKibana][plugins] Failed to get saml header: Authentication Exception :: {"path":"/_opendistro/_security/authinfo","query":{},"statusCode":401,"response":"Authentication finally failed"}
 error  [21:37:10.977]  Error: Internal Server Error
    at HapiResponseAdapter.toError (/usr/share/kibana/src/core/server/http/router/response_adapter.js:132:19)
    at HapiResponseAdapter.toHapiResponse (/usr/share/kibana/src/core/server/http/router/response_adapter.js:86:19)
    at HapiResponseAdapter.handle (/usr/share/kibana/src/core/server/http/router/response_adapter.js:81:17)
    at Router.handle (/usr/share/kibana/src/core/server/http/router/router.js:164:34)
    at process._tickCallback (internal/process/next_tick.js:68:7)

Running the security admin script works, but takes several minutes.
Any help?

1 Like

@retorpigs Could you share your config.yml and kibana.yml files?

Kibana.yml (sensitive parts excluded):

opendistro_security.multitenancy.enabled: true
opendistro_security.multitenancy.tenants.preferred: ["Private", "Global"]
opendistro_security.readonly_mode.roles: ["kibana_read_only"]

logging.dest: /var/log/kibana/kibana.log
logging.json: false
logging.verbose: true

newsfeed.enabled: false
telemetry.optIn: false
telemetry.enabled: false

opendistro_security.auth.type: "saml"
server.xsrf.whitelist: ["/_opendistro/_security/saml/acs/idpinitiated", "/_opendistro/_security/saml/acs", "/_opendistro/_security/saml/logout"]

Config.yml (also stripped of possibly sensitive info):

_meta:
  type: "config"
  config_version: 2

config:
  dynamic:
    # Set filtered_alias_mode to 'disallow' to forbid more than 2 filtered aliases per index
    # Set filtered_alias_mode to 'warn' to allow more than 2 filtered aliases per index but warns about it (default)
    # Set filtered_alias_mode to 'nowarn' to allow more than 2 filtered aliases per index silently
    #filtered_alias_mode: warn
    #do_not_fail_on_forbidden: false
    #kibana:
    # Kibana multitenancy
    #multitenancy_enabled: true
    #server_username: kibanaserver
    #index: '.kibana'
    http:
      anonymous_auth_enabled: false
      xff:
        enabled: false
        internalProxies: '192\.168\.0\.10|192\.168\.0\.11' # regex pattern
        #internalProxies: '.*' # trust all internal proxies, regex pattern
        #remoteIpHeader:  'x-forwarded-for'
        ###### see https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html for regex help
        ###### more information about XFF https://en.wikipedia.org/wiki/X-Forwarded-For
        ###### and here https://tools.ietf.org/html/rfc7239
        ###### and https://tomcat.apache.org/tomcat-8.0-doc/config/valve.html#Remote_IP_Valve
    authc:
      basic_internal_auth_domain:
        http_enabled: true
        transport_enabled: true
        order: 0
        http_authenticator:
          type: basic
          challenge: false
        authentication_backend:
          type: internal
      saml_auth_domain:
        http_enabled: true
        transport_enabled: false
        order: 1
        http_authenticator:
          type: saml
          challenge: true
          config:
            idp:
              metadata_url: https://<SSO PROVIDER URL>/sso/saml/metadata
              entity_id: https://<SSO PROVIDER URL>/<ENTITY ID>
            sp:
              entity_id: <MY ENTITY ID>
            kibana_url: https://<SERVER URL>:5601/
            roles_key: Roles
            exchange_key: <EXCHANGE KEY>
        authentication_backend:
          type: noop
    authz:
      roles_from_myldap:
        description: "Authorize via LDAP or Active Directory"
        http_enabled: false
        transport_enabled: false
        authorization_backend:
          # LDAP authorization backend (gather roles from a LDAP or Active Directory, you have to configure the above LDAP authentication backend settings too)
          type: ldap
          config:
            # enable ldaps
            enable_ssl: false
            # enable start tls, enable_ssl should be false
            enable_start_tls: false
            # send client certificate
            enable_ssl_client_auth: false
            # verify ldap hostname
            verify_hostnames: true
            hosts:
            - localhost:8389
            bind_dn: null
            password: null
            rolebase: 'ou=groups,dc=example,dc=com'
            # Filter to search for roles (currently in the whole subtree beneath rolebase)
            # {0} is substituted with the DN of the user
            # {1} is substituted with the username
            # {2} is substituted with an attribute value from user's directory entry, of the authenticated user. Use userroleattribute to specify the name of the attribute
            rolesearch: '(member={0})'
            # Specify the name of the attribute which value should be substituted with {2} above
            userroleattribute: null
            # Roles as an attribute of the user entry
            userrolename: disabled
            #userrolename: memberOf
            # The attribute in a role entry containing the name of that role, Default is "name".
            # Can also be "dn" to use the full DN as rolename.
            rolename: cn
            # Resolve nested roles transitive (roles which are members of other roles and so on ...)
            resolve_nested_roles: true
            userbase: 'ou=people,dc=example,dc=com'
            # Filter to search for users (currently in the whole subtree beneath userbase)
            # {0} is substituted with the username
            usersearch: '(uid={0})'
            # Skip users matching a user name, a wildcard or a regex pattern
            #skip_users:
            #  - 'cn=Michael Jackson,ou*people,o=TEST'
            #  - '/\S*/'
      roles_from_another_ldap:
        description: "Authorize via another Active Directory"
        http_enabled: false
        transport_enabled: false
        authorization_backend:
          type: ldap
          #config goes here ...
  #    auth_failure_listeners:
  #      ip_rate_limiting:
  #        type: ip
  #        allowed_tries: 10
  #        time_window_seconds: 3600
  #        block_expiry_seconds: 600
  #        max_blocked_clients: 100000
  #        max_tracked_clients: 100000
  #      internal_authentication_backend_limiting:
  #        type: username
  #        authentication_backend: intern
  #        allowed_tries: 10
  #        time_window_seconds: 3600
  #        block_expiry_seconds: 600
  #        max_blocked_clients: 100000
  #        max_tracked_clients: 100000

@retorpigs Both configs look correct.
Can you login to the Kibana portal successfully? Do you see any errors on Elasticsearch servers? Any issues during the Elasticsearch startup?

Was this always working like that or did the issue start recently?

What is your SAML IdP and what is the ODFE version?

Hey @retorpigs

Did you find a solution for this? Running into the same exact issue.

@YassineLazaar Could you open a new thread and share your configs?

@YassineLazaar Turns out that the machine was in a VLAN that didnt have access to the internet, so it was unable to get the SAML config XML from the IDP.
Solution was to put the metatdata XML on the server and refernce it as metadata_file: /usr/share/elasticsearch/plugins/opendistro_security/securityconfig/metadata.xml instead of metadata_url in config.yml