diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java index 2f3dece0868..168fd460021 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java @@ -59,6 +59,8 @@ public class InternalAuthorizationService extends AbstractComponent implements A public static final String INDICES_PERMISSIONS_KEY = "_indices_permissions"; static final String ORIGINATING_ACTION_KEY = "_originating_action_name"; + private static final Predicate MONITOR_INDEX_PREDICATE = IndexPrivilege.MONITOR.predicate(); + private final ClusterService clusterService; private final RolesStore rolesStore; private final AuditTrail auditTrail; @@ -218,8 +220,10 @@ public class InternalAuthorizationService extends AbstractComponent implements A throw denial(user, action, request); } else if (indicesAccessControl.getIndexPermissions(ShieldTemplateService.SECURITY_INDEX_NAME) != null && indicesAccessControl.getIndexPermissions(ShieldTemplateService.SECURITY_INDEX_NAME).isGranted() - && XPackUser.is(user) == false) { - // only the XPackUser is allowed to work with this index, but we should allow health/stats through + && XPackUser.is(user) == false + && MONITOR_INDEX_PREDICATE.test(action) == false) { + // only the XPackUser is allowed to work with this index, but we should allow indices monitoring actions through for debugging + // purposes. These monitor requests also sometimes resolve indices concretely and then requests them logger.debug("user [{}] attempted to directly perform [{}] against the security index [{}]", user.principal(), action, ShieldTemplateService.SECURITY_INDEX_NAME); throw denial(user, action, request); diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authc/esnative/NativeRealmIntegTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authc/esnative/NativeRealmIntegTests.java index df8c8859651..5fc13f76fce 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authc/esnative/NativeRealmIntegTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authc/esnative/NativeRealmIntegTests.java @@ -8,6 +8,7 @@ package org.elasticsearch.shield.authc.esnative; import org.apache.lucene.util.CollectionUtil; import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; +import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.Strings; import org.elasticsearch.common.ValidationException; @@ -34,6 +35,7 @@ import static org.elasticsearch.shield.authc.support.UsernamePasswordToken.basic import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; /** * Tests for the ESNativeUsersStore and ESNativeRolesStore @@ -397,4 +399,24 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase { assertThat(v.getMessage().contains("password"), is(true)); } } + + public void testUsersAndRolesDoNotInterfereWithIndicesStats() throws Exception { + client().prepareIndex("foo", "bar").setSource("ignore", "me").get(); + + SecurityClient client = securityClient(); + if (randomBoolean()) { + client.preparePutUser("joe", "s3krit".toCharArray(), ShieldSettingsSource.DEFAULT_ROLE).get(); + } else { + client.preparePutRole("read_role") + .cluster("none") + .addIndices(new String[]{"*"}, new String[]{"read"}, null, null) + .get(); + } + + IndicesStatsResponse response = client().admin().indices().prepareStats("foo", ShieldTemplateService.SECURITY_INDEX_NAME).get(); + assertThat(response.getIndices().size(), is(2)); + assertThat(response.getIndices().get(ShieldTemplateService.SECURITY_INDEX_NAME), notNullValue()); + assertThat(response.getIndices().get(ShieldTemplateService.SECURITY_INDEX_NAME).getIndex(), + is(ShieldTemplateService.SECURITY_INDEX_NAME)); + } } diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/InternalAuthorizationServiceTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/InternalAuthorizationServiceTests.java index 0b632ddafaa..ad05d5b3d5c 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/InternalAuthorizationServiceTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/InternalAuthorizationServiceTests.java @@ -15,6 +15,20 @@ import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; import org.elasticsearch.action.admin.indices.create.CreateIndexAction; import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest; +import org.elasticsearch.action.admin.indices.recovery.RecoveryAction; +import org.elasticsearch.action.admin.indices.recovery.RecoveryRequest; +import org.elasticsearch.action.admin.indices.segments.IndicesSegmentsAction; +import org.elasticsearch.action.admin.indices.segments.IndicesSegmentsRequest; +import org.elasticsearch.action.admin.indices.settings.get.GetSettingsAction; +import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest; +import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsAction; +import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest; +import org.elasticsearch.action.admin.indices.shards.IndicesShardStoresAction; +import org.elasticsearch.action.admin.indices.shards.IndicesShardStoresRequest; +import org.elasticsearch.action.admin.indices.stats.IndicesStatsAction; +import org.elasticsearch.action.admin.indices.stats.IndicesStatsRequest; +import org.elasticsearch.action.admin.indices.upgrade.get.UpgradeStatusAction; +import org.elasticsearch.action.admin.indices.upgrade.get.UpgradeStatusRequest; import org.elasticsearch.action.delete.DeleteAction; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.get.GetAction; @@ -495,6 +509,8 @@ public class InternalAuthorizationServiceTests extends ESTestCase { requests.add(new Tuple<>(TermVectorsAction.NAME, new TermVectorsRequest(ShieldTemplateService.SECURITY_INDEX_NAME, "type", "id"))); requests.add(new Tuple<>(IndicesAliasesAction.NAME, new IndicesAliasesRequest().addAlias("shield_alias", ShieldTemplateService.SECURITY_INDEX_NAME))); + requests.add( + new Tuple<>(UpdateSettingsAction.NAME, new UpdateSettingsRequest().indices(ShieldTemplateService.SECURITY_INDEX_NAME))); for (Tuple requestTuple : requests) { String action = requestTuple.v1(); @@ -520,6 +536,38 @@ public class InternalAuthorizationServiceTests extends ESTestCase { verify(auditTrail).accessGranted(user, ClusterHealthAction.NAME, request); } + public void testGrantedNonXPackUserCanExecuteMonitoringOperationsAgainstSecurityIndex() { + User user = new User("all_access_user", "all_access"); + when(rolesStore.role("all_access")).thenReturn(Role.builder("all_access") + .add(IndexPrivilege.ALL, "*") + .cluster(ClusterPrivilege.ALL) + .build()); + ClusterState state = mock(ClusterState.class); + when(clusterService.state()).thenReturn(state); + when(state.metaData()).thenReturn(MetaData.builder() + .put(new IndexMetaData.Builder(ShieldTemplateService.SECURITY_INDEX_NAME) + .settings(Settings.builder().put("index.version.created", Version.CURRENT).build()) + .numberOfShards(1).numberOfReplicas(0).build(), true) + .build()); + + List> requests = new ArrayList<>(); + requests.add(new Tuple<>(IndicesStatsAction.NAME, new IndicesStatsRequest().indices(ShieldTemplateService.SECURITY_INDEX_NAME))); + requests.add(new Tuple<>(RecoveryAction.NAME, new RecoveryRequest().indices(ShieldTemplateService.SECURITY_INDEX_NAME))); + requests.add(new Tuple<>(IndicesSegmentsAction.NAME, + new IndicesSegmentsRequest().indices(ShieldTemplateService.SECURITY_INDEX_NAME))); + requests.add(new Tuple<>(GetSettingsAction.NAME, new GetSettingsRequest().indices(ShieldTemplateService.SECURITY_INDEX_NAME))); + requests.add(new Tuple<>(IndicesShardStoresAction.NAME, + new IndicesShardStoresRequest().indices(ShieldTemplateService.SECURITY_INDEX_NAME))); + requests.add(new Tuple<>(UpgradeStatusAction.NAME, new UpgradeStatusRequest().indices(ShieldTemplateService.SECURITY_INDEX_NAME))); + + for (Tuple requestTuple : requests) { + String action = requestTuple.v1(); + TransportRequest request = requestTuple.v2(); + internalAuthorizationService.authorize(user, action, request); + verify(auditTrail).accessGranted(user, action, request); + } + } + public void testXPackUserCanExecuteOperationAgainstShieldIndex() { ClusterState state = mock(ClusterState.class); when(clusterService.state()).thenReturn(state);