Skip to content
Closed
Show file tree
Hide file tree
Changes from 153 commits
Commits
Show all changes
159 commits
Select commit Hold shift + click to select a range
c2abab4
Clean up
n1v0lg Nov 22, 2022
27a8679
Checkstyle
n1v0lg Nov 22, 2022
67df73b
Add assertion
n1v0lg Nov 23, 2022
ed6dd51
Merge branch 'main' into remote-access-authentication-header
n1v0lg Nov 23, 2022
d4f399d
Clean up and test parse bytes
n1v0lg Nov 23, 2022
b39c861
Visibility
n1v0lg Nov 23, 2022
0d1bb46
Merge branch 'main' into remote-access-authentication-header
n1v0lg Nov 23, 2022
5b53b3d
Nit
n1v0lg Nov 23, 2022
8d0d67e
Typo
n1v0lg Nov 23, 2022
7c6b127
Merge branch 'main' into remote-access-authentication-header
n1v0lg Nov 24, 2022
567d652
Merge branch 'main' into remote-access-authentication-header
n1v0lg Nov 24, 2022
33b9eac
List instead of collection
n1v0lg Nov 24, 2022
bf17175
Merge branch 'main' into remote-access-authentication-header
n1v0lg Nov 24, 2022
ceb631d
WIP send requests with remote access headers
n1v0lg Nov 25, 2022
36bfadc
Merge branch 'main' into send-remote-access-headers
n1v0lg Dec 4, 2022
461fab2
Merge branch 'main' into remote-access-authentication-header
n1v0lg Dec 4, 2022
9928890
WIP remote access authenticator
n1v0lg Dec 4, 2022
c88a776
WIP role building
n1v0lg Dec 4, 2022
64d0ff4
Subject authc type
n1v0lg Dec 4, 2022
0b1c04c
WIP role references
n1v0lg Dec 4, 2022
97d6dca
Hacky but it works
n1v0lg Dec 4, 2022
badff5c
Lint
n1v0lg Dec 4, 2022
cd691b5
Merge branch 'main' into remote-access-authentication-header
n1v0lg Dec 5, 2022
3646d27
Refactor access header
n1v0lg Dec 5, 2022
2491e98
Not a supplier
n1v0lg Dec 5, 2022
aeb09f4
Address review feedback
n1v0lg Dec 5, 2022
302d1e0
Lint and assert
n1v0lg Dec 5, 2022
17f0231
Name
n1v0lg Dec 5, 2022
c6ce67f
Getters
n1v0lg Dec 5, 2022
b5e0b51
Role descriptors bytes
n1v0lg Dec 5, 2022
137ccf3
Use wrap raw bytes in class
n1v0lg Dec 5, 2022
59fc014
Plug in raw bytes
n1v0lg Dec 5, 2022
97e0bbf
Merge branch 'main' into remote-access-authentication-header
n1v0lg Dec 5, 2022
ca23372
Merge branch 'main' into poc/remote-access-authorization
n1v0lg Dec 5, 2022
b9f9814
Clean up tests
n1v0lg Dec 5, 2022
397792f
Merge branch 'main' into remote-access-authentication-header
n1v0lg Dec 6, 2022
8da5abd
Merge branch 'remote-access-authentication-header' into send-remote-a…
n1v0lg Dec 6, 2022
64f4345
WIP
n1v0lg Dec 6, 2022
31f742f
Merge branch 'main' into send-remote-access-headers
n1v0lg Dec 6, 2022
8a89fa1
Composite interceptor
n1v0lg Dec 6, 2022
67c9e49
Throw on missing credential
n1v0lg Dec 6, 2022
c6154fa
More
n1v0lg Dec 6, 2022
20ce992
WIP restructuring
n1v0lg Dec 6, 2022
8660bd8
Wrap send request
n1v0lg Dec 6, 2022
5fded15
Version check etc
n1v0lg Dec 6, 2022
bfd9d7e
Move code around
n1v0lg Dec 6, 2022
ab71afd
Remove log line
n1v0lg Dec 6, 2022
d0d476b
Move assertion
n1v0lg Dec 6, 2022
4b3b263
WIP interceptor
n1v0lg Dec 6, 2022
7af1d83
More request types
n1v0lg Dec 6, 2022
2ef482a
Include shardsearch
n1v0lg Dec 6, 2022
948d6e3
Comments
n1v0lg Dec 6, 2022
a2a62f8
Merge branch 'main' into send-remote-access-headers
n1v0lg Dec 6, 2022
4efa803
Clean up
n1v0lg Dec 7, 2022
d2ba5d9
Conjunction
n1v0lg Dec 7, 2022
88386d8
Test with multiple clusters
n1v0lg Dec 7, 2022
a89dc1c
Minor
n1v0lg Dec 7, 2022
dc8d497
WIP tests
n1v0lg Dec 7, 2022
9680dc3
WIP tests
n1v0lg Dec 8, 2022
9e31464
More test
n1v0lg Dec 8, 2022
7caa648
Complete happy path unit test
n1v0lg Dec 8, 2022
29b6cff
More obvious feature flag test
n1v0lg Dec 8, 2022
9ac5fac
Test and TODOs
n1v0lg Dec 8, 2022
68ca91d
Test credential missing
n1v0lg Dec 9, 2022
99a0ee4
Nit
n1v0lg Dec 9, 2022
14f15af
Merge branch 'main' into send-remote-access-headers
n1v0lg Dec 12, 2022
1665703
WIP remote access headers IT
n1v0lg Dec 12, 2022
ff96c1b
WIP IT
n1v0lg Dec 12, 2022
19f9aac
Simplify and move test
n1v0lg Dec 12, 2022
b668944
Yet more
n1v0lg Dec 12, 2022
0b5fb77
Use queue
n1v0lg Dec 12, 2022
1a166c6
Merge branch 'main' into send-remote-access-headers
n1v0lg Dec 12, 2022
12e3dc2
IT
n1v0lg Dec 12, 2022
5ec40eb
Merge branch 'main' into send-remote-access-headers
n1v0lg Dec 13, 2022
c00f4d9
More headers
n1v0lg Dec 13, 2022
9e44075
Minimize roundtrips
n1v0lg Dec 13, 2022
5281743
Remove old class
n1v0lg Dec 13, 2022
720e470
Fix v dumb typo
n1v0lg Dec 13, 2022
9bc0706
Simplify test
n1v0lg Dec 13, 2022
7ef7c72
Clean up
n1v0lg Dec 13, 2022
096786a
Merge branch 'main' into send-remote-access-headers
n1v0lg Dec 14, 2022
d208619
WIP conditions not met test
n1v0lg Dec 14, 2022
beec35b
More tests and cleanup
n1v0lg Dec 14, 2022
4ccdd71
Search local cluster
n1v0lg Dec 14, 2022
89c001b
WIP multi-cluster test
n1v0lg Dec 14, 2022
84c5f83
Multicluster test
n1v0lg Dec 14, 2022
0698d0c
Nit clean up
n1v0lg Dec 14, 2022
dfec24b
Assert on local results
n1v0lg Dec 14, 2022
eeff6ab
Refresh true on index doc
n1v0lg Dec 14, 2022
6f07a4a
Rm TODO
n1v0lg Dec 14, 2022
8acd2e5
Undo whitespace
n1v0lg Dec 14, 2022
6543cc0
Merge branch 'main' into send-remote-access-headers
n1v0lg Dec 15, 2022
8043952
WIP address review feedback
n1v0lg Dec 15, 2022
bcb9f4f
Address comments
n1v0lg Dec 15, 2022
8fc8c2a
Javadoc
n1v0lg Dec 15, 2022
369c1df
Merge branch 'main' into send-remote-access-headers
n1v0lg Dec 15, 2022
845c674
Merge branch 'main' into send-remote-access-headers
n1v0lg Dec 16, 2022
ea93667
Merge branch 'send-remote-access-headers' into poc/remote-access-auth…
n1v0lg Dec 16, 2022
a3339c4
New roles etc
n1v0lg Dec 16, 2022
073148e
Hack around scroll handling
n1v0lg Dec 16, 2022
71e3a52
Merge branch 'main' into poc/remote-access-authorization
n1v0lg Dec 21, 2022
6b5e4bc
Merge
n1v0lg Dec 21, 2022
1f748b6
WIP remote access support for internal users
n1v0lg Dec 27, 2022
e45f2a0
Also throw
n1v0lg Dec 27, 2022
39ba86f
Tweaks
n1v0lg Dec 27, 2022
3ab9d5f
Fix resolve remote cluster alias
n1v0lg Dec 27, 2022
dbce724
Merge branch 'main' into remote-access-for-internal-users
n1v0lg Dec 27, 2022
fdcb86f
Resolve cluster alias for opened connections
n1v0lg Dec 27, 2022
46475de
Merge branch 'main' into use-internal-remote-connection-on-open
n1v0lg Dec 28, 2022
0419e7c
Merge branch 'main' into remote-access-for-internal-users
n1v0lg Dec 28, 2022
23ce32a
Adjust tests
n1v0lg Dec 28, 2022
a7298f9
Fix typo
n1v0lg Dec 28, 2022
4236c42
Remove assertion
n1v0lg Dec 28, 2022
6f8a796
Merge branch 'use-internal-remote-connection-on-open' into poc/remote…
n1v0lg Dec 28, 2022
6c16e9c
Hack internal users
n1v0lg Dec 28, 2022
c7f54bf
WIP cross cluster search user
n1v0lg Dec 28, 2022
cadf2d2
Merge branch 'remote-access-for-internal-users' into poc/remote-acces…
n1v0lg Dec 28, 2022
d450731
Async search user not involved in CCS
n1v0lg Dec 29, 2022
25d8ca7
Remove async user for now
n1v0lg Dec 29, 2022
64fdd2e
Merge branch 'main' into poc/remote-access-authorization
n1v0lg Jan 17, 2023
04e593d
WIP profiles and separate authenticator
n1v0lg Jan 17, 2023
0782f93
Merge branch 'main' into poc/remote-access-authorization
n1v0lg Jan 17, 2023
d7941f7
Nit
n1v0lg Jan 18, 2023
2aea06e
Merge branch 'main' into poc/remote-access-authorization
n1v0lg Jan 18, 2023
2bf04cb
Rewire internal user handling
n1v0lg Jan 18, 2023
28cedc4
Detect remote cluster profile
n1v0lg Jan 18, 2023
8538534
Strip headers
n1v0lg Jan 18, 2023
4f81cc3
Two clusters
n1v0lg Jan 18, 2023
955c739
Remote access authenticator stands alone
n1v0lg Jan 18, 2023
3d919e1
Tweaks
n1v0lg Jan 18, 2023
444699a
Nits
n1v0lg Jan 18, 2023
eec7ab8
Exclude remote access type from tests
n1v0lg Jan 18, 2023
7d29281
Clean up
n1v0lg Jan 18, 2023
bf1e4f6
Undo
n1v0lg Jan 18, 2023
0e9e4a5
Another nit
n1v0lg Jan 18, 2023
d5fe668
Merge branch 'main' into poc/remote-access-authorization
n1v0lg Jan 18, 2023
3b84ef9
Enum ordinal matters
n1v0lg Jan 18, 2023
30a650d
More test fixes
n1v0lg Jan 18, 2023
2586d22
WIP explore re-using authc chain
n1v0lg Jan 19, 2023
db9d4a0
Works with alternative
n1v0lg Jan 19, 2023
003b4aa
Limited role fix
n1v0lg Jan 19, 2023
65bea32
More
n1v0lg Jan 19, 2023
b674a80
Fix
n1v0lg Jan 19, 2023
9871670
More tweaks
n1v0lg Jan 19, 2023
acd0b0e
Spotless
n1v0lg Jan 19, 2023
7f0e548
Integ test
n1v0lg Jan 19, 2023
0c16aa1
Remove unused classes
n1v0lg Jan 19, 2023
97b0681
Merge
n1v0lg Jan 20, 2023
0adb15e
Remove other test
n1v0lg Jan 20, 2023
10252c0
Clean up
n1v0lg Jan 20, 2023
7bfa646
Nit
n1v0lg Jan 20, 2023
5738221
Merge branch 'main' into poc/remote-access-authorization
n1v0lg Jan 23, 2023
83e1fda
Merge branch 'main' into poc/remote-access-authorization
n1v0lg Jan 23, 2023
a535210
Merge branch 'main' into poc/remote-access-authorization
n1v0lg Jan 26, 2023
3fc93e5
Fixes
n1v0lg Jan 26, 2023
a358ffa
Merge branch 'main' into poc/remote-access-authorization
n1v0lg Jan 31, 2023
fea0ee0
Merge and clean up and better encapsulate remote access authc
n1v0lg Jan 31, 2023
6be3ca9
More clean up
n1v0lg Jan 31, 2023
33d3353
Dont need api key service as field
n1v0lg Jan 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,23 @@ def fulfillingCluster = testClusters.register('fulfilling-cluster') {
setting 'xpack.watcher.enabled', 'false'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is safe to ignore: hacking it to work with two fulfilling clusters and the new remote port set up.

setting 'xpack.ml.enabled', 'false'
setting 'xpack.license.self_generated.type', 'trial'
setting 'remote_cluster.enabled', 'true'
// TODO
setting 'remote_cluster.port', '9901'

user username: 'elastic-admin', password: 'elastic-password', role: '_es_test_root'
}

def fulfillingCluster2 = testClusters.register('fulfilling-cluster2') {
testDistribution = providers.systemProperty('run.distribution').orElse('default').get()

setting 'xpack.security.enabled', 'true'
setting 'xpack.watcher.enabled', 'false'
setting 'xpack.ml.enabled', 'false'
setting 'xpack.license.self_generated.type', 'trial'
setting 'remote_cluster.enabled', 'true'
// TODO
setting 'remote_cluster.port', '9902'

user username: 'elastic-admin', password: 'elastic-password', role: '_es_test_root'
}
Expand All @@ -29,27 +46,38 @@ def queryingCluster = testClusters.register('querying-cluster') {
setting 'xpack.ml.enabled', 'false'
setting 'xpack.license.self_generated.type', 'trial'
setting 'cluster.remote.connections_per_cluster', "1"

user username: 'elastic-admin', password: 'elastic-password', role: '_es_test_root'
user username: 'ccs-admin', password: 'elastic-password', role: '_ccs_admin'
}


tasks.register("run-ccs", RunTask) {
useCluster fulfillingCluster2
useCluster fulfillingCluster
useCluster queryingCluster
doFirst {
queryingCluster.get().getNodes().each { node ->
if (proxyMode) {
node.setting('cluster.remote.my_remote_cluster.mode', 'proxy')
node.setting('cluster.remote.my_remote_cluster.proxy_address', "\"${fulfillingCluster.get().getAllTransportPortURI().get(0)}\"")
node.setting('cluster.remote.my_remote_cluster.proxy_address', "\"${fulfillingCluster.get().getAllRemoteAccessPortURI().get(0)}\"")
node.setting('cluster.remote.my_remote_cluster2.mode', 'proxy')
node.setting('cluster.remote.my_remote_cluster2.proxy_address', "\"${fulfillingCluster2.get().getAllRemoteAccessPortURI().get(0)}\"")
} else {
node.setting('cluster.remote.my_remote_cluster.seeds', fulfillingCluster.get().getAllTransportPortURI().collect { "\"$it\"" }.toString())
node.setting('cluster.remote.my_remote_cluster.seeds', fulfillingCluster.get().getAllRemoteAccessPortURI().collect { "\"$it\"" }.toString())
node.setting('cluster.remote.my_remote_cluster2.seeds', fulfillingCluster2.get().getAllRemoteAccessPortURI().collect { "\"$it\"" }.toString())
}
}
queryingCluster.get().restart() //updates the on-disk config

println "** Querying cluster HTTP endpoints are: ${-> queryingCluster.get().allHttpSocketURI.join(",")}"
println "** Querying cluster transport endpoints are: ${-> queryingCluster.get().getAllTransportPortURI().join(",")}"

println "** Fulfilling cluster HTTP endpoints are: ${-> fulfillingCluster.get().allHttpSocketURI.join(",")}"
println "** Fulfilling cluster transport endpoints are: ${-> fulfillingCluster.get().getAllTransportPortURI().join(",")}"
println "** Fulfilling cluster remote access endpoints are: ${-> fulfillingCluster.get().getAllRemoteAccessPortURI().join(",")}"

println "** Fulfilling cluster 2 HTTP endpoints are: ${-> fulfillingCluster2.get().allHttpSocketURI.join(",")}"
println "** Fulfilling cluster 2 transport endpoints are: ${-> fulfillingCluster2.get().getAllTransportPortURI().join(",")}"
println "** Fulfilling cluster 2 remote access endpoints are: ${-> fulfillingCluster2.get().getAllRemoteAccessPortURI().join(",")}"
}
}
8 changes: 8 additions & 0 deletions build-tools/src/main/resources/roles.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,11 @@ _es_test_root:
- application: "*"
privileges: [ "*" ]
resources: [ "*" ]

#_ccs_admin:
# cluster: [ "ALL" ]
# remote_indices:
# - names: [ "my-index-*" ]
# privileges: [ "READ", "READ_CROSS_CLUSTER" ]
# clusters: [ "*remote*" ]

Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,14 @@ public void removeListener(TransportConnectionListener listener) {

@Override
public void openConnection(DiscoveryNode node, ConnectionProfile profile, ActionListener<Transport.Connection> listener) {
delegate.openConnection(node, profile, listener);
delegate.openConnection(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change from this PR: #92584 -- pending approval from distributed team.

node,
profile,
ActionListener.wrap(
connection -> listener.onResponse(new InternalRemoteConnection(connection, clusterAlias)),
listener::onFailure
)
);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.net.InetAddress;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;

import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItems;
Expand Down Expand Up @@ -89,7 +90,7 @@ public void testGetConnection() {
assertEquals(1, versions.size());
}

public void testResolveRemoteClusterAlias() {
public void testResolveRemoteClusterAlias() throws ExecutionException, InterruptedException {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change from this PR: #92584 -- pending approval from distributed team.

DiscoveryNode remoteNode1 = new DiscoveryNode("remote-node-1", address, Version.CURRENT);
PlainActionFuture<Void> future = PlainActionFuture.newFuture();
remoteConnectionManager.connectToRemoteClusterNode(remoteNode1, validator, future);
Expand All @@ -105,6 +106,10 @@ public void testResolveRemoteClusterAlias() {
Transport.Connection proxyConnection = remoteConnectionManager.getConnection(remoteNode2);
assertThat(proxyConnection, instanceOf(RemoteConnectionManager.ProxyConnection.class));
assertThat(RemoteConnectionManager.resolveRemoteClusterAlias(proxyConnection).get(), equalTo("remote-cluster"));

PlainActionFuture<Transport.Connection> future2 = PlainActionFuture.newFuture();
remoteConnectionManager.openConnection(remoteNode1, null, future2);
assertThat(RemoteConnectionManager.resolveRemoteClusterAlias(future2.get()).get(), equalTo("remote-cluster"));
}

private static class TestRemoteConnection extends CloseableConnection {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import static org.elasticsearch.xpack.core.security.authc.Authentication.RealmRef.newApiKeyRealmRef;
import static org.elasticsearch.xpack.core.security.authc.Authentication.RealmRef.newInternalAttachRealmRef;
import static org.elasticsearch.xpack.core.security.authc.Authentication.RealmRef.newInternalFallbackRealmRef;
import static org.elasticsearch.xpack.core.security.authc.Authentication.RealmRef.newRemoteAccessRealmRef;
import static org.elasticsearch.xpack.core.security.authc.Authentication.RealmRef.newServiceAccountRealmRef;
import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.ANONYMOUS_REALM_NAME;
import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.ANONYMOUS_REALM_TYPE;
Expand All @@ -62,6 +63,8 @@
import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.ATTACH_REALM_TYPE;
import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.FALLBACK_REALM_NAME;
import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.FALLBACK_REALM_TYPE;
import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.REMOTE_ACCESS_REALM_NAME;
import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.REMOTE_ACCESS_REALM_TYPE;
import static org.elasticsearch.xpack.core.security.authc.RealmDomain.REALM_DOMAIN_PARSER;

/**
Expand Down Expand Up @@ -509,6 +512,7 @@ public void writeTo(StreamOutput out) throws IOException {
*/
public boolean canAccessResourcesOf(Authentication resourceCreatorAuthentication) {
// if we introduce new authentication types in the future, it is likely that we'll need to revisit this method

assert EnumSet.of(
Authentication.AuthenticationType.REALM,
Authentication.AuthenticationType.API_KEY,
Expand Down Expand Up @@ -792,6 +796,10 @@ static RealmRef newApiKeyRealmRef(String nodeName) {
// no domain for API Key tokens
return new RealmRef(API_KEY_REALM_NAME, API_KEY_REALM_TYPE, nodeName, null);
}

static RealmRef newRemoteAccessRealmRef(String apiKeyId, String nodeName) {
return new RealmRef(REMOTE_ACCESS_REALM_NAME + "_" + apiKeyId, REMOTE_ACCESS_REALM_TYPE, nodeName, null);
}
}

public static boolean isFileOrNativeRealm(String realmType) {
Expand Down Expand Up @@ -882,6 +890,33 @@ public static Authentication newApiKeyAuthentication(AuthenticationResult<User>
return authentication;
}

public static Authentication newRemoteAccessAuthentication(
AuthenticationResult<User> authResult,
Authentication receivedAuthentication,
String nodeName
) {
assert authResult.isAuthenticated() : "API Key authn for remote access result must be successful";
final User receivedUser = receivedAuthentication.getEffectiveSubject().getUser();
assert receivedUser.enabled();
final User user = new User(
// Consider including API key ID as well
receivedUser.principal(),
new String[0],
receivedUser.fullName(),
receivedUser.email(),
receivedUser.metadata(),
true
);
final User apiKeyUser = authResult.getValue();
final Authentication.RealmRef authenticatedBy = newRemoteAccessRealmRef(apiKeyUser.principal(), nodeName);
final Authentication authentication = new Authentication(
new Subject(user, authenticatedBy, Version.CURRENT, authResult.getMetadata()),
AuthenticationType.API_KEY
);
assert false == authentication.isAssignedToDomain();
return authentication;
}

private static RealmRef maybeRewriteRealmRef(Version streamVersion, RealmRef realmRef) {
if (realmRef != null && realmRef.getDomain() != null && streamVersion.before(VERSION_REALM_DOMAINS)) {
logger.info("Rewriting realm [" + realmRef + "] without domain");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,9 @@ public final class AuthenticationField {
public static final String ATTACH_REALM_NAME = "__attach";
public static final String ATTACH_REALM_TYPE = "__attach";

public static final String REMOTE_ACCESS_REALM_NAME = "_es_remote_access";
public static final String REMOTE_ACCESS_REALM_TYPE = "_es_remote_access";
public static final String REMOTE_ACCESS_ROLE_DESCRIPTORS_KEY = "_security_remote_access_role_descriptors";

private AuthenticationField() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public String toString() {
+ '}';
}

private static List<RoleDescriptorsBytes> toRoleDescriptorsBytesList(final RoleDescriptorsIntersection roleDescriptorsIntersection)
public static List<RoleDescriptorsBytes> toRoleDescriptorsBytesList(final RoleDescriptorsIntersection roleDescriptorsIntersection)
throws IOException {
// If we ever lift this restriction, we need to ensure that the serialization of each set of role descriptors to raw bytes is
// deterministic. We can do so by sorting the role descriptors before serializing.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

package org.elasticsearch.xpack.core.security.authc;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.Version;
import org.elasticsearch.common.bytes.BytesArray;
Expand All @@ -19,12 +21,15 @@
import org.elasticsearch.xpack.core.security.user.AnonymousUser;
import org.elasticsearch.xpack.core.security.user.User;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static org.elasticsearch.xpack.core.security.authc.Authentication.VERSION_API_KEY_ROLES_AS_BYTES;
import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY;
import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY;
import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.REMOTE_ACCESS_ROLE_DESCRIPTORS_KEY;
import static org.elasticsearch.xpack.core.security.authc.Subject.Type.API_KEY;

/**
Expand All @@ -35,10 +40,13 @@
*/
public class Subject {

private static final Logger logger = LogManager.getLogger(Subject.class);

public enum Type {
USER,
API_KEY,
SERVICE_ACCOUNT,
REMOTE_ACCESS,
}

private final Version version;
Expand All @@ -65,6 +73,9 @@ public Subject(User user, Authentication.RealmRef realm, Version version, Map<St
} else if (ServiceAccountSettings.REALM_TYPE.equals(realm.getType())) {
assert ServiceAccountSettings.REALM_NAME.equals(realm.getName()) : "service account realm name mismatch";
this.type = Type.SERVICE_ACCOUNT;
} else if (AuthenticationField.REMOTE_ACCESS_REALM_TYPE.equals(realm.getType())) {
assert realm.getName().startsWith(AuthenticationField.REMOTE_ACCESS_REALM_NAME) : "remote access realm name prefix mismatch";
this.type = Type.REMOTE_ACCESS;
} else {
this.type = Type.USER;
}
Expand Down Expand Up @@ -99,6 +110,8 @@ public RoleReferenceIntersection getRoleReferenceIntersection(@Nullable Anonymou
return buildRoleReferencesForApiKey();
case SERVICE_ACCOUNT:
return new RoleReferenceIntersection(new RoleReference.ServiceAccountRoleReference(user.principal()));
case REMOTE_ACCESS:
return buildRoleReferencesForRemoteAccess();
default:
assert false : "unknown subject type: [" + type + "]";
throw new IllegalStateException("unknown subject type: [" + type + "]");
Expand Down Expand Up @@ -220,6 +233,23 @@ private RoleReferenceIntersection buildRoleReferencesForApiKey() {
);
}

private RoleReferenceIntersection buildRoleReferencesForRemoteAccess() {
final List<RoleReference> roleReferences = new ArrayList<>(buildRoleReferencesForApiKey().getRoleReferences());
@SuppressWarnings("unchecked")
final List<RemoteAccessAuthentication.RoleDescriptorsBytes> remoteAccessRoleDescriptorsBytes = (List<
RemoteAccessAuthentication.RoleDescriptorsBytes>) metadata.get(REMOTE_ACCESS_ROLE_DESCRIPTORS_KEY);
if (remoteAccessRoleDescriptorsBytes.isEmpty()) {
// TODO hack. fail earlier, and assert here instead
return new RoleReferenceIntersection(List.of(new RoleReference.NamedRoleReference(new String[] {})));
}
// TODO handle this once we support API keys as querying subjects
assert remoteAccessRoleDescriptorsBytes.size() == 1;
for (RemoteAccessAuthentication.RoleDescriptorsBytes roleDescriptorsBytes : remoteAccessRoleDescriptorsBytes) {
roleReferences.add(new RoleReference.RemoteAccessRoleReference(roleDescriptorsBytes));
}
return new RoleReferenceIntersection(List.copyOf(roleReferences));
}

private static boolean isEmptyRoleDescriptorsBytes(BytesReference roleDescriptorsBytes) {
return roleDescriptorsBytes == null || (roleDescriptorsBytes.length() == 2 && "{}".equals(roleDescriptorsBytes.utf8ToString()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,7 @@ public IndicesAccessControl authorize(
*/
@Override
public IsResourceAuthorizedPredicate allowedIndicesMatcher(String action) {
IsResourceAuthorizedPredicate predicate = baseRole.indices().allowedIndicesMatcher(action);
predicate = predicate.and(limitedByRole.indices().allowedIndicesMatcher(action));
return predicate;
return baseRole.allowedIndicesMatcher(action).and(limitedByRole.allowedIndicesMatcher(action));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.hash.MessageDigests;
import org.elasticsearch.xpack.core.security.authc.RemoteAccessAuthentication;

import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -116,6 +117,38 @@ public ApiKeyRoleType getRoleType() {
}
}

final class RemoteAccessRoleReference implements RoleReference {

private final RemoteAccessAuthentication.RoleDescriptorsBytes roleDescriptorsBytes;
private RoleKey id = null;

public RemoteAccessRoleReference(RemoteAccessAuthentication.RoleDescriptorsBytes roleDescriptorsBytes) {
this.roleDescriptorsBytes = roleDescriptorsBytes;
}

@Override
public RoleKey id() {
// Hashing can be expensive. memorize the result in case the method is called multiple times.
if (id == null) {
final String roleDescriptorsHash = MessageDigests.toHexString(
MessageDigests.digest(roleDescriptorsBytes, MessageDigests.sha256())
);
id = new RoleKey(Set.of("remote_access:" + roleDescriptorsHash), "remote_access");
}
return id;
}

@Override
public void resolve(RoleReferenceResolver resolver, ActionListener<RolesRetrievalResult> listener) {
resolver.resolveRemoteAccessRoleReference(this, listener);
}

public RemoteAccessAuthentication.RoleDescriptorsBytes getRoleDescriptorsBytes() {
return roleDescriptorsBytes;
}

}

/**
* Same as {@link ApiKeyRoleReference} but for BWC purpose (prior to v7.9.0)
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ public interface RoleReferenceResolver {

void resolveApiKeyRoleReference(RoleReference.ApiKeyRoleReference apiKeyRoleReference, ActionListener<RolesRetrievalResult> listener);

void resolveRemoteAccessRoleReference(
RoleReference.RemoteAccessRoleReference remoteAccessRoleReference,
ActionListener<RolesRetrievalResult> listener
);

void resolveBwcApiKeyRoleReference(
RoleReference.BwcApiKeyRoleReference bwcApiKeyRoleReference,
ActionListener<RolesRetrievalResult> listener
Expand Down
Loading