How to map role to Kerberos authenticated user?

Versions (relevant - OpenSearch/Dashboard/Server OS/Browser):

OpenSearch and Dashboard version: 2.18.0 on CentOS Linux, installed via tarball.

Describe the issue:

Hi OpenSearch Community,

I have studied the official documentation about access control and mapping roles to users but for Kerberos it is not really clear for me how the role mapping should be done.

I have a fully working OpenSearch 2.18.0 cluster with TLS configured enabled (using my own PKI) and also configured Kerberos authentication which seems to work fine.

Enabled Kerberos auth in config.yml:

...
   authc:
      kerberos_auth_domain:
        http_enabled: true
        transport_enabled: true
        order: 2
        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: true
            # If true then the realm will be stripped from the user name
            strip_realm_from_principal: true
        authentication_backend:
          type: noop
...

I tried to map the all_access role to the Kerberos user like this in roles_mapping.yml:

...
all_access:
  reserved: false
  backend_roles:
  - "admin"
  users:
  - "opensearch/node-1.example.com"
  description: "Maps admin to all_access"
...

I did not modified roles.yml or internal_users.yml.

Use kinit to get a new ticket:

[root@centos ~]# kinit -kt ./opensearch.keytab opensearch/node-1.example.com

Validate the ticket using klist:

[root@centos ~]# klist
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: opensearch/node-1.example.com@REALM

Valid starting     Expires            Service principal
14/05/25 12:40:40  15/05/25 12:40:40  krbtgt/REALM@REALM
	renew until 19/05/25 12:40:40
14/05/25 12:40:55  15/05/25 12:40:40  opensearch/node-1.example.com@REALM
	renew until 19/05/25 12:40:40

However when I authenticate with Kerberos, my user seems to only have the own_index permission:

[root@centos ~]# curl --negotiate -u : -k -v --service-name opensearch https://node-1.example.com:9201/_plugins/_security/authinfo?pretty --cacert ***_cacerts.pem
* Uses proxy env variable no_proxy == '0.0.0.0,localhost,127.0.0.1'
*   Trying ***...
* TCP_NODELAY set
* Connected to node-1.example.com (***) port 9201 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: ***_cacerts.pem
  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, [no content] (0):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Request CERT (13):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS handshake, [no content] (0):
* TLSv1.3 (OUT), TLS handshake, Certificate (11):
* TLSv1.3 (OUT), TLS handshake, [no content] (0):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=***; ST=***; CN=node-1.example.com
*  start date: May  7 12:12:16 2025 GMT
*  expire date: May  7 23:59:59 2026 GMT
*  issuer: C=***; ST=***; CN=***
*  SSL certificate verify ok.
* TLSv1.3 (OUT), TLS app data, [no content] (0):
> GET /_plugins/_security/authinfo?pretty HTTP/1.1
> Host: node-1.example.com:9201
> User-Agent: curl/7.61.1
> Accept: */*
> 
* TLSv1.3 (IN), TLS handshake, [no content] (0):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS app data, [no content] (0):
< HTTP/1.1 401 Unauthorized
< WWW-Authenticate: Negotiate
< content-type: application/json; charset=UTF-8
< content-length: 53
< 
* Ignoring the response-body
* Connection #0 to host node-1.example.com left intact
* Issue another request to this URL: 'https://node-1.example.com:9201/_plugins/_security/authinfo?pretty'
* Uses proxy env variable no_proxy == '0.0.0.0,localhost,127.0.0.1'
* Found bundle for host node-1.example.com: 0x5628585624f0 [can pipeline]
* Re-using existing connection! (#0) with host node-1.example.com
* Connected to node-1.example.com (***) port 9201 (#0)
* Server auth using Negotiate with user ''
* TLSv1.3 (OUT), TLS app data, [no content] (0):
> GET /_plugins/_security/authinfo?pretty HTTP/1.1
> Host: node-1.example.com:9201
> Authorization: Negotiate YIID***EJdwg=
> User-Agent: curl/7.61.1
> Accept: */*
> 
* TLSv1.3 (IN), TLS app data, [no content] (0):
< HTTP/1.1 200 OK
< content-type: application/json; charset=UTF-8
< content-length: 526
< 
{
  "user" : "User [name=opensearch/node-1.example.com, backend_roles=[], requestedTenant=null]",
  "user_name" : "opensearch/node-1.example.com",
  "user_requested_tenant" : null,
  "remote_address" : "***:38692",
  "backend_roles" : [ ],
  "custom_attribute_names" : [ ],
  "roles" : [
    "own_index"
  ],
  "tenants" : {
    "opensearch/node-1.example.com" : true
  },
  "principal" : null,
  "peer_certificates" : "0",
  "sso_logout_url" : null
}
* Closing connection 0
* TLSv1.3 (OUT), TLS alert, [no content] (0):
* TLSv1.3 (OUT), TLS alert, close notify (256):

The Kerberos user cannot access the root REST API endpoint:

[root@centos ~]# curl --negotiate -u : -k --service-name opensearch https://node-1.example.com:9201/?pretty --cacert ***_cacerts.pem
{
  "error" : {
    "root_cause" : [
      {
        "type" : "security_exception",
        "reason" : "no permissions for [cluster:monitor/main] and User [name=opensearch/node-1.example.com, backend_roles=[], requestedTenant=null]"
      }
    ],
    "type" : "security_exception",
    "reason" : "no permissions for [cluster:monitor/main] and User [name=opensearch/node-1.example.com, backend_roles=[], requestedTenant=null]"
  },
  "status" : 403
}

I used securityadmin.sh to load the changes of the YAML files into the security index.

Can you please help how the roles should be mapped to Kerberos authenticated users?

Configuration:

Note: I removed some details using asterisks.

opensearch.yml

network.host: [ _site_, _local_]
http.port: 9200
transport.port: 9300
node.roles: [ master ]
discovery.seed_providers: file
plugins.security.kerberos.krb5_filepath: /etc/krb5.conf
plugins.security.kerberos.acceptor_keytab_filepath: opensearch.keytab
plugins.security.kerberos.acceptor_principal: opensearch/node-1.example.com@REALM
plugins:
  security:
    ssl:
      transport:
        pemcert_filepath: ***_chain.pem
        pemkey_filepath: ***_key.pem
        pemtrustedcas_filepath: ***_cacerts.pem
        enforce_hostname_verification: false
      http:
        enabled: true
        pemcert_filepath: ***_chain.pem
        pemkey_filepath: ***_key.pem
        pemtrustedcas_filepath: ***_cacerts.pem
    allow_unsafe_democertificates: false
    allow_default_init_securityindex: true
    authcz:
      admin_dn:
        - "CN=node-1.example.com,ST=*,C=*"
    nodes_dn:
      - "CN=*.example.com,ST=*,C=*"
    audit.type: internal_opensearch
    enable_snapshot_restore_privilege: true
    check_snapshot_restore_write_privileges: true
    restapi:
      roles_enabled: ["all_access", "security_rest_api_access"]
    system_indices:
      enabled: true
      indices:
        [
          ".opendistro-alerting-config",
          ".opendistro-alerting-alert*",
          ".opendistro-anomaly-results*",
          ".opendistro-anomaly-detector*",
          ".opendistro-anomaly-checkpoints",
          ".opendistro-anomaly-detection-state",
          ".opendistro-reports-*",
          ".opendistro-notifications-*",
          ".opendistro-notebooks",
          ".opendistro-asynchronous-search-response*",
        ]
cluster.initial_master_nodes: [ node-1.example.com ]
path.logs: /var/log/opensearch/master
path.data: /var/lib/opensearch/master

/etc/krb5.conf

[logging]
 default = FILE:/var/log/krb5libs.log
 kdc = FILE:/var/log/krb5kdc.log
 admin_server = FILE:/var/log/kadmind.log

[libdefaults]
 renew_lifetime = 8d
 default_realm = REALM
 dns_lookup_realm = false
 dns_lookup_kdc = false
 ticket_lifetime = 1d
 forwardable = yes
 allow_weak_crypto = true
 kdc_timeout = 10s

[realms]
 REALM = {
  kdc = node-1.example.com:88
  admin_server = node-1.example.com:749
  default_domain = example.com
 }

[domain_realm]
 .example.com = REALM
 example.com = REALM

Relevant Logs or Screenshots:

@paksydavid Did you apply roles mapping to your OpenSearch cluster?
Could you run the following API call against the cluster?

curl --insecure -u admin:<password> https://<OpenSearch_node_FQDN_or_IP>:9200/_plugins/_security/api/rolesmapping?pretty
1 Like

Many thanks @pablo for your answer. :+1: It seems that I had some stuck OpenSearch processes in the background and after doing a cleanup and a fresh start the role mapping was applied successfully:

 "all_access" : {
    "hosts" : [ ],
    "users" : [
      "opensearch/node-1.example.com"
    ],
    "reserved" : false,
    "hidden" : false,
    "backend_roles" : [
      "admin"
    ],
    "and_backend_roles" : [ ],
    "description" : "Maps admin to all_access"
  },

1 Like