security: allow indices monitor actions to access the security index
This commit allows authorized users to monitor the security index. This fixes an issue with the _cat/indices api, which resolves the concrete indices using the cluster state and then makes a indices stats request. Without this change, the api fails with an authorization exception because it is specifically requesting the security index and the user is not the internal user. Closes elastic/elasticsearch#1895 Original commit: elastic/x-pack-elasticsearch@070a389833
This commit is contained in:
parent
3c65f38fbe
commit
9031cee432
|
@ -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<String> 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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String, TransportRequest> 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<Tuple<String, ? extends TransportRequest>> 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<String, ? extends TransportRequest> 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);
|
||||
|
|
Loading…
Reference in New Issue