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:
jaymode 2016-04-02 17:42:16 -04:00
parent 3c65f38fbe
commit 9031cee432
3 changed files with 76 additions and 2 deletions

View File

@ -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);

View File

@ -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));
}
}

View File

@ -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);