From 794b1f2957af94cf53e3cd86688f134b0c826863 Mon Sep 17 00:00:00 2001 From: Tim Vernum Date: Wed, 5 Jan 2022 14:14:43 +1100 Subject: [PATCH 1/2] Allow read template with cluster monitor privilege All three template types (legacy templates, composable index templates and component templates) are stored in cluster state metadata (in fields "templates", "index_template" and "component_template"). This cluster state is readable (via GET /_cluster/state) for users who have the monitor privilege at the cluster level. However, calling the explicit read endpoints for these templates required the manage_index_templates privilege. This change grants access to the template specific retrieval APIs for all users (or API Keys) with the cluster monitor privilge so that they can make use of these fit-for-purpose APIs instead of parsing data directly from cluster metadata Relates: https://github.com/elastic/beats/issues/29554, #78832 Backport of: #82046 --- .../authz/privilege/ClusterPrivilegeResolver.java | 10 +++++++++- .../authz/privilege/ClusterPrivilegeTests.java | 15 +++++++++++++++ .../authz/store/ReservedRolesStoreTests.java | 2 +- .../ClusterPrivilegeIntegrationTests.java | 8 ++++++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java index 51833b65776f6..1ad3411014584 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java @@ -15,6 +15,9 @@ import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsAction; import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusAction; import org.elasticsearch.action.admin.cluster.state.ClusterStateAction; +import org.elasticsearch.action.admin.indices.template.get.GetComponentTemplateAction; +import org.elasticsearch.action.admin.indices.template.get.GetComposableIndexTemplateAction; +import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesAction; import org.elasticsearch.action.ingest.GetPipelineAction; import org.elasticsearch.action.ingest.SimulatePipelineAction; import org.elasticsearch.common.Strings; @@ -69,7 +72,12 @@ public class ClusterPrivilegeResolver { private static final Set MANAGE_SERVICE_ACCOUNT_PATTERN = Collections.singleton( "cluster:admin/xpack/security/service_account/*" ); - private static final Set MONITOR_PATTERN = Collections.singleton("cluster:monitor/*"); + private static final Set MONITOR_PATTERN = Set.of( + "cluster:monitor/*", + GetIndexTemplatesAction.NAME, + GetComponentTemplateAction.NAME, + GetComposableIndexTemplateAction.NAME + ); private static final Set MONITOR_TEXT_STRUCTURE_PATTERN = Collections.singleton("cluster:monitor/text_structure/*"); private static final Set MONITOR_TRANSFORM_PATTERN = Collections.unmodifiableSet( Sets.newHashSet("cluster:monitor/data_frame/*", "cluster:monitor/transform/*") diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeTests.java index 267d04984fd0c..ad9f02cf27ea7 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeTests.java @@ -8,16 +8,31 @@ package org.elasticsearch.xpack.core.security.authz.privilege; import org.elasticsearch.action.ActionType; +import org.elasticsearch.action.admin.indices.template.get.GetComponentTemplateAction; +import org.elasticsearch.action.admin.indices.template.get.GetComposableIndexTemplateAction; +import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesAction; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.core.enrich.action.EnrichStatsAction; import org.elasticsearch.xpack.core.security.authz.permission.ClusterPermission; +import java.util.List; + public class ClusterPrivilegeTests extends ESTestCase { public void testMonitorPrivilegeWillGrantActions() { assertGranted(ClusterPrivilegeResolver.MONITOR, EnrichStatsAction.INSTANCE); } + public void testMonitorPrivilegeGrantsGetTemplateActions() { + for (var action : List.of( + GetComponentTemplateAction.INSTANCE, + GetComposableIndexTemplateAction.INSTANCE, + GetIndexTemplatesAction.INSTANCE + )) { + assertGranted(ClusterPrivilegeResolver.MONITOR, action); + } + } + public static void assertGranted(ClusterPrivilege clusterPrivilege, ActionType actionType) { assertTrue(clusterPrivilege.buildPermission(ClusterPermission.builder()).build().check(actionType.name(), null, null)); } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java index d6872673a597b..046559704da04 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java @@ -1206,7 +1206,7 @@ public void testRemoteMonitoringCollectorRole() { assertThat(remoteMonitoringCollectorRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true)); assertThat(remoteMonitoringCollectorRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(true)); assertThat(remoteMonitoringCollectorRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(true)); - assertThat(remoteMonitoringCollectorRole.cluster().check(GetIndexTemplatesAction.NAME, request, authentication), is(false)); + assertThat(remoteMonitoringCollectorRole.cluster().check(GetIndexTemplatesAction.NAME, request, authentication), is(true)); assertThat(remoteMonitoringCollectorRole.cluster().check(PutIndexTemplateAction.NAME, request, authentication), is(false)); assertThat(remoteMonitoringCollectorRole.cluster().check(DeleteIndexTemplateAction.NAME, request, authentication), is(false)); assertThat(remoteMonitoringCollectorRole.cluster().check(ClusterRerouteAction.NAME, request, authentication), is(false)); diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/ClusterPrivilegeIntegrationTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/ClusterPrivilegeIntegrationTests.java index 48de739b10d7b..043760d0c02f1 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/ClusterPrivilegeIntegrationTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/ClusterPrivilegeIntegrationTests.java @@ -123,9 +123,17 @@ public void testThatClusterPrivilegesWorkAsExpectedViaHttp() throws Exception { assertAccessIsAllowed("user_b", "GET", "/_nodes/stats"); assertAccessIsAllowed("user_b", "GET", "/_nodes/hot_threads"); assertAccessIsAllowed("user_b", "GET", "/_nodes/infos"); + // monitoring allows template retrieval (because it's implied by having read access to cluster state + assertAccessIsAllowed("user_b", "GET", "/_cat/templates/" + (randomBoolean() ? "" : randomAlphaOfLengthBetween(2, 8))); + assertAccessIsAllowed("user_b", "GET", "/_template/"); + assertAccessIsAllowed("user_b", "GET", "/_index_template/"); + assertAccessIsAllowed("user_b", "GET", "/_component_template/"); // but no admin stuff assertAccessIsDenied("user_b", "POST", "/_cluster/reroute"); assertAccessIsDenied("user_b", "PUT", "/_cluster/settings", "{ \"transient\" : { \"search.default_search_timeout\": \"1m\" } }"); + assertAccessIsDenied("user_b", "DELETE", "/_template/" + randomAlphaOfLengthBetween(2, 8)); + assertAccessIsDenied("user_b", "DELETE", "/_index_template/" + randomAlphaOfLengthBetween(2, 8)); + assertAccessIsDenied("user_b", "DELETE", "/_component_template/" + randomAlphaOfLengthBetween(2, 8)); // sorry user_c, you are not allowed anything assertAccessIsDenied("user_c", "GET", "/_cluster/state"); From 65ea2bbb795330a130e201fc455a78c86677a3cb Mon Sep 17 00:00:00 2001 From: Tim Vernum Date: Wed, 5 Jan 2022 15:09:57 +1100 Subject: [PATCH 2/2] Fix backport --- .../security/authz/privilege/ClusterPrivilegeResolver.java | 2 +- .../core/security/authz/privilege/ClusterPrivilegeTests.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java index 1ad3411014584..df80d2823b129 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java @@ -72,7 +72,7 @@ public class ClusterPrivilegeResolver { private static final Set MANAGE_SERVICE_ACCOUNT_PATTERN = Collections.singleton( "cluster:admin/xpack/security/service_account/*" ); - private static final Set MONITOR_PATTERN = Set.of( + private static final Set MONITOR_PATTERN = Sets.newHashSet( "cluster:monitor/*", GetIndexTemplatesAction.NAME, GetComponentTemplateAction.NAME, diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeTests.java index ad9f02cf27ea7..2159ded00cefa 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeTests.java @@ -15,7 +15,7 @@ import org.elasticsearch.xpack.core.enrich.action.EnrichStatsAction; import org.elasticsearch.xpack.core.security.authz.permission.ClusterPermission; -import java.util.List; +import java.util.Arrays; public class ClusterPrivilegeTests extends ESTestCase { @@ -24,7 +24,7 @@ public void testMonitorPrivilegeWillGrantActions() { } public void testMonitorPrivilegeGrantsGetTemplateActions() { - for (var action : List.of( + for (ActionType action : Arrays.asList( GetComponentTemplateAction.INSTANCE, GetComposableIndexTemplateAction.INSTANCE, GetIndexTemplatesAction.INSTANCE