Add ILM-specific security privileges (#36493)

* add read_ilm cluster privilege

Although managing ILM policies is best done using the
"manage" cluster privilege, it is useful to have read-only
views.

* adds `read_ilm` cluster privilege for viewing policies and status
* adds Explain API to the `view_index_metadata` index privilege

* add manage_ilm privileges
This commit is contained in:
Tal Levy 2018-12-13 08:11:33 -08:00 committed by GitHub
parent f998e04c34
commit e3cf642299
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 83 additions and 4 deletions

View File

@ -315,9 +315,11 @@ public final class Role {
public static final String MANAGE_PIPELINE = "manage_pipeline"; public static final String MANAGE_PIPELINE = "manage_pipeline";
public static final String MANAGE_CCR = "manage_ccr"; public static final String MANAGE_CCR = "manage_ccr";
public static final String READ_CCR = "read_ccr"; public static final String READ_CCR = "read_ccr";
public static final String MANAGE_ILM = "manage_ilm";
public static final String READ_ILM = "read_ilm";
public static final String[] ALL_ARRAY = new String[] { NONE, ALL, MONITOR, MONITOR_ML, MONITOR_WATCHER, MONITOR_ROLLUP, MANAGE, public static final String[] ALL_ARRAY = new String[] { NONE, ALL, MONITOR, MONITOR_ML, MONITOR_WATCHER, MONITOR_ROLLUP, MANAGE,
MANAGE_ML, MANAGE_WATCHER, MANAGE_ROLLUP, MANAGE_INDEX_TEMPLATES, MANAGE_INGEST_PIPELINES, TRANSPORT_CLIENT, MANAGE_ML, MANAGE_WATCHER, MANAGE_ROLLUP, MANAGE_INDEX_TEMPLATES, MANAGE_INGEST_PIPELINES, TRANSPORT_CLIENT,
MANAGE_SECURITY, MANAGE_SAML, MANAGE_TOKEN, MANAGE_PIPELINE, MANAGE_CCR, READ_CCR }; MANAGE_SECURITY, MANAGE_SAML, MANAGE_TOKEN, MANAGE_PIPELINE, MANAGE_CCR, READ_CCR, MANAGE_ILM, READ_ILM };
} }
/** /**
@ -338,8 +340,9 @@ public final class Role {
public static final String CREATE_INDEX = "create_index"; public static final String CREATE_INDEX = "create_index";
public static final String VIEW_INDEX_METADATA = "view_index_metadata"; public static final String VIEW_INDEX_METADATA = "view_index_metadata";
public static final String MANAGE_FOLLOW_INDEX = "manage_follow_index"; public static final String MANAGE_FOLLOW_INDEX = "manage_follow_index";
public static final String MANAGE_ILM = "manage_ilm";
public static final String[] ALL_ARRAY = new String[] { NONE, ALL, READ, READ_CROSS, CREATE, INDEX, DELETE, WRITE, MONITOR, MANAGE, public static final String[] ALL_ARRAY = new String[] { NONE, ALL, READ, READ_CROSS, CREATE, INDEX, DELETE, WRITE, MONITOR, MANAGE,
DELETE_INDEX, CREATE_INDEX, VIEW_INDEX_METADATA, MANAGE_FOLLOW_INDEX }; DELETE_INDEX, CREATE_INDEX, VIEW_INDEX_METADATA, MANAGE_FOLLOW_INDEX, MANAGE_ILM };
} }
} }

View File

@ -9,6 +9,8 @@ import org.apache.lucene.util.automaton.Automaton;
import org.elasticsearch.action.admin.cluster.state.ClusterStateAction; import org.elasticsearch.action.admin.cluster.state.ClusterStateAction;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.xpack.core.indexlifecycle.action.GetLifecycleAction;
import org.elasticsearch.xpack.core.indexlifecycle.action.GetStatusAction;
import org.elasticsearch.xpack.core.security.action.token.InvalidateTokenAction; import org.elasticsearch.xpack.core.security.action.token.InvalidateTokenAction;
import org.elasticsearch.xpack.core.security.action.token.RefreshTokenAction; import org.elasticsearch.xpack.core.security.action.token.RefreshTokenAction;
import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesAction; import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesAction;
@ -47,6 +49,8 @@ public final class ClusterPrivilege extends Privilege {
private static final Automaton MANAGE_CCR_AUTOMATON = private static final Automaton MANAGE_CCR_AUTOMATON =
patterns("cluster:admin/xpack/ccr/*", ClusterStateAction.NAME, HasPrivilegesAction.NAME); patterns("cluster:admin/xpack/ccr/*", ClusterStateAction.NAME, HasPrivilegesAction.NAME);
private static final Automaton READ_CCR_AUTOMATON = patterns(ClusterStateAction.NAME, HasPrivilegesAction.NAME); private static final Automaton READ_CCR_AUTOMATON = patterns(ClusterStateAction.NAME, HasPrivilegesAction.NAME);
private static final Automaton MANAGE_ILM_AUTOMATON = patterns("cluster:admin/ilm/*");
private static final Automaton READ_ILM_AUTOMATON = patterns(GetLifecycleAction.NAME, GetStatusAction.NAME);
public static final ClusterPrivilege NONE = new ClusterPrivilege("none", Automatons.EMPTY); public static final ClusterPrivilege NONE = new ClusterPrivilege("none", Automatons.EMPTY);
public static final ClusterPrivilege ALL = new ClusterPrivilege("all", ALL_CLUSTER_AUTOMATON); public static final ClusterPrivilege ALL = new ClusterPrivilege("all", ALL_CLUSTER_AUTOMATON);
@ -69,6 +73,8 @@ public final class ClusterPrivilege extends Privilege {
public static final ClusterPrivilege MANAGE_PIPELINE = new ClusterPrivilege("manage_pipeline", "cluster:admin/ingest/pipeline/*"); public static final ClusterPrivilege MANAGE_PIPELINE = new ClusterPrivilege("manage_pipeline", "cluster:admin/ingest/pipeline/*");
public static final ClusterPrivilege MANAGE_CCR = new ClusterPrivilege("manage_ccr", MANAGE_CCR_AUTOMATON); public static final ClusterPrivilege MANAGE_CCR = new ClusterPrivilege("manage_ccr", MANAGE_CCR_AUTOMATON);
public static final ClusterPrivilege READ_CCR = new ClusterPrivilege("read_ccr", READ_CCR_AUTOMATON); public static final ClusterPrivilege READ_CCR = new ClusterPrivilege("read_ccr", READ_CCR_AUTOMATON);
public static final ClusterPrivilege MANAGE_ILM = new ClusterPrivilege("manage_ilm", MANAGE_ILM_AUTOMATON);
public static final ClusterPrivilege READ_ILM = new ClusterPrivilege("read_ilm", READ_ILM_AUTOMATON);
public static final Predicate<String> ACTION_MATCHER = ClusterPrivilege.ALL.predicate(); public static final Predicate<String> ACTION_MATCHER = ClusterPrivilege.ALL.predicate();
@ -92,6 +98,8 @@ public final class ClusterPrivilege extends Privilege {
.put("manage_rollup", MANAGE_ROLLUP) .put("manage_rollup", MANAGE_ROLLUP)
.put("manage_ccr", MANAGE_CCR) .put("manage_ccr", MANAGE_CCR)
.put("read_ccr", READ_CCR) .put("read_ccr", READ_CCR)
.put("manage_ilm", MANAGE_ILM)
.put("read_ilm", READ_ILM)
.immutableMap(); .immutableMap();
private static final ConcurrentHashMap<Set<String>, ClusterPrivilege> CACHE = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<Set<String>, ClusterPrivilege> CACHE = new ConcurrentHashMap<>();

View File

@ -24,6 +24,7 @@ import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.xpack.core.ccr.action.PutFollowAction; import org.elasticsearch.xpack.core.ccr.action.PutFollowAction;
import org.elasticsearch.xpack.core.ccr.action.UnfollowAction; import org.elasticsearch.xpack.core.ccr.action.UnfollowAction;
import org.elasticsearch.xpack.core.indexlifecycle.action.ExplainLifecycleAction;
import org.elasticsearch.xpack.core.security.support.Automatons; import org.elasticsearch.xpack.core.security.support.Automatons;
import java.util.Arrays; import java.util.Arrays;
@ -57,9 +58,11 @@ public final class IndexPrivilege extends Privilege {
private static final Automaton DELETE_INDEX_AUTOMATON = patterns(DeleteIndexAction.NAME); private static final Automaton DELETE_INDEX_AUTOMATON = patterns(DeleteIndexAction.NAME);
private static final Automaton VIEW_METADATA_AUTOMATON = patterns(GetAliasesAction.NAME, AliasesExistAction.NAME, private static final Automaton VIEW_METADATA_AUTOMATON = patterns(GetAliasesAction.NAME, AliasesExistAction.NAME,
GetIndexAction.NAME, IndicesExistsAction.NAME, GetFieldMappingsAction.NAME + "*", GetMappingsAction.NAME, GetIndexAction.NAME, IndicesExistsAction.NAME, GetFieldMappingsAction.NAME + "*", GetMappingsAction.NAME,
ClusterSearchShardsAction.NAME, TypesExistsAction.NAME, ValidateQueryAction.NAME + "*", GetSettingsAction.NAME); ClusterSearchShardsAction.NAME, TypesExistsAction.NAME, ValidateQueryAction.NAME + "*", GetSettingsAction.NAME,
ExplainLifecycleAction.NAME);
private static final Automaton MANAGE_FOLLOW_INDEX_AUTOMATON = patterns(PutFollowAction.NAME, UnfollowAction.NAME, private static final Automaton MANAGE_FOLLOW_INDEX_AUTOMATON = patterns(PutFollowAction.NAME, UnfollowAction.NAME,
CloseIndexAction.NAME); CloseIndexAction.NAME);
private static final Automaton MANAGE_ILM_AUTOMATON = patterns("indices:admin/ilm/*");
public static final IndexPrivilege NONE = new IndexPrivilege("none", Automatons.EMPTY); public static final IndexPrivilege NONE = new IndexPrivilege("none", Automatons.EMPTY);
public static final IndexPrivilege ALL = new IndexPrivilege("all", ALL_AUTOMATON); public static final IndexPrivilege ALL = new IndexPrivilege("all", ALL_AUTOMATON);
@ -75,6 +78,7 @@ public final class IndexPrivilege extends Privilege {
public static final IndexPrivilege CREATE_INDEX = new IndexPrivilege("create_index", CREATE_INDEX_AUTOMATON); public static final IndexPrivilege CREATE_INDEX = new IndexPrivilege("create_index", CREATE_INDEX_AUTOMATON);
public static final IndexPrivilege VIEW_METADATA = new IndexPrivilege("view_index_metadata", VIEW_METADATA_AUTOMATON); public static final IndexPrivilege VIEW_METADATA = new IndexPrivilege("view_index_metadata", VIEW_METADATA_AUTOMATON);
public static final IndexPrivilege MANAGE_FOLLOW_INDEX = new IndexPrivilege("manage_follow_index", MANAGE_FOLLOW_INDEX_AUTOMATON); public static final IndexPrivilege MANAGE_FOLLOW_INDEX = new IndexPrivilege("manage_follow_index", MANAGE_FOLLOW_INDEX_AUTOMATON);
public static final IndexPrivilege MANAGE_ILM = new IndexPrivilege("manage_ilm", MANAGE_ILM_AUTOMATON);
private static final Map<String, IndexPrivilege> VALUES = MapBuilder.<String, IndexPrivilege>newMapBuilder() private static final Map<String, IndexPrivilege> VALUES = MapBuilder.<String, IndexPrivilege>newMapBuilder()
.put("none", NONE) .put("none", NONE)
@ -91,6 +95,7 @@ public final class IndexPrivilege extends Privilege {
.put("view_index_metadata", VIEW_METADATA) .put("view_index_metadata", VIEW_METADATA)
.put("read_cross_cluster", READ_CROSS_CLUSTER) .put("read_cross_cluster", READ_CROSS_CLUSTER)
.put("manage_follow_index", MANAGE_FOLLOW_INDEX) .put("manage_follow_index", MANAGE_FOLLOW_INDEX)
.put("manage_ilm", MANAGE_ILM)
.immutableMap(); .immutableMap();
public static final Predicate<String> ACTION_MATCHER = ALL.predicate(); public static final Predicate<String> ACTION_MATCHER = ALL.predicate();

View File

@ -138,4 +138,57 @@ public class PrivilegeTests extends ESTestCase {
assertThat(predicate.test("cluster:admin/xpack/whatever"), is(false)); assertThat(predicate.test("cluster:admin/xpack/whatever"), is(false));
} }
public void testIlmPrivileges() {
{
Predicate<String> predicate = ClusterPrivilege.MANAGE_ILM.predicate();
// check cluster actions
assertThat(predicate.test("cluster:admin/ilm/delete"), is(true));
assertThat(predicate.test("cluster:admin/ilm/_move/post"), is(true));
assertThat(predicate.test("cluster:admin/ilm/put"), is(true));
assertThat(predicate.test("cluster:admin/ilm/start"), is(true));
assertThat(predicate.test("cluster:admin/ilm/stop"), is(true));
assertThat(predicate.test("cluster:admin/ilm/brand_new_api"), is(true));
assertThat(predicate.test("cluster:admin/ilm/get"), is(true));
assertThat(predicate.test("cluster:admin/ilm/operation_mode/get"), is(true));
// check non-ilm action
assertThat(predicate.test("cluster:admin/whatever"), is(false));
}
{
Predicate<String> predicate = ClusterPrivilege.READ_ILM.predicate();
// check cluster actions
assertThat(predicate.test("cluster:admin/ilm/delete"), is(false));
assertThat(predicate.test("cluster:admin/ilm/_move/post"), is(false));
assertThat(predicate.test("cluster:admin/ilm/put"), is(false));
assertThat(predicate.test("cluster:admin/ilm/start"), is(false));
assertThat(predicate.test("cluster:admin/ilm/stop"), is(false));
assertThat(predicate.test("cluster:admin/ilm/brand_new_api"), is(false));
assertThat(predicate.test("cluster:admin/ilm/get"), is(true));
assertThat(predicate.test("cluster:admin/ilm/operation_mode/get"), is(true));
// check non-ilm action
assertThat(predicate.test("cluster:admin/whatever"), is(false));
}
{
Predicate<String> predicate = IndexPrivilege.MANAGE_ILM.predicate();
// check indices actions
assertThat(predicate.test("indices:admin/ilm/retry"), is(true));
assertThat(predicate.test("indices:admin/ilm/remove_policy"), is(true));
assertThat(predicate.test("indices:admin/ilm/brand_new_api"), is(true));
assertThat(predicate.test("indices:admin/ilm/explain"), is(true));
// check non-ilm action
assertThat(predicate.test("indices:admin/whatever"), is(false));
}
{
Predicate<String> predicate = IndexPrivilege.VIEW_METADATA.predicate();
// check indices actions
assertThat(predicate.test("indices:admin/ilm/retry"), is(false));
assertThat(predicate.test("indices:admin/ilm/remove_policy"), is(false));
assertThat(predicate.test("indices:admin/ilm/brand_new_api"), is(false));
assertThat(predicate.test("indices:admin/ilm/explain"), is(true));
// check non-ilm action
assertThat(predicate.test("indices:admin/whatever"), is(false));
}
}
} }

View File

@ -1,8 +1,11 @@
ilm: ilm:
cluster: cluster:
- monitor - monitor
- manage - manage_ilm
indices: indices:
- names: [ 'view-only-*' ]
privileges:
- view_index_metadata
- names: [ 'ilm-*' ] - names: [ 'ilm-*' ]
privileges: privileges:
- monitor - monitor

View File

@ -119,6 +119,13 @@ public class PermissionsIT extends ESRestTestCase {
}); });
} }
public void testCanViewExplainOnUnmanagedIndex() throws Exception {
createIndexAsAdmin("view-only-ilm", indexSettingsWithPolicy, "");
Request request = new Request("GET", "/view-only-ilm/_ilm/explain");
// test_ilm user has permissions to view
assertOK(client().performRequest(request));
}
private void createNewSingletonPolicy(String policy, String phaseName, LifecycleAction action) throws IOException { private void createNewSingletonPolicy(String policy, String phaseName, LifecycleAction action) throws IOException {
Phase phase = new Phase(phaseName, TimeValue.ZERO, singletonMap(action.getWriteableName(), action)); Phase phase = new Phase(phaseName, TimeValue.ZERO, singletonMap(action.getWriteableName(), action));
LifecyclePolicy lifecyclePolicy = new LifecyclePolicy(policy, singletonMap(phase.getName(), phase)); LifecyclePolicy lifecyclePolicy = new LifecyclePolicy(policy, singletonMap(phase.getName(), phase));