Hello,
**Elasticsearch version: 8.7.1
**Kibana version (if relevant): 8.7.1
Describe the issue:
With Kibana 8.7.1, multi tenancy enabled, we get big trouble with tenants that have the action kibana:saved_objects/*/read (SGS_KIBANA_ALL_READ action group).
When a user with a role that have this action kibana:saved_objects/*/read is trying to do a discover, the user is getting a permission deny.
I set up my logger to debug mode and found that when a user is trying to do a discover, KIBANA need to do the action indices:data/write/bulk on .kibana indice.
SG is then verifying that the user have kibana:saved_objects/*/write in the isTenantAllowed function: search-guard/dlic-fe-multi-tenancy/src/main/java/com/floragunn/searchguard/enterprise/femt/PrivilegesInterceptorImpl.java at sg-flx-1.6.0-es-8.7.x · floragunncom/search-guard · GitHub
You can see in the logs I provided a user k006898 trying to access discover on the tenant PT_MDW_RONLY and being rejected because he does not have the kibana:saved_objects/*/write permission on this tenant.
Why do you check that the user have kibana:saved_objects/*/write permission onto a tenant instead of checking that the user have the indices:data/write/bulk permission onto the .kibana indice ? It seems like a bug.
All our readonly users can’t see the discover anymore which is a very big issue for my company.
Thanks for your reply.
Mickaël
elasticsearch/config/elasticsearch.yml
#SG
searchguard.restapi.roles_enabled: [SGS_ALL_ACCESS, xxx_admin]
searchguard.ssl.http.clientauth_mode: OPTIONAL
searchguard.ssl.transport.pemcert_filepath: xxx.crt
searchguard.ssl.transport.pemkey_filepath: xxx.key
searchguard.ssl.transport.pemtrustedcas_filepath: xx.ca
searchguard.ssl.transport.enforce_hostname_verification: false
searchguard.ssl.transport.resolve_hostname: false
searchguard.ssl.http.enabled: true
searchguard.ssl.http.pemcert_filepath: xxx.crt
searchguard.ssl.http.pemkey_filepath: xxx.key
searchguard.ssl.http.pemtrustedcas_filepath: xx.ca
searchguard.enterprise_modules_enabled: true
searchguard.nodes_dn: ['*xxxx*']
searchguard.authcz.admin_dn: [xxxx]
/sgconfig/sg_config.yml
sg_config:
dynamic:
filtered_alias_mode: "warn"
disable_rest_auth: false
disable_intertransport_auth: false
respect_request_indices_options: false
license: "xxxxxxxxxxxxxxx"
kibana:
multitenancy_enabled: true
server_username: "kibanaserver"
index: ".kibana"
rbac_enabled: false
http:
anonymous_auth_enabled: false
xff:
enabled: true
internalProxies: ".*"
remoteIpHeader: "X-Forwarded-For"
authc:
openid_auth_domain:
http_enabled: false
transport_enabled: false
order: 0
http_authenticator:
challenge: false
type: "openid"
config:
roles_key: "profile"
openid_connect_url: "xxxxxxxxxxxxxxxxxx"
authentication_backend:
type: "noop"
config: {}
description: "Authenticate via OpenId Connect"
skip_users: []
jwt_auth_domain:
http_enabled: false
transport_enabled: false
order: 4
http_authenticator:
challenge: false
type: "jwt"
config:
signing_key: "base64 encoded HMAC key or public RSA/ECDSA pem key"
jwt_header: "Authorization"
authentication_backend:
type: "noop"
config: {}
description: "Authenticate via Json Web Token"
skip_users: []
ldap:
http_enabled: false
transport_enabled: false
order: 5
http_authenticator:
challenge: false
type: "basic"
config: {}
authentication_backend:
type: "ldap"
config:
enable_ssl: false
enable_start_tls: false
enable_ssl_client_auth: false
verify_hostnames: true
hosts:
- "localhost:8389"
userbase: "ou=people,dc=example,dc=com"
usersearch: "(sAMAccountName={0})"
description: "Authenticate via LDAP or Active Directory"
skip_users: []
basic_internal_auth_domain:
http_enabled: false
transport_enabled: false
order: 3
http_authenticator:
challenge: true
type: "basic"
config: {}
authentication_backend:
type: "intern"
config: {}
description: "Authenticate via HTTP Basic against internal users database"
skip_users: []
proxy_auth_domain:
http_enabled: true
transport_enabled: true
order: 0
http_authenticator:
challenge: false
type: "proxy"
config:
user_header: "x-proxy-user"
roles_header: "x-proxy-roles"
authentication_backend:
type: "noop"
config: {}
description: "Authenticate via proxy"
skip_users: []
clientcert_auth_domain:
http_enabled: true
transport_enabled: true
order: 1
http_authenticator:
challenge: false
type: "clientcert"
config:
username_attribute: "cn"
authentication_backend:
type: "noop"
config: {}
description: "Authenticate via SSL client certificates"
skip_users: []
kerberos_auth_domain:
http_enabled: false
transport_enabled: false
order: 6
http_authenticator:
challenge: true
type: "kerberos"
config:
krb_debug: false
strip_realm_from_principal: true
authentication_backend:
type: "noop"
config: {}
skip_users: []
authz:
roles_from_another_ldap:
http_enabled: false
transport_enabled: false
authorization_backend:
type: "ldap"
config: {}
description: "Authorize via another Active Directory"
skipped_users: []
roles_from_myldap:
http_enabled: false
transport_enabled: false
authorization_backend:
type: "ldap"
config:
enable_ssl: false
enable_start_tls: false
enable_ssl_client_auth: false
verify_hostnames: true
hosts:
- "localhost:8389"
rolebase: "ou=groups,dc=example,dc=com"
rolesearch: "(member={0})"
userrolename: "disabled"
rolename: "cn"
resolve_nested_roles: true
userbase: "ou=people,dc=example,dc=com"
usersearch: "(uid={0})"
description: "Authorize via LDAP or Active Directory"
skipped_users: []
auth_failure_listeners: {}
do_not_fail_on_forbidden: true
multi_rolespan_enabled: true
hosts_resolver_mode: "ip-only"
do_not_fail_on_forbidden_empty: false
auth_token_provider: {}
Provide logs:
[2024-11-08T09:29:14,480][DEBUG][c.f.s.a.r.a.HttpClientCertAuthenticationFrontend] [xxx] No client cert provided
[2024-11-08T09:29:14,480][DEBUG][c.f.s.a.b.UserMapping ] [xxx] Mapping user using attributes {credentials={user_name=n/a}, request={headers=org.elasticsearch.http.netty4.Netty4HttpRequest$HttpHeadersMap@3d6bc5b9, direct_ip_address=10.76.0.124, originating_ip_address=127.0.0.1}} for AuthCredentials [username=n/a, subUserName=null, authDomainInfo=AuthDomainInfo [authDomainId=null, authenticatorType=trusted_origin, authBackendType=null], password=null, nativeCredentials=null, backendRoles=[], searchGuardRoles=[], complete=true, authzComplete=false, redirectUri=null, attributes={}, structuredAttributes={}, claims={}, attributesForUserMapping={credentials={user_name=n/a}, request={headers=org.elasticsearch.http.netty4.Netty4HttpRequest$HttpHeadersMap@3d6bc5b9, direct_ip_address=10.76.0.124, originating_ip_address=127.0.0.1}}]
[2024-11-08T09:29:14,480][DEBUG][c.f.s.a.b.UserMapping ] [xxx] Mapped user name: k006898
[2024-11-08T09:29:14,480][DEBUG][c.f.s.a.b.UserMapping ] [xxx] Mapping user using attributes {credentials={user_name=n/a}, request={headers=org.elasticsearch.http.netty4.Netty4HttpRequest$HttpHeadersMap@3d6bc5b9, direct_ip_address=10.76.0.124, originating_ip_address=127.0.0.1}} for AuthCredentials [username=k006898, subUserName=null, authDomainInfo=AuthDomainInfo [authDomainId=null, authenticatorType=trusted_origin, authBackendType=noop], password=null, nativeCredentials=null, backendRoles=[], searchGuardRoles=[], complete=true, authzComplete=false, redirectUri=null, attributes={}, structuredAttributes={}, claims={}, attributesForUserMapping={credentials={user_name=n/a}, request={headers=org.elasticsearch.http.netty4.Netty4HttpRequest$HttpHeadersMap@3d6bc5b9, direct_ip_address=10.76.0.124, originating_ip_address=127.0.0.1}}]
[2024-11-08T09:29:14,480][DEBUG][c.f.s.a.b.UserMapping ] [xxx] Mapped roles: [P_RONLY_MDW]
[2024-11-08T09:29:14,480][DEBUG][c.f.s.a.b.RequestAuthenticationProcessor] [xxx] Authentication successful for User k006898 <trusted_origin/noop> [P_RONLY_MDW]/[] {} on trusted_origin[67a55e62] using com.floragunn.searchguard.authc.rest.RestRequestAuthenticationProcessor@6a567231
requestedTenant: PT_MDW_TMP
[2024-11-08T09:29:14,481][DEBUG][c.f.s.a.PrivilegesEvaluator] [xxx] ### evaluate indices:data/write/bulk (org.elasticsearch.action.bulk.BulkRequest)
User: User k006898 <trusted_origin/noop> [backend_roles=[P_RONLY_MDW] requestedTenant=PT_MDW_TMP]
specialPrivilegesEvaluationContext: null
Resolved: local: [.kibana_8.7.1_001]; remote: []
Uresolved: [[indices=[.kibana_8.7.1], indicesOptions=IndicesOptions[ignore_unavailable=false, allow_no_indices=false, expand_wildcards_open=false, expand_wildcards_closed=false, expand_wildcards_hidden=false, allow_aliases_to_multiple_indices=false, forbid_closed_indices=true, ignore_aliases=false, ignore_throttled=false], allowsRemoteIndices=false, includeDataStreams=false, role=null]]
IgnoreUnauthorizedIndices: true
[2024-11-08T09:29:14,481][DEBUG][c.f.s.e.f.PrivilegesInterceptorImpl] [xxx] replaceKibanaIndex(indices:data/write/bulk, User k006898 <trusted_origin/noop> [backend_roles=[P_RONLY_MDW] requestedTenant=PT_MDW_TMP])
requestedResolved: local: [.kibana_8.7.1_001]; remote: []
requestedTenant: PT_MDW_TMP
[2024-11-08T09:29:14,482][DEBUG][c.f.s.e.f.PrivilegesInterceptorImpl] [xxx] IndexInfo: IndexInfo [originalName=.kibana_8.7.1, prefix=.kibana, suffix=_8.7.1, tenantInfoPart=null]
[2024-11-08T09:29:14,482][WARN ][c.f.s.e.f.PrivilegesInterceptorImpl] [xxx] Tenant PT_MDW_TMP is not allowed to write (user: k006898)
[2024-11-08T09:29:14,482][DEBUG][c.f.s.a.PrivilegesEvaluator] [xxx] Result from privileges interceptor for cluster perm: DENY
[2024-11-08T09:29:14,482][DEBUG][r.suppressed ] [xxx] path: /_bulk, params: {require_alias=true, refresh=false}
org.elasticsearch.ElasticsearchSecurityException: Insufficient permissions
at com.floragunn.searchguard.authz.PrivilegesEvaluationResult.toSecurityException(PrivilegesEvaluationResult.java:305) ~[?:?]