Make .async-search-* a restricted namespace (#50294)

Hide the `.async-search-*` in Security by making it a restricted index namespace.
The namespace is hard-coded.
To grant privileges on restricted indices, one must explicitly toggle the
`allow_restricted_indices` flag in the indices permission in the role definition.
As is the case with any other index, if a certain user lacks all permissions for an
index, that index is effectively nonexistent for that user.
This commit is contained in:
Albert Zaharovits 2020-01-13 12:20:54 +02:00 committed by GitHub
parent c31a21c3d8
commit 2b789fa3e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 218 additions and 13 deletions

View File

@ -119,17 +119,13 @@ public final class FieldExpression implements RoleMapperExpression {
private static CharacterRunAutomaton buildAutomaton(Object value) { private static CharacterRunAutomaton buildAutomaton(Object value) {
if (value instanceof String) { if (value instanceof String) {
final String str = (String) value; final String str = (String) value;
if (Regex.isSimpleMatchPattern(str) || isLuceneRegex(str)) { if (Regex.isSimpleMatchPattern(str) || Automatons.isLuceneRegex(str)) {
return new CharacterRunAutomaton(Automatons.patterns(str)); return new CharacterRunAutomaton(Automatons.patterns(str));
} }
} }
return null; return null;
} }
private static boolean isLuceneRegex(String str) {
return str.length() > 1 && str.charAt(0) == '/' && str.charAt(str.length() - 1) == '/';
}
public Object getValue() { public Object getValue() {
return value; return value;
} }

View File

@ -15,6 +15,7 @@ import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl; import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl;
import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege; import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames;
@ -139,7 +140,7 @@ public final class IndicesPermission {
final Map<IndicesPermission.Group, Automaton> predicateCache = new HashMap<>(); final Map<IndicesPermission.Group, Automaton> predicateCache = new HashMap<>();
for (String forIndexPattern : checkForIndexPatterns) { for (String forIndexPattern : checkForIndexPatterns) {
Automaton checkIndexAutomaton = Automatons.patterns(forIndexPattern); Automaton checkIndexAutomaton = Automatons.patterns(forIndexPattern);
if (false == allowRestrictedIndices && false == RestrictedIndicesNames.RESTRICTED_NAMES.contains(forIndexPattern)) { if (false == allowRestrictedIndices && false == isConcreteRestrictedIndex(forIndexPattern)) {
checkIndexAutomaton = Automatons.minusAndMinimize(checkIndexAutomaton, RestrictedIndicesNames.NAMES_AUTOMATON); checkIndexAutomaton = Automatons.minusAndMinimize(checkIndexAutomaton, RestrictedIndicesNames.NAMES_AUTOMATON);
} }
if (false == Operations.isEmpty(checkIndexAutomaton)) { if (false == Operations.isEmpty(checkIndexAutomaton)) {
@ -268,6 +269,13 @@ public final class IndicesPermission {
return unmodifiableMap(indexPermissions); return unmodifiableMap(indexPermissions);
} }
private boolean isConcreteRestrictedIndex(String indexPattern) {
if (Regex.isSimpleMatchPattern(indexPattern) || Automatons.isLuceneRegex(indexPattern)) {
return false;
}
return RestrictedIndicesNames.isRestricted(indexPattern);
}
public static class Group { public static class Group {
private final IndexPrivilege privilege; private final IndexPrivilege privilege;
private final Predicate<String> actionMatcher; private final Predicate<String> actionMatcher;
@ -316,7 +324,7 @@ public final class IndicesPermission {
private boolean check(String action, String index) { private boolean check(String action, String index) {
assert index != null; assert index != null;
return check(action) && indexNameMatcher.test(index) return check(action) && indexNameMatcher.test(index)
&& (allowRestrictedIndices || (false == RestrictedIndicesNames.RESTRICTED_NAMES.contains(index))); && (allowRestrictedIndices || (false == RestrictedIndicesNames.isRestricted(index)));
} }
boolean hasQuery() { boolean hasQuery() {
@ -351,13 +359,13 @@ public final class IndicesPermission {
final Predicate<String> predicate; final Predicate<String> predicate;
if (restrictedIndices.isEmpty()) { if (restrictedIndices.isEmpty()) {
predicate = indexMatcher(ordinaryIndices) predicate = indexMatcher(ordinaryIndices)
.and(index -> false == RestrictedIndicesNames.RESTRICTED_NAMES.contains(index)); .and(index -> false == RestrictedIndicesNames.isRestricted(index));
} else if (ordinaryIndices.isEmpty()) { } else if (ordinaryIndices.isEmpty()) {
predicate = indexMatcher(restrictedIndices); predicate = indexMatcher(restrictedIndices);
} else { } else {
predicate = indexMatcher(restrictedIndices) predicate = indexMatcher(restrictedIndices)
.or(indexMatcher(ordinaryIndices) .or(indexMatcher(ordinaryIndices)
.and(index -> false == RestrictedIndicesNames.RESTRICTED_NAMES.contains(index))); .and(index -> false == RestrictedIndicesNames.isRestricted(index)));
} }
return predicate; return predicate;
} }

View File

@ -10,6 +10,7 @@ import org.apache.lucene.util.automaton.Automaton;
import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.xpack.core.security.support.Automatons; import org.elasticsearch.xpack.core.security.support.Automatons;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Set; import java.util.Set;
@ -21,10 +22,20 @@ public final class RestrictedIndicesNames {
public static final String INTERNAL_SECURITY_TOKENS_INDEX_7 = ".security-tokens-7"; public static final String INTERNAL_SECURITY_TOKENS_INDEX_7 = ".security-tokens-7";
public static final String SECURITY_TOKENS_ALIAS = ".security-tokens"; public static final String SECURITY_TOKENS_ALIAS = ".security-tokens";
// public for tests
public static final String ASYNC_SEARCH_PREFIX = ".async-search-";
private static final Automaton ASYNC_SEARCH_AUTOMATON = Automatons.patterns(ASYNC_SEARCH_PREFIX + "*");
// public for tests
public static final Set<String> RESTRICTED_NAMES = Collections.unmodifiableSet(Sets.newHashSet(SECURITY_MAIN_ALIAS, public static final Set<String> RESTRICTED_NAMES = Collections.unmodifiableSet(Sets.newHashSet(SECURITY_MAIN_ALIAS,
INTERNAL_SECURITY_MAIN_INDEX_6, INTERNAL_SECURITY_MAIN_INDEX_7, INTERNAL_SECURITY_TOKENS_INDEX_7, SECURITY_TOKENS_ALIAS)); INTERNAL_SECURITY_MAIN_INDEX_6, INTERNAL_SECURITY_MAIN_INDEX_7, INTERNAL_SECURITY_TOKENS_INDEX_7, SECURITY_TOKENS_ALIAS));
public static final Automaton NAMES_AUTOMATON = Automatons.patterns(RESTRICTED_NAMES); public static boolean isRestricted(String concreteIndexName) {
return RESTRICTED_NAMES.contains(concreteIndexName) || concreteIndexName.startsWith(ASYNC_SEARCH_PREFIX);
}
public static final Automaton NAMES_AUTOMATON = Automatons.unionAndMinimize(Arrays.asList(Automatons.patterns(RESTRICTED_NAMES),
ASYNC_SEARCH_AUTOMATON));
private RestrictedIndicesNames() { private RestrictedIndicesNames() {
} }

View File

@ -107,6 +107,13 @@ public final class Automatons {
} }
} }
/**
* Is the str a lucene type of pattern
*/
public static boolean isLuceneRegex(String str) {
return str.length() > 1 && str.charAt(0) == '/' && str.charAt(str.length() - 1) == '/';
}
private static Automaton buildAutomaton(String pattern) { private static Automaton buildAutomaton(String pattern) {
if (pattern.startsWith("/")) { // it's a lucene regexp if (pattern.startsWith("/")) { // it's a lucene regexp
if (pattern.length() == 1 || !pattern.endsWith("/")) { if (pattern.length() == 1 || !pattern.endsWith("/")) {

View File

@ -249,8 +249,11 @@ public class ReservedRolesStoreTests extends ESTestCase {
// but that depends on how users are supposed to perform snapshots of those new indices. // but that depends on how users are supposed to perform snapshots of those new indices.
assertThat(snapshotUserRole.indices().allowedIndicesMatcher(GetIndexAction.NAME).test(index), is(true)); assertThat(snapshotUserRole.indices().allowedIndicesMatcher(GetIndexAction.NAME).test(index), is(true));
} }
assertThat(snapshotUserRole.indices().allowedIndicesMatcher(GetIndexAction.NAME).test(
RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(true));
assertNoAccessAllowed(snapshotUserRole, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(snapshotUserRole, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(snapshotUserRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
} }
public void testIngestAdminRole() { public void testIngestAdminRole() {
@ -280,6 +283,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
is(false)); is(false));
assertNoAccessAllowed(ingestAdminRole, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(ingestAdminRole, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(ingestAdminRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
} }
public void testKibanaSystemRole() { public void testKibanaSystemRole() {
@ -390,6 +394,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
assertThat(kibanaRole.indices().allowedIndicesMatcher(READ_CROSS_CLUSTER_NAME).test(index), is(false)); assertThat(kibanaRole.indices().allowedIndicesMatcher(READ_CROSS_CLUSTER_NAME).test(index), is(false));
assertNoAccessAllowed(kibanaRole, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(kibanaRole, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(kibanaRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
} }
public void testKibanaUserRole() { public void testKibanaUserRole() {
@ -429,6 +434,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
"*"), is(false)); "*"), is(false));
assertNoAccessAllowed(kibanaUserRole, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(kibanaUserRole, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(kibanaUserRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
} }
public void testMonitoringUserRole() { public void testMonitoringUserRole() {
@ -476,6 +482,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
assertThat(monitoringUserRole.indices().allowedIndicesMatcher(READ_CROSS_CLUSTER_NAME).test(index), is(true)); assertThat(monitoringUserRole.indices().allowedIndicesMatcher(READ_CROSS_CLUSTER_NAME).test(index), is(true));
assertNoAccessAllowed(monitoringUserRole, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(monitoringUserRole, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(monitoringUserRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana");
assertThat(monitoringUserRole.application().grants( assertThat(monitoringUserRole.application().grants(
@ -550,6 +557,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(GetAction.NAME).test(metricbeatIndex), is(false)); assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(GetAction.NAME).test(metricbeatIndex), is(false));
assertNoAccessAllowed(remoteMonitoringAgentRole, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(remoteMonitoringAgentRole, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(remoteMonitoringAgentRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
} }
public void testRemoteMonitoringCollectorRole() { public void testRemoteMonitoringCollectorRole() {
@ -603,29 +611,50 @@ public class ReservedRolesStoreTests extends ESTestCase {
// (but ideally, the monitoring user should see all indices). // (but ideally, the monitoring user should see all indices).
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(GetSettingsAction.NAME) assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(GetSettingsAction.NAME)
.test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(true)); .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(GetSettingsAction.NAME)
.test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndicesShardStoresAction.NAME) assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndicesShardStoresAction.NAME)
.test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(true)); .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndicesShardStoresAction.NAME)
.test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(UpgradeStatusAction.NAME) assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(UpgradeStatusAction.NAME)
.test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(true)); .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(UpgradeStatusAction.NAME)
.test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(RecoveryAction.NAME) assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(RecoveryAction.NAME)
.test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(true)); .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(RecoveryAction.NAME)
.test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndicesStatsAction.NAME) assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndicesStatsAction.NAME)
.test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(true)); .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndicesStatsAction.NAME)
.test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndicesSegmentsAction.NAME) assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndicesSegmentsAction.NAME)
.test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(true)); .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndicesSegmentsAction.NAME)
.test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(SearchAction.NAME) assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(SearchAction.NAME)
.test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(false)); .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(false));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(SearchAction.NAME)
.test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(false));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(GetAction.NAME) assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(GetAction.NAME)
.test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(false)); .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(false));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(GetAction.NAME)
.test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(false));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(DeleteAction.NAME) assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(DeleteAction.NAME)
.test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(false)); .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(false));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(DeleteAction.NAME)
.test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(false));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndexAction.NAME) assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndexAction.NAME)
.test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(false)); .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(false));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndexAction.NAME)
.test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(false));
assertMonitoringOnRestrictedIndices(remoteMonitoringAgentRole); assertMonitoringOnRestrictedIndices(remoteMonitoringAgentRole);
assertNoAccessAllowed(remoteMonitoringAgentRole, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(remoteMonitoringAgentRole, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(remoteMonitoringAgentRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
} }
private void assertMonitoringOnRestrictedIndices(Role role) { private void assertMonitoringOnRestrictedIndices(Role role) {
@ -644,11 +673,13 @@ public class ReservedRolesStoreTests extends ESTestCase {
final List<String> indexMonitoringActionNamesList = Arrays.asList(IndicesStatsAction.NAME, IndicesSegmentsAction.NAME, final List<String> indexMonitoringActionNamesList = Arrays.asList(IndicesStatsAction.NAME, IndicesSegmentsAction.NAME,
GetSettingsAction.NAME, IndicesShardStoresAction.NAME, UpgradeStatusAction.NAME, RecoveryAction.NAME); GetSettingsAction.NAME, IndicesShardStoresAction.NAME, UpgradeStatusAction.NAME, RecoveryAction.NAME);
for (final String indexMonitoringActionName : indexMonitoringActionNamesList) { for (final String indexMonitoringActionName : indexMonitoringActionNamesList) {
String asyncSearchIndex = RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2);
final Map<String, IndexAccessControl> authzMap = role.indices().authorize(indexMonitoringActionName, final Map<String, IndexAccessControl> authzMap = role.indices().authorize(indexMonitoringActionName,
Sets.newHashSet(internalSecurityIndex, RestrictedIndicesNames.SECURITY_MAIN_ALIAS), Sets.newHashSet(internalSecurityIndex, RestrictedIndicesNames.SECURITY_MAIN_ALIAS, asyncSearchIndex),
metaData.getAliasAndIndexLookup(), fieldPermissionsCache); metaData.getAliasAndIndexLookup(), fieldPermissionsCache);
assertThat(authzMap.get(internalSecurityIndex).isGranted(), is(true)); assertThat(authzMap.get(internalSecurityIndex).isGranted(), is(true));
assertThat(authzMap.get(RestrictedIndicesNames.SECURITY_MAIN_ALIAS).isGranted(), is(true)); assertThat(authzMap.get(RestrictedIndicesNames.SECURITY_MAIN_ALIAS).isGranted(), is(true));
assertThat(authzMap.get(asyncSearchIndex).isGranted(), is(true));
} }
} }
@ -692,6 +723,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
assertThat(reportingUserRole.indices().allowedIndicesMatcher(BulkAction.NAME).test(index), is(false)); assertThat(reportingUserRole.indices().allowedIndicesMatcher(BulkAction.NAME).test(index), is(false));
assertNoAccessAllowed(reportingUserRole, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(reportingUserRole, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(reportingUserRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
} }
public void testKibanaDashboardOnlyUserRole() { public void testKibanaDashboardOnlyUserRole() {
@ -728,6 +760,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
new ApplicationPrivilege(applicationWithRandomIndex, "app-random-index", "all"), "*"), is(false)); new ApplicationPrivilege(applicationWithRandomIndex, "app-random-index", "all"), "*"), is(false));
assertNoAccessAllowed(dashboardsOnlyUserRole, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(dashboardsOnlyUserRole, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(dashboardsOnlyUserRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
} }
public void testSuperuserRole() { public void testSuperuserRole() {
@ -828,6 +861,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
is(false)); is(false));
assertNoAccessAllowed(logstashSystemRole, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(logstashSystemRole, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(logstashSystemRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
} }
public void testBeatsAdminRole() { public void testBeatsAdminRole() {
@ -868,6 +902,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
assertThat(beatsAdminRole.indices().allowedIndicesMatcher(GetAction.NAME).test(index), is(true)); assertThat(beatsAdminRole.indices().allowedIndicesMatcher(GetAction.NAME).test(index), is(true));
assertNoAccessAllowed(beatsAdminRole, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(beatsAdminRole, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(beatsAdminRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
} }
public void testBeatsSystemRole() { public void testBeatsSystemRole() {
@ -903,6 +938,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
assertThat(beatsSystemRole.indices().allowedIndicesMatcher(BulkAction.NAME).test(index), is(true)); assertThat(beatsSystemRole.indices().allowedIndicesMatcher(BulkAction.NAME).test(index), is(true));
assertNoAccessAllowed(beatsSystemRole, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(beatsSystemRole, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(beatsSystemRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
} }
public void testAPMSystemRole() { public void testAPMSystemRole() {
@ -943,7 +979,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
"indices:data/write/index:op_type/" + randomAlphaOfLengthBetween(3,5)).test(index), is(false)); "indices:data/write/index:op_type/" + randomAlphaOfLengthBetween(3,5)).test(index), is(false));
assertNoAccessAllowed(APMSystemRole, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(APMSystemRole, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(APMSystemRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
} }
public void testAPMUserRole() { public void testAPMUserRole() {
@ -1037,6 +1073,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
assertReadWriteDocsButNotDeleteIndexAllowed(role, AnnotationIndex.INDEX_NAME); assertReadWriteDocsButNotDeleteIndexAllowed(role, AnnotationIndex.INDEX_NAME);
assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana");
assertThat(role.application().grants( assertThat(role.application().grants(
@ -1123,6 +1160,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
assertReadWriteDocsButNotDeleteIndexAllowed(role, AnnotationIndex.INDEX_NAME); assertReadWriteDocsButNotDeleteIndexAllowed(role, AnnotationIndex.INDEX_NAME);
assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana");
@ -1170,6 +1208,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
assertNoAccessAllowed(role, TransformInternalIndexConstants.LATEST_INDEX_NAME); // internal use only assertNoAccessAllowed(role, TransformInternalIndexConstants.LATEST_INDEX_NAME); // internal use only
assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana");
assertThat(role.application().grants( assertThat(role.application().grants(
@ -1222,6 +1261,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
assertNoAccessAllowed(role, TransformInternalIndexConstants.LATEST_INDEX_NAME); assertNoAccessAllowed(role, TransformInternalIndexConstants.LATEST_INDEX_NAME);
assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana");
assertThat(role.application().grants( assertThat(role.application().grants(
@ -1272,6 +1312,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
} }
assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
} }
public void testWatcherUserRole() { public void testWatcherUserRole() {
@ -1305,6 +1346,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
} }
assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
} }
private void assertReadWriteDocsButNotDeleteIndexAllowed(Role role, String index) { private void assertReadWriteDocsButNotDeleteIndexAllowed(Role role, String index) {
@ -1329,6 +1371,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
assertThat(role.indices().allowedIndicesMatcher(BulkAction.NAME).test(index), is(false)); assertThat(role.indices().allowedIndicesMatcher(BulkAction.NAME).test(index), is(false));
assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
} }
private void assertNoAccessAllowed(Role role, Collection<String> indices) { private void assertNoAccessAllowed(Role role, Collection<String> indices) {

View File

@ -545,6 +545,117 @@ public class RBACEngineTests extends ESTestCase {
)); ));
} }
public void testCheckRestrictedIndexPatternPermission() throws Exception {
User user = new User(randomAlphaOfLengthBetween(4, 12));
Authentication authentication = mock(Authentication.class);
when(authentication.getUser()).thenReturn(user);
final String patternPrefix = RestrictedIndicesNames.ASYNC_SEARCH_PREFIX.substring(0,
randomIntBetween(1, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX.length() - 2));
Role role = Role.builder("role")
.add(FieldPermissions.DEFAULT, null, IndexPrivilege.INDEX, false, patternPrefix + "*")
.build();
RBACAuthorizationInfo authzInfo = new RBACAuthorizationInfo(role, null);
String prePatternPrefix = patternPrefix.substring(0, randomIntBetween(1, patternPrefix.length() - 1)) + "*";
HasPrivilegesResponse response = hasPrivileges(RoleDescriptor.IndicesPrivileges.builder()
.indices(prePatternPrefix)
.allowRestrictedIndices(randomBoolean())
.privileges("index")
.build(), authentication, authzInfo, Collections.emptyList(), Strings.EMPTY_ARRAY);
assertThat(response.isCompleteMatch(), is(false));
assertThat(response.getIndexPrivileges(), Matchers.iterableWithSize(1));
assertThat(response.getIndexPrivileges(), containsInAnyOrder(
ResourcePrivileges.builder(prePatternPrefix)
.addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap<String, Boolean>())
.put("index", false).map()).build()));
String matchesPatternPrefix = RestrictedIndicesNames.ASYNC_SEARCH_PREFIX.substring(0, patternPrefix.length() + 1);
response = hasPrivileges(RoleDescriptor.IndicesPrivileges.builder()
.indices(matchesPatternPrefix + "*")
.allowRestrictedIndices(false)
.privileges("index")
.build(), authentication, authzInfo, Collections.emptyList(), Strings.EMPTY_ARRAY);
assertThat(response.isCompleteMatch(), is(true));
assertThat(response.getIndexPrivileges(), Matchers.iterableWithSize(1));
assertThat(response.getIndexPrivileges(), containsInAnyOrder(
ResourcePrivileges.builder(matchesPatternPrefix + "*")
.addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap<String, Boolean>())
.put("index", true).map()).build()));
response = hasPrivileges(RoleDescriptor.IndicesPrivileges.builder()
.indices(matchesPatternPrefix + "*")
.allowRestrictedIndices(true)
.privileges("index")
.build(), authentication, authzInfo, Collections.emptyList(), Strings.EMPTY_ARRAY);
assertThat(response.isCompleteMatch(), is(false));
assertThat(response.getIndexPrivileges(), Matchers.iterableWithSize(1));
assertThat(response.getIndexPrivileges(), containsInAnyOrder(
ResourcePrivileges.builder(matchesPatternPrefix + "*")
.addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap<String, Boolean>())
.put("index", false).map()).build()));
response = hasPrivileges(RoleDescriptor.IndicesPrivileges.builder()
.indices(matchesPatternPrefix)
.allowRestrictedIndices(randomBoolean())
.privileges("index")
.build(), authentication, authzInfo, Collections.emptyList(), Strings.EMPTY_ARRAY);
assertThat(response.isCompleteMatch(), is(true));
assertThat(response.getIndexPrivileges(), Matchers.iterableWithSize(1));
assertThat(response.getIndexPrivileges(), containsInAnyOrder(
ResourcePrivileges.builder(matchesPatternPrefix)
.addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap<String, Boolean>())
.put("index", true).map()).build()));
final String restrictedIndexMatchingWildcard = RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2);
response = hasPrivileges(RoleDescriptor.IndicesPrivileges.builder()
.indices(restrictedIndexMatchingWildcard + "*")
.allowRestrictedIndices(true)
.privileges("index")
.build(), authentication, authzInfo, Collections.emptyList(), Strings.EMPTY_ARRAY);
assertThat(response.isCompleteMatch(), is(false));
assertThat(response.getIndexPrivileges(), Matchers.iterableWithSize(1));
assertThat(response.getIndexPrivileges(), containsInAnyOrder(
ResourcePrivileges.builder(restrictedIndexMatchingWildcard + "*")
.addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap<String, Boolean>())
.put("index", false).map()).build()));
response = hasPrivileges(RoleDescriptor.IndicesPrivileges.builder()
.indices(restrictedIndexMatchingWildcard + "*")
.allowRestrictedIndices(false)
.privileges("index")
.build(), authentication, authzInfo, Collections.emptyList(), Strings.EMPTY_ARRAY);
assertThat(response.isCompleteMatch(), is(false));
assertThat(response.getIndexPrivileges(), Matchers.iterableWithSize(1));
assertThat(response.getIndexPrivileges(), containsInAnyOrder(
ResourcePrivileges.builder(restrictedIndexMatchingWildcard + "*")
.addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap<String, Boolean>())
.put("index", false).map()).build()));
response = hasPrivileges(RoleDescriptor.IndicesPrivileges.builder()
.indices(restrictedIndexMatchingWildcard)
.allowRestrictedIndices(randomBoolean())
.privileges("index")
.build(), authentication, authzInfo, Collections.emptyList(), Strings.EMPTY_ARRAY);
assertThat(response.isCompleteMatch(), is(false));
assertThat(response.getIndexPrivileges(), Matchers.iterableWithSize(1));
assertThat(response.getIndexPrivileges(), containsInAnyOrder(
ResourcePrivileges.builder(restrictedIndexMatchingWildcard)
.addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap<String, Boolean>())
.put("index", false).map()).build()));
role = Role.builder("role")
.add(FieldPermissions.DEFAULT, null, IndexPrivilege.INDEX, true, patternPrefix + "*")
.build();
authzInfo = new RBACAuthorizationInfo(role, null);
response = hasPrivileges(RoleDescriptor.IndicesPrivileges.builder()
.indices(matchesPatternPrefix + "*")
.allowRestrictedIndices(randomBoolean())
.privileges("index")
.build(), authentication, authzInfo, Collections.emptyList(), Strings.EMPTY_ARRAY);
assertThat(response.isCompleteMatch(), is(true));
assertThat(response.getIndexPrivileges(), Matchers.iterableWithSize(1));
assertThat(response.getIndexPrivileges(), containsInAnyOrder(
ResourcePrivileges.builder(matchesPatternPrefix + "*")
.addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap<String, Boolean>())
.put("index", true).map()).build()));
}
public void testCheckExplicitRestrictedIndexPermissions() throws Exception { public void testCheckExplicitRestrictedIndexPermissions() throws Exception {
User user = new User(randomAlphaOfLengthBetween(4, 12)); User user = new User(randomAlphaOfLengthBetween(4, 12));
Authentication authentication = mock(Authentication.class); Authentication authentication = mock(Authentication.class);

View File

@ -323,6 +323,32 @@ public class IndicesPermissionTests extends ESTestCase {
assertThat(authzMap.get(RestrictedIndicesNames.SECURITY_MAIN_ALIAS).isGranted(), is(true)); assertThat(authzMap.get(RestrictedIndicesNames.SECURITY_MAIN_ALIAS).isGranted(), is(true));
} }
public void testAsyncSearchIndicesPermissions() {
final Settings indexSettings = Settings.builder().put("index.version.created", Version.CURRENT).build();
final String asyncSearchIndex = RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2);
final MetaData metaData = new MetaData.Builder()
.put(new IndexMetaData.Builder(asyncSearchIndex)
.settings(indexSettings)
.numberOfShards(1)
.numberOfReplicas(0)
.build(), true)
.build();
FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY);
SortedMap<String, AliasOrIndex> lookup = metaData.getAliasAndIndexLookup();
// allow_restricted_indices: false
IndicesPermission.Group group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, false, "*");
Map<String, IndicesAccessControl.IndexAccessControl> authzMap = new IndicesPermission(group).authorize(SearchAction.NAME,
Sets.newHashSet(asyncSearchIndex), lookup, fieldPermissionsCache);
assertThat(authzMap.get(asyncSearchIndex).isGranted(), is(false));
// allow_restricted_indices: true
group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, true, "*");
authzMap = new IndicesPermission(group).authorize(SearchAction.NAME,
Sets.newHashSet(asyncSearchIndex), lookup, fieldPermissionsCache);
assertThat(authzMap.get(asyncSearchIndex).isGranted(), is(true));
}
private static FieldPermissionsDefinition fieldPermissionDef(String[] granted, String[] denied) { private static FieldPermissionsDefinition fieldPermissionDef(String[] granted, String[] denied) {
return new FieldPermissionsDefinition(granted, denied); return new FieldPermissionsDefinition(granted, denied);
} }

View File

@ -34,6 +34,7 @@ public class XPackUserTests extends ESTestCase {
for (String index : RestrictedIndicesNames.RESTRICTED_NAMES) { for (String index : RestrictedIndicesNames.RESTRICTED_NAMES) {
assertThat(predicate.test(index), Matchers.is(false)); assertThat(predicate.test(index), Matchers.is(false));
} }
assertThat(predicate.test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), Matchers.is(false));
} }
public void testXPackUserCanReadAuditTrail() { public void testXPackUserCanReadAuditTrail() {

View File

@ -41,6 +41,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
@ -650,7 +651,8 @@ public abstract class SqlSecurityTestCase extends ESRestTestCase {
* SQL drops them from the interface. So we might have access to them, but we * SQL drops them from the interface. So we might have access to them, but we
* don't show them. * don't show them.
*/ */
indices.removeAll(RestrictedIndicesNames.RESTRICTED_NAMES); indices = indices.stream().filter(
idx -> false == RestrictedIndicesNames.isRestricted(idx)).collect(Collectors.toList());
} }
} }
// Use a sorted list for indices for consistent error reporting // Use a sorted list for indices for consistent error reporting