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:
–