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:
parent
f998e04c34
commit
e3cf642299
|
@ -315,9 +315,11 @@ public final class Role {
|
|||
public static final String MANAGE_PIPELINE = "manage_pipeline";
|
||||
public static final String MANAGE_CCR = "manage_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,
|
||||
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 VIEW_INDEX_METADATA = "view_index_metadata";
|
||||
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,
|
||||
DELETE_INDEX, CREATE_INDEX, VIEW_INDEX_METADATA, MANAGE_FOLLOW_INDEX };
|
||||
DELETE_INDEX, CREATE_INDEX, VIEW_INDEX_METADATA, MANAGE_FOLLOW_INDEX, MANAGE_ILM };
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import org.apache.lucene.util.automaton.Automaton;
|
|||
import org.elasticsearch.action.admin.cluster.state.ClusterStateAction;
|
||||
import org.elasticsearch.common.Strings;
|
||||
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.RefreshTokenAction;
|
||||
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 =
|
||||
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 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 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_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 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();
|
||||
|
||||
|
@ -92,6 +98,8 @@ public final class ClusterPrivilege extends Privilege {
|
|||
.put("manage_rollup", MANAGE_ROLLUP)
|
||||
.put("manage_ccr", MANAGE_CCR)
|
||||
.put("read_ccr", READ_CCR)
|
||||
.put("manage_ilm", MANAGE_ILM)
|
||||
.put("read_ilm", READ_ILM)
|
||||
.immutableMap();
|
||||
|
||||
private static final ConcurrentHashMap<Set<String>, ClusterPrivilege> CACHE = new ConcurrentHashMap<>();
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.elasticsearch.common.Strings;
|
|||
import org.elasticsearch.common.collect.MapBuilder;
|
||||
import org.elasticsearch.xpack.core.ccr.action.PutFollowAction;
|
||||
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 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 VIEW_METADATA_AUTOMATON = patterns(GetAliasesAction.NAME, AliasesExistAction.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,
|
||||
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 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 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_ILM = new IndexPrivilege("manage_ilm", MANAGE_ILM_AUTOMATON);
|
||||
|
||||
private static final Map<String, IndexPrivilege> VALUES = MapBuilder.<String, IndexPrivilege>newMapBuilder()
|
||||
.put("none", NONE)
|
||||
|
@ -91,6 +95,7 @@ public final class IndexPrivilege extends Privilege {
|
|||
.put("view_index_metadata", VIEW_METADATA)
|
||||
.put("read_cross_cluster", READ_CROSS_CLUSTER)
|
||||
.put("manage_follow_index", MANAGE_FOLLOW_INDEX)
|
||||
.put("manage_ilm", MANAGE_ILM)
|
||||
.immutableMap();
|
||||
|
||||
public static final Predicate<String> ACTION_MATCHER = ALL.predicate();
|
||||
|
|
|
@ -138,4 +138,57 @@ public class PrivilegeTests extends ESTestCase {
|
|||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
ilm:
|
||||
cluster:
|
||||
- monitor
|
||||
- manage
|
||||
- manage_ilm
|
||||
indices:
|
||||
- names: [ 'view-only-*' ]
|
||||
privileges:
|
||||
- view_index_metadata
|
||||
- names: [ 'ilm-*' ]
|
||||
privileges:
|
||||
- monitor
|
||||
|
|
|
@ -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 {
|
||||
Phase phase = new Phase(phaseName, TimeValue.ZERO, singletonMap(action.getWriteableName(), action));
|
||||
LifecyclePolicy lifecyclePolicy = new LifecyclePolicy(policy, singletonMap(phase.getName(), phase));
|
||||
|
|
Loading…
Reference in New Issue