Skip to content

Commit 68ec7b1

Browse files
Add new internal cross-project search user (#135531)
This PR introduces a dedicated `_cross_project_search` user to perform internal orchestration actions for cross project operations, like establishing remote cluster handshakes, cancelling cross- cluster searches, etc...
1 parent aa04178 commit 68ec7b1

File tree

3 files changed

+79
-1
lines changed

3 files changed

+79
-1
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/InternalUsers.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import org.elasticsearch.action.search.TransportSearchAction;
3030
import org.elasticsearch.action.search.TransportSearchScrollAction;
3131
import org.elasticsearch.index.reindex.ReindexAction;
32+
import org.elasticsearch.tasks.TaskCancellationService;
33+
import org.elasticsearch.transport.RemoteClusterService;
3234
import org.elasticsearch.xpack.core.XPackPlugin;
3335
import org.elasticsearch.xpack.core.ilm.action.ILMActions;
3436
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
@@ -294,6 +296,29 @@ public class InternalUsers {
294296
)
295297
);
296298

299+
/**
300+
* Internal user that can manage a cross-project connections (e.g. handshake)
301+
* and searches (e.g. cancelling).
302+
*/
303+
public static final InternalUser CROSS_PROJECT_SEARCH_USER = new InternalUser(
304+
UsernamesField.CROSS_PROJECT_SEARCH_USER_NAME,
305+
new RoleDescriptor(
306+
UsernamesField.CROSS_PROJECT_SEARCH_ROLE_NAME,
307+
new String[] {
308+
RemoteClusterService.REMOTE_CLUSTER_HANDSHAKE_ACTION_NAME,
309+
TaskCancellationService.REMOTE_CLUSTER_BAN_PARENT_ACTION_NAME,
310+
TaskCancellationService.REMOTE_CLUSTER_CANCEL_CHILD_ACTION_NAME,
311+
"cluster:internal:data/read/esql/open_exchange",
312+
"cluster:internal:data/read/esql/exchange" },
313+
null,
314+
null,
315+
null,
316+
null,
317+
MetadataUtils.DEFAULT_RESERVED_METADATA,
318+
Map.of()
319+
)
320+
);
321+
297322
public static final SystemUser SYSTEM_USER = SystemUser.INSTANCE;
298323

299324
private static final Map<String, InternalUser> INTERNAL_USERS;
@@ -309,7 +334,8 @@ public class InternalUsers {
309334
DATA_STREAM_LIFECYCLE_USER,
310335
REINDEX_DATA_STREAM_USER,
311336
SYNONYMS_USER,
312-
LAZY_ROLLOVER_USER
337+
LAZY_ROLLOVER_USER,
338+
CROSS_PROJECT_SEARCH_USER
313339
).collect(Collectors.toUnmodifiableMap(InternalUser::principal, Function.identity()));
314340
}
315341

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/UsernamesField.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public final class UsernamesField {
3434
public static final String STORAGE_ROLE_NAME = "_storage";
3535
public static final String SYNONYMS_USER_NAME = "_synonyms";
3636
public static final String SYNONYMS_ROLE_NAME = "_synonyms";
37+
public static final String CROSS_PROJECT_SEARCH_USER_NAME = "_cross_project_search";
38+
public static final String CROSS_PROJECT_SEARCH_ROLE_NAME = "_cross_project_search";
3739

3840
public static final String REMOTE_MONITORING_NAME = "remote_monitoring_user";
3941
public static final String REMOTE_MONITORING_COLLECTION_ROLE = "remote_monitoring_collector";

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/user/InternalUsersTests.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,22 @@
3939
import org.elasticsearch.common.settings.Settings;
4040
import org.elasticsearch.index.IndexVersion;
4141
import org.elasticsearch.index.reindex.ReindexAction;
42+
import org.elasticsearch.tasks.TaskCancellationService;
4243
import org.elasticsearch.test.ESTestCase;
44+
import org.elasticsearch.transport.RemoteClusterService;
4345
import org.elasticsearch.transport.TransportRequest;
46+
import org.elasticsearch.transport.TransportService;
4447
import org.elasticsearch.xpack.core.XPackPlugin;
48+
import org.elasticsearch.xpack.core.action.XPackInfoAction;
4549
import org.elasticsearch.xpack.core.ml.action.UpdateJobAction;
4650
import org.elasticsearch.xpack.core.security.authc.Authentication;
4751
import org.elasticsearch.xpack.core.security.authc.AuthenticationTestHelper;
52+
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
4853
import org.elasticsearch.xpack.core.security.authz.permission.ApplicationPermission;
4954
import org.elasticsearch.xpack.core.security.authz.permission.ClusterPermission;
5055
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsCache;
56+
import org.elasticsearch.xpack.core.security.authz.permission.IndicesPermission;
57+
import org.elasticsearch.xpack.core.security.authz.permission.RemoteClusterPermissions;
5158
import org.elasticsearch.xpack.core.security.authz.permission.RemoteIndicesPermission;
5259
import org.elasticsearch.xpack.core.security.authz.permission.Role;
5360
import org.elasticsearch.xpack.core.security.authz.permission.RunAsPermission;
@@ -57,11 +64,13 @@
5764

5865
import java.util.Arrays;
5966
import java.util.List;
67+
import java.util.Optional;
6068

6169
import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.INTERNAL_SECURITY_MAIN_INDEX_7;
6270
import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.INTERNAL_SECURITY_TOKENS_INDEX_7;
6371
import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.SECURITY_MAIN_ALIAS;
6472
import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.SECURITY_TOKENS_ALIAS;
73+
import static org.elasticsearch.xpack.core.security.user.UsernamesField.CROSS_PROJECT_SEARCH_USER_NAME;
6574
import static org.elasticsearch.xpack.core.security.user.UsernamesField.REINDEX_DATA_STREAM_NAME;
6675
import static org.hamcrest.Matchers.arrayContaining;
6776
import static org.hamcrest.Matchers.equalTo;
@@ -358,6 +367,47 @@ public void testRegularUser() {
358367
expectThrows(IllegalStateException.class, () -> InternalUsers.getUser(username));
359368
}
360369

370+
public void testCrossProjectSearchUser() {
371+
final InternalUser crossProjectSearchUser = InternalUsers.CROSS_PROJECT_SEARCH_USER;
372+
assertThat(InternalUsers.getUser(CROSS_PROJECT_SEARCH_USER_NAME), is(crossProjectSearchUser));
373+
374+
assertThat(crossProjectSearchUser.getRemoteAccessRoleDescriptor().isPresent(), equalTo(false));
375+
376+
Optional<RoleDescriptor> localClusterRoleDescriptor = crossProjectSearchUser.getLocalClusterRoleDescriptor();
377+
assertThat(localClusterRoleDescriptor.isPresent(), equalTo(true));
378+
assertThat(localClusterRoleDescriptor.get().getMetadata(), equalTo(MetadataUtils.DEFAULT_RESERVED_METADATA));
379+
380+
final SimpleRole role = getLocalClusterRole(crossProjectSearchUser);
381+
382+
assertThat(role.indices(), is(IndicesPermission.NONE));
383+
assertThat(role.runAs(), is(RunAsPermission.NONE));
384+
assertThat(role.application(), is(ApplicationPermission.NONE));
385+
assertThat(role.remoteIndices(), is(RemoteIndicesPermission.NONE));
386+
assertThat(role.remoteCluster(), is(RemoteClusterPermissions.NONE));
387+
assertThat(role.hasFieldOrDocumentLevelSecurity(), is(false));
388+
assertThat(role.hasWorkflowsRestriction(), is(false));
389+
390+
final List<String> allowedClusterActions = List.of(
391+
RemoteClusterService.REMOTE_CLUSTER_HANDSHAKE_ACTION_NAME,
392+
TaskCancellationService.REMOTE_CLUSTER_BAN_PARENT_ACTION_NAME,
393+
TaskCancellationService.REMOTE_CLUSTER_CANCEL_CHILD_ACTION_NAME,
394+
"cluster:internal:data/read/esql/open_exchange",
395+
"cluster:internal:data/read/esql/exchange"
396+
);
397+
398+
for (String clusterAction : allowedClusterActions) {
399+
checkClusterAccess(crossProjectSearchUser, role, clusterAction, true);
400+
}
401+
402+
checkClusterAccess(
403+
crossProjectSearchUser,
404+
role,
405+
randomFrom(ClusterStateAction.NAME, XPackInfoAction.NAME, TransportService.HANDSHAKE_ACTION_NAME),
406+
false
407+
);
408+
409+
}
410+
361411
private static SimpleRole getLocalClusterRole(InternalUser internalUser) {
362412
final FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY);
363413
return Role.buildFromRoleDescriptor(

0 commit comments

Comments
 (0)