Skip to content

Commit 273c82d

Browse files
authored
Add support for "authorization_realms" (#33262)
Authorization Realms allow an authenticating realm to delegate the task of constructing a User object (with name, roles, etc) to one or more other realms. E.g. A client could authenticate using PKI, but then delegate to an LDAP realm. The LDAP realm performs a "lookup" by principal, and then does regular role-mapping from the discovered user. This commit includes: - authorization_realm support in the pki, ldap, saml & kerberos realms - docs for authorization_realms - checks that there are no "authorization chains" (whereby "realm-a" delegates to "realm-b", but "realm-b" delegates to "realm-c") Authorization realms is a platinum feature.
1 parent 6dd0aa5 commit 273c82d

37 files changed

+1430
-233
lines changed

docs/reference/settings/security-settings.asciidoc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,13 @@ This setting is multivalued; you can specify multiple user contexts.
246246
Required to operate in user template mode. If `user_search.base_dn` is specified,
247247
this setting is not valid. For more information on
248248
the different modes, see {xpack-ref}/ldap-realm.html[LDAP realms].
249+
250+
`authorization_realms`::
251+
The names of the realms that should be consulted for delegate authorization.
252+
If this setting is used, then the LDAP realm does not perform role mapping and
253+
instead loads the user from the listed realms. The referenced realms are
254+
consulted in the order that they are defined in this list.
255+
See {stack-ov}/realm-chains.html#authorization_realms[Delegating authorization to another realm]
249256
+
250257
--
251258
NOTE: If any settings starting with `user_search` are specified, the
@@ -733,6 +740,12 @@ Specifies the {xpack-ref}/security-files.html[location] of the
733740
{xpack-ref}/mapping-roles.html[YAML role mapping configuration file].
734741
Defaults to `ES_PATH_CONF/role_mapping.yml`.
735742

743+
`authorization_realms`::
744+
The names of the realms that should be consulted for delegate authorization.
745+
If this setting is used, then the PKI realm does not perform role mapping and
746+
instead loads the user from the listed realms.
747+
See {stack-ov}/realm-chains.html#authorization_realms[Delegating authorization to another realm]
748+
736749
`cache.ttl`::
737750
Specifies the time-to-live for cached user entries. A user and a hash of its
738751
credentials are cached for this period of time. Use the
@@ -856,6 +869,12 @@ Defaults to `false`.
856869
Specifies whether to populate the {es} user's metadata with the values that are
857870
provided by the SAML attributes. Defaults to `true`.
858871

872+
`authorization_realms`::
873+
The names of the realms that should be consulted for delegate authorization.
874+
If this setting is used, then the SAML realm does not perform role mapping and
875+
instead loads the user from the listed realms.
876+
See {stack-ov}/realm-chains.html#authorization_realms[Delegating authorization to another realm]
877+
859878
`allowed_clock_skew`::
860879
The maximum amount of skew that can be tolerated between the IdP's clock and the
861880
{es} node's clock.

x-pack/docs/en/security/authentication/configuring-kerberos-realm.asciidoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,5 +166,10 @@ POST _xpack/security/role_mapping/kerbrolemapping
166166
// CONSOLE
167167

168168
For more information, see {stack-ov}/mapping-roles.html[Mapping users and groups to roles].
169+
170+
NOTE: The Kerberos realm supports
171+
{stack-ov}/realm-chains.html#authorization_realms[authorization realms] as an
172+
alternative to role mapping.
173+
169174
--
170175

x-pack/docs/en/security/authentication/configuring-ldap-realm.asciidoc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,11 @@ For more information, see
189189
{xpack-ref}/ldap-realm.html#mapping-roles-ldap[Mapping LDAP Groups to Roles]
190190
and
191191
{xpack-ref}/mapping-roles.html[Mapping Users and Groups to Roles].
192+
193+
NOTE: The LDAP realm supports
194+
{stack-ov}/realm-chains.html#authorization_realms[authorization realms] as an
195+
alternative to role mapping.
196+
192197
--
193198

194199
. (Optional) Configure the `metadata` setting on the LDAP realm to include extra
@@ -211,4 +216,4 @@ xpack:
211216
type: ldap
212217
metadata: cn
213218
--------------------------------------------------
214-
--
219+
--

x-pack/docs/en/security/authentication/configuring-pki-realm.asciidoc

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ NOTE: You cannot use PKI certificates to authenticate users in {kib}.
1010

1111
To use PKI in {es}, you configure a PKI realm, enable client authentication on
1212
the desired network layers (transport or http), and map the Distinguished Names
13-
(DNs) from the user certificates to {security} roles in the role mapping file.
13+
(DNs) from the user certificates to {security} roles in the
14+
<<security-api-role-mapping,role-mapping API>> or role-mapping file.
1415

1516
You can also use a combination of PKI and username/password authentication. For
1617
example, you can enable SSL/TLS on the transport layer and define a PKI realm to
@@ -173,4 +174,9 @@ key. You can also use the authenticate API to validate your role mapping.
173174

174175
For more information, see
175176
{xpack-ref}/mapping-roles.html[Mapping Users and Groups to Roles].
176-
--
177+
178+
NOTE: The PKI realm supports
179+
{stack-ov}/realm-chains.html#authorization_realms[authorization realms] as an
180+
alternative to role mapping.
181+
182+
--

x-pack/docs/en/security/authentication/configuring-saml-realm.asciidoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,11 @@ access any data.
219219

220220
Your SAML users cannot do anything until they are mapped to {security}
221221
roles. See {stack-ov}/saml-role-mapping.html[Configuring role mappings].
222+
223+
NOTE: The SAML realm supports
224+
{stack-ov}/realm-chains.html#authorization_realms[authorization realms] as an
225+
alternative to role mapping.
226+
222227
--
223228

224229
. {stack-ov}/saml-kibana.html[Configure {kib} to use SAML SSO].

x-pack/docs/en/security/authentication/saml-guide.asciidoc

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ or separate keys used for each of those.
473473

474474
The Elastic Stack uses X.509 certificates with RSA private keys for SAML
475475
cryptography. These keys can be generated using any standard SSL tool, including
476-
the `elasticsearch-certutil` tool that ships with X-Pack.
476+
the `elasticsearch-certutil` tool that ships with {xpack}.
477477

478478
Your IdP may require that the Elastic Stack have a cryptographic key for signing
479479
SAML messages, and that you provide the corresponding signing certificate within
@@ -624,9 +624,10 @@ When a user authenticates using SAML, they are identified to the Elastic Stack,
624624
but this does not automatically grant them access to perform any actions or
625625
access any data.
626626

627-
Your SAML users cannot do anything until they are mapped to {security}
628-
roles. This mapping is performed through the
629-
{ref}/security-api-put-role-mapping.html[add role mapping API].
627+
Your SAML users cannot do anything until they are assigned {security}
628+
roles. This is done through either the
629+
{ref}/security-api-put-role-mapping.html[add role mapping API], or with
630+
<<authorization_realms, authorization realms>>.
630631

631632
This is an example of a simple role mapping that grants the `kibana_user` role
632633
to any user who authenticates against the `saml1` realm:
@@ -683,6 +684,18 @@ PUT /_xpack/security/role_mapping/saml-finance
683684
// CONSOLE
684685
// TEST
685686

687+
If your users also exist in a repository that can be directly accessed by {security}
688+
(such as an LDAP directory) then you can use
689+
<<authorization_realms, authorization realms>> instead of role mappings.
690+
691+
In this case, you perform the following steps:
692+
1. In your SAML realm, assigned a SAML attribute to act as the lookup userid,
693+
by configuring the `attributes.principal` setting.
694+
2. Create a new realm that can lookup users from your local repository (e.g. an
695+
`ldap` realm)
696+
3. In your SAML realm, set `authorization_realms` to the name of the realm you
697+
created in step 2.
698+
686699
[[saml-user-metadata]]
687700
=== User metadata
688701

x-pack/docs/en/security/authorization/mapping-roles.asciidoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ either role management method. For example, when you use the role mapping API,
2424
you are able to map users to both API-managed roles and file-managed roles
2525
(and likewise for file-based role-mappings).
2626

27+
NOTE: The PKI, LDAP, Kerberos and SAML realms support using
28+
<<authorization_realms, authorization realms>> as an alternative to role mapping.
29+
2730
[[mapping-roles-api]]
2831
==== Using the role mapping API
2932

x-pack/docs/en/security/authorization/run-as-privilege.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ the realm you use to authenticate. Both the internal `native` and `file` realms
1212
support this out of the box. The LDAP realm must be configured to run in
1313
<<ldap-user-search, _user search_ mode>>. The Active Directory realm must be
1414
<<ad-settings,configured with a `bind_dn` and `secure_bind_password`>> to support
15-
_run as_. The PKI realm does not support _run as_.
15+
_run as_. The PKI, Kerberos, and SAML realms do not support _run as_.
1616

1717
To submit requests on behalf of other users, you need to have the `run_as`
1818
permission. For example, the following role grants permission to submit request

x-pack/plugin/core/src/main/java/org/elasticsearch/license/XPackLicenseState.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,10 +410,20 @@ public AllowedRealmType allowedRealmType() {
410410
*/
411411
public boolean isCustomRoleProvidersAllowed() {
412412
final Status localStatus = status;
413-
return (localStatus.mode == OperationMode.PLATINUM || localStatus.mode == OperationMode.TRIAL )
413+
return (localStatus.mode == OperationMode.PLATINUM || localStatus.mode == OperationMode.TRIAL)
414414
&& localStatus.active;
415415
}
416416

417+
/**
418+
* @return whether "authorization_realms" are allowed based on the license {@link OperationMode}
419+
* @see org.elasticsearch.xpack.core.security.authc.support.DelegatedAuthorizationSettings
420+
*/
421+
public boolean isAuthorizationRealmAllowed() {
422+
final Status localStatus = status;
423+
return (localStatus.mode == OperationMode.PLATINUM || localStatus.mode == OperationMode.TRIAL)
424+
&& localStatus.active;
425+
}
426+
417427
/**
418428
* Determine if Watcher is available based on the current license.
419429
* <p>

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/Realm.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import org.apache.logging.log4j.Logger;
99
import org.elasticsearch.action.ActionListener;
1010
import org.elasticsearch.common.util.concurrent.ThreadContext;
11+
import org.elasticsearch.license.XPackLicenseState;
12+
import org.elasticsearch.xpack.core.security.authc.support.DelegatedAuthorizationSettings;
1113
import org.elasticsearch.xpack.core.XPackField;
1214
import org.elasticsearch.xpack.core.security.user.User;
1315

@@ -146,6 +148,14 @@ public String toString() {
146148
return type + "/" + config.name;
147149
}
148150

151+
/**
152+
* This is no-op in the base class, but allows realms to be aware of what other realms are configured
153+
*
154+
* @see DelegatedAuthorizationSettings
155+
*/
156+
public void initialize(Iterable<Realm> realms, XPackLicenseState licenseState) {
157+
}
158+
149159
/**
150160
* A factory interface to construct a security realm.
151161
*/

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/kerberos/KerberosRealmSettings.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.elasticsearch.common.settings.Setting.Property;
1111
import org.elasticsearch.common.unit.TimeValue;
1212
import org.elasticsearch.common.util.set.Sets;
13+
import org.elasticsearch.xpack.core.security.authc.support.DelegatedAuthorizationSettings;
1314

1415
import java.util.Set;
1516

@@ -44,7 +45,9 @@ private KerberosRealmSettings() {
4445
* @return the valid set of {@link Setting}s for a {@value #TYPE} realm
4546
*/
4647
public static Set<Setting<?>> getSettings() {
47-
return Sets.newHashSet(HTTP_SERVICE_KEYTAB_PATH, CACHE_TTL_SETTING, CACHE_MAX_USERS_SETTING, SETTING_KRB_DEBUG_ENABLE,
48-
SETTING_REMOVE_REALM_NAME);
48+
final Set<Setting<?>> settings = Sets.newHashSet(HTTP_SERVICE_KEYTAB_PATH, CACHE_TTL_SETTING, CACHE_MAX_USERS_SETTING,
49+
SETTING_KRB_DEBUG_ENABLE, SETTING_REMOVE_REALM_NAME);
50+
settings.addAll(DelegatedAuthorizationSettings.getSettings());
51+
return settings;
4952
}
5053
}

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/ldap/LdapRealmSettings.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.elasticsearch.common.unit.TimeValue;
1010
import org.elasticsearch.xpack.core.security.authc.ldap.support.LdapMetaDataResolverSettings;
1111
import org.elasticsearch.xpack.core.security.authc.support.CachingUsernamePasswordRealmSettings;
12+
import org.elasticsearch.xpack.core.security.authc.support.DelegatedAuthorizationSettings;
1213
import org.elasticsearch.xpack.core.security.authc.support.mapper.CompositeRoleMapperSettings;
1314

1415
import java.util.HashSet;
@@ -37,6 +38,7 @@ public static Set<Setting<?>> getSettings(String type) {
3738
assert LDAP_TYPE.equals(type) : "type [" + type + "] is unknown. expected one of [" + AD_TYPE + ", " + LDAP_TYPE + "]";
3839
settings.addAll(LdapSessionFactorySettings.getSettings());
3940
settings.addAll(LdapUserSearchSessionFactorySettings.getSettings());
41+
settings.addAll(DelegatedAuthorizationSettings.getSettings());
4042
}
4143
settings.addAll(LdapMetaDataResolverSettings.getSettings());
4244
return settings;

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/pki/PkiRealmSettings.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import org.elasticsearch.common.settings.Setting;
99
import org.elasticsearch.common.unit.TimeValue;
10+
import org.elasticsearch.xpack.core.security.authc.support.DelegatedAuthorizationSettings;
1011
import org.elasticsearch.xpack.core.security.authc.support.mapper.CompositeRoleMapperSettings;
1112
import org.elasticsearch.xpack.core.ssl.SSLConfigurationSettings;
1213

@@ -43,6 +44,7 @@ public static Set<Setting<?>> getSettings() {
4344
settings.add(SSL_SETTINGS.truststoreAlgorithm);
4445
settings.add(SSL_SETTINGS.caPaths);
4546

47+
settings.addAll(DelegatedAuthorizationSettings.getSettings());
4648
settings.addAll(CompositeRoleMapperSettings.getSettings());
4749

4850
return settings;

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/saml/SamlRealmSettings.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import org.elasticsearch.common.settings.Setting;
99
import org.elasticsearch.common.unit.TimeValue;
1010
import org.elasticsearch.common.util.set.Sets;
11+
import org.elasticsearch.xpack.core.security.authc.support.DelegatedAuthorizationSettings;
1112
import org.elasticsearch.xpack.core.ssl.SSLConfigurationSettings;
1213
import org.elasticsearch.xpack.core.ssl.X509KeyPairSettings;
1314

@@ -89,6 +90,7 @@ public static Set<Setting<?>> getSettings() {
8990
set.addAll(DN_ATTRIBUTE.settings());
9091
set.addAll(NAME_ATTRIBUTE.settings());
9192
set.addAll(MAIL_ATTRIBUTE.settings());
93+
set.addAll(DelegatedAuthorizationSettings.getSettings());
9294
return set;
9395
}
9496

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
package org.elasticsearch.xpack.core.security.authc.support;
8+
9+
import org.elasticsearch.common.settings.Setting;
10+
11+
import java.util.Collection;
12+
import java.util.Collections;
13+
import java.util.List;
14+
import java.util.function.Function;
15+
16+
/**
17+
* Settings related to "Delegated Authorization" (aka Lookup Realms)
18+
*/
19+
public class DelegatedAuthorizationSettings {
20+
21+
public static final Setting<List<String>> AUTHZ_REALMS = Setting.listSetting("authorization_realms",
22+
Collections.emptyList(), Function.identity(), Setting.Property.NodeScope);
23+
24+
public static Collection<Setting<?>> getSettings() {
25+
return Collections.singleton(AUTHZ_REALMS);
26+
}
27+
}

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/AuthenticationService.java

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@
3434
import org.elasticsearch.xpack.core.security.user.User;
3535
import org.elasticsearch.xpack.security.audit.AuditTrail;
3636
import org.elasticsearch.xpack.security.audit.AuditTrailService;
37+
import org.elasticsearch.xpack.security.authc.support.RealmUserLookup;
3738

3839
import java.util.LinkedHashMap;
3940
import java.util.List;
4041
import java.util.Map;
42+
import java.util.Objects;
4143
import java.util.function.BiConsumer;
4244
import java.util.function.Consumer;
4345

@@ -381,33 +383,18 @@ private void consumeUser(User user, Map<Realm, Tuple<String, Exception>> message
381383
* names of users that exist using a timing attack
382384
*/
383385
private void lookupRunAsUser(final User user, String runAsUsername, Consumer<User> userConsumer) {
384-
final List<Realm> realmsList = realms.asList();
385-
final BiConsumer<Realm, ActionListener<User>> realmLookupConsumer = (realm, lookupUserListener) ->
386-
realm.lookupUser(runAsUsername, ActionListener.wrap((lookedupUser) -> {
387-
if (lookedupUser != null) {
388-
lookedupBy = new RealmRef(realm.name(), realm.type(), nodeName);
389-
lookupUserListener.onResponse(lookedupUser);
390-
} else {
391-
lookupUserListener.onResponse(null);
392-
}
393-
}, lookupUserListener::onFailure));
394-
395-
final IteratingActionListener<User, Realm> userLookupListener =
396-
new IteratingActionListener<>(ActionListener.wrap((lookupUser) -> {
397-
if (lookupUser == null) {
398-
// the user does not exist, but we still create a User object, which will later be rejected by authz
399-
userConsumer.accept(new User(runAsUsername, null, user));
400-
} else {
401-
userConsumer.accept(new User(lookupUser, user));
402-
}
403-
},
404-
(e) -> listener.onFailure(request.exceptionProcessingRequest(e, authenticationToken))),
405-
realmLookupConsumer, realmsList, threadContext);
406-
try {
407-
userLookupListener.run();
408-
} catch (Exception e) {
409-
listener.onFailure(request.exceptionProcessingRequest(e, authenticationToken));
410-
}
386+
final RealmUserLookup lookup = new RealmUserLookup(realms.asList(), threadContext);
387+
lookup.lookup(runAsUsername, ActionListener.wrap(tuple -> {
388+
if (tuple == null) {
389+
// the user does not exist, but we still create a User object, which will later be rejected by authz
390+
userConsumer.accept(new User(runAsUsername, null, user));
391+
} else {
392+
User foundUser = Objects.requireNonNull(tuple.v1());
393+
Realm realm = Objects.requireNonNull(tuple.v2());
394+
lookedupBy = new RealmRef(realm.name(), realm.type(), nodeName);
395+
userConsumer.accept(new User(foundUser, user));
396+
}
397+
}, exception -> listener.onFailure(request.exceptionProcessingRequest(exception, authenticationToken))));
411398
}
412399

413400
/**

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/Realms.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ public Realms(Settings settings, Environment env, Map<String, Realm.Factory> fac
9393

9494
this.standardRealmsOnly = Collections.unmodifiableList(standardRealms);
9595
this.nativeRealmsOnly = Collections.unmodifiableList(nativeRealms);
96+
realms.forEach(r -> r.initialize(this, licenseState));
9697
}
9798

9899
@Override

0 commit comments

Comments
 (0)