Security: Add watcher_user and watcher_admin role (elastic/x-pack-elasticsearch#983)

This built-in watcher_admin role is able to execute all watcher actions,
read the watch history indices and read the watches index
index. The watcher_user role allows to GET a watch and to get the stats and thats it.

relates elastic/x-pack-elasticsearch#978

Original commit: elastic/x-pack-elasticsearch@11b33a413b
This commit is contained in:
Alexander Reelsen 2017-04-11 16:28:55 +01:00 committed by GitHub
parent a5f0a74b67
commit 71852c7215
5 changed files with 124 additions and 7 deletions

View File

@ -120,6 +120,18 @@ Grants access necessary for the Logstash system user to send data to Elasticsear
NOTE: This role should not be assigned to users as the granted permissions may
change between releases.
[[built-in-roles-watcher-user]]
`watcher_user`::
+
Grants read access to the `.watches` index, the get watch action and the watcher
stats.
[[built-in-roles-watcher-admin]]
`watcher_admin`::
+
Grants write access to the `.watches` index, read access to the watch history and
the triggered watches index and allows to execute all watcher actions.
[[defining-roles]]
=== Defining Roles

View File

@ -15,6 +15,9 @@ settings update, rerouting, or managing users and roles.
All cluster read-only operations, like cluster health & state, hot threads, node
info, node & cluster stats, snapshot/restore status, pending cluster tasks.
`monitor_watcher`::
All read only watcher operations, such as getting a watch and watcher stats.
`manage`::
Builds on `monitor` and adds cluster operations that change values in the cluster.
This includes snapshotting,updating settings, and rerouting. This privilege does
@ -30,6 +33,9 @@ All operations on index templates.
`manage_pipeline`::
All operations on ingest pipelines.
`manage_watcher`::
All watcher operations, such as putting watches, executing, activate or acknowledging.
`transport_client`::
All privileges necessary for a transport client to connect.

View File

@ -27,9 +27,11 @@ public final class ClusterPrivilege extends Privilege {
private static final Automaton MANAGE_SECURITY_AUTOMATON = patterns("cluster:admin/xpack/security/*");
private static final Automaton MONITOR_AUTOMATON = patterns("cluster:monitor/*");
private static final Automaton MONITOR_ML_AUTOMATON = patterns("cluster:monitor/xpack/ml/*");
private static final Automaton MONITOR_WATCHER_AUTOMATON = patterns("cluster:monitor/xpack/watcher/*");
private static final Automaton ALL_CLUSTER_AUTOMATON = patterns("cluster:*", "indices:admin/template/*");
private static final Automaton MANAGE_AUTOMATON = minusAndMinimize(ALL_CLUSTER_AUTOMATON, MANAGE_SECURITY_AUTOMATON);
private static final Automaton MANAGE_ML_AUTOMATON = patterns("cluster:admin/xpack/ml/*", "cluster:monitor/xpack/ml/*");
private static final Automaton MANAGE_WATCHER_AUTOMATON = patterns("cluster:admin/xpack/watcher/*", "cluster:monitor/xpack/watcher/*");
private static final Automaton TRANSPORT_CLIENT_AUTOMATON = patterns("cluster:monitor/nodes/liveness", "cluster:monitor/state");
private static final Automaton MANAGE_IDX_TEMPLATE_AUTOMATON = patterns("indices:admin/template/*");
private static final Automaton MANAGE_INGEST_PIPELINE_AUTOMATON = patterns("cluster:admin/ingest/pipeline/*");
@ -38,8 +40,10 @@ public final class ClusterPrivilege extends Privilege {
public static final ClusterPrivilege ALL = new ClusterPrivilege("all", ALL_CLUSTER_AUTOMATON);
public static final ClusterPrivilege MONITOR = new ClusterPrivilege("monitor", MONITOR_AUTOMATON);
public static final ClusterPrivilege MONITOR_ML = new ClusterPrivilege("monitor_ml", MONITOR_ML_AUTOMATON);
public static final ClusterPrivilege MONITOR_WATCHER = new ClusterPrivilege("monitor_watcher", MONITOR_WATCHER_AUTOMATON);
public static final ClusterPrivilege MANAGE = new ClusterPrivilege("manage", MANAGE_AUTOMATON);
public static final ClusterPrivilege MANAGE_ML = new ClusterPrivilege("manage_ml", MANAGE_ML_AUTOMATON);
public static final ClusterPrivilege MANAGE_WATCHER = new ClusterPrivilege("manage_watcher", MANAGE_WATCHER_AUTOMATON);
public static final ClusterPrivilege MANAGE_IDX_TEMPLATES =
new ClusterPrivilege("manage_index_templates", MANAGE_IDX_TEMPLATE_AUTOMATON);
public static final ClusterPrivilege MANAGE_INGEST_PIPELINES =
@ -55,8 +59,10 @@ public final class ClusterPrivilege extends Privilege {
.put("all", ALL)
.put("monitor", MONITOR)
.put("monitor_ml", MONITOR_ML)
.put("monitor_watcher", MONITOR_WATCHER)
.put("manage", MANAGE)
.put("manage_ml", MANAGE_ML)
.put("manage_watcher", MANAGE_WATCHER)
.put("manage_index_templates", MANAGE_IDX_TEMPLATES)
.put("manage_ingest_pipelines", MANAGE_INGEST_PIPELINES)
.put("transport_client", TRANSPORT_CLIENT)

View File

@ -5,19 +5,21 @@
*/
package org.elasticsearch.xpack.security.authz.store;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.xpack.monitoring.action.MonitoringBulkAction;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.support.MetadataUtils;
import org.elasticsearch.xpack.security.user.KibanaUser;
import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.xpack.watcher.execution.TriggeredWatchStore;
import org.elasticsearch.xpack.watcher.history.HistoryStore;
import org.elasticsearch.xpack.watcher.watch.Watch;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
public class ReservedRolesStore {
@ -70,6 +72,19 @@ public class ReservedRolesStore {
new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder().indices(".ml-*").privileges("view_index_metadata", "read")
.build() }, null, MetadataUtils.DEFAULT_RESERVED_METADATA))
.put("watcher_admin", new RoleDescriptor("watcher_admin", new String[] { "manage_watcher" },
new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder().indices(Watch.INDEX, TriggeredWatchStore.INDEX_NAME,
HistoryStore.INDEX_PREFIX + "*").privileges("read").build() },
null, MetadataUtils.DEFAULT_RESERVED_METADATA))
.put("watcher_user", new RoleDescriptor("watcher_user", new String[] { "monitor_watcher" },
new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder().indices(Watch.INDEX)
.privileges("read")
.build(),
RoleDescriptor.IndicesPrivileges.builder().indices(HistoryStore.INDEX_PREFIX + "*")
.privileges("read")
.build() }, null, MetadataUtils.DEFAULT_RESERVED_METADATA))
.immutableMap();
}

View File

@ -33,6 +33,7 @@ import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.monitoring.action.MonitoringBulkAction;
import org.elasticsearch.xpack.security.action.role.PutRoleAction;
import org.elasticsearch.xpack.security.action.user.PutUserAction;
@ -41,7 +42,19 @@ import org.elasticsearch.xpack.security.authz.accesscontrol.IndicesAccessControl
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsCache;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.watcher.execution.TriggeredWatchStore;
import org.elasticsearch.xpack.watcher.history.HistoryStore;
import org.elasticsearch.xpack.watcher.transport.actions.ack.AckWatchAction;
import org.elasticsearch.xpack.watcher.transport.actions.activate.ActivateWatchAction;
import org.elasticsearch.xpack.watcher.transport.actions.delete.DeleteWatchAction;
import org.elasticsearch.xpack.watcher.transport.actions.execute.ExecuteWatchAction;
import org.elasticsearch.xpack.watcher.transport.actions.get.GetWatchAction;
import org.elasticsearch.xpack.watcher.transport.actions.put.PutWatchAction;
import org.elasticsearch.xpack.watcher.transport.actions.service.WatcherServiceAction;
import org.elasticsearch.xpack.watcher.transport.actions.stats.WatcherStatsAction;
import org.elasticsearch.xpack.watcher.watch.Watch;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import java.util.Arrays;
import java.util.Map;
@ -65,6 +78,8 @@ public class ReservedRolesStoreTests extends ESTestCase {
assertThat(ReservedRolesStore.isReserved("remote_monitoring_agent"), is(true));
assertThat(ReservedRolesStore.isReserved("monitoring_user"), is(true));
assertThat(ReservedRolesStore.isReserved("reporting_user"), is(true));
assertThat(ReservedRolesStore.isReserved("watcher_user"), is(true));
assertThat(ReservedRolesStore.isReserved("watcher_admin"), is(true));
}
public void testIngestAdminRole() {
@ -361,4 +376,67 @@ public class ReservedRolesStoreTests extends ESTestCase {
assertThat(beatsSystemRole.indices().allowedIndicesMatcher("indices:foo").test(randomAlphaOfLengthBetween(8, 24)),
is(false));
}
public void testWatcherAdminRole() {
RoleDescriptor roleDescriptor = new ReservedRolesStore().roleDescriptor("watcher_admin");
assertNotNull(roleDescriptor);
assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true));
Role role = Role.builder(roleDescriptor, null).build();
assertThat(role.cluster().check(PutWatchAction.NAME), is(true));
assertThat(role.cluster().check(GetWatchAction.NAME), is(true));
assertThat(role.cluster().check(DeleteWatchAction.NAME), is(true));
assertThat(role.cluster().check(ExecuteWatchAction.NAME), is(true));
assertThat(role.cluster().check(AckWatchAction.NAME), is(true));
assertThat(role.cluster().check(ActivateWatchAction.NAME), is(true));
assertThat(role.cluster().check(WatcherServiceAction.NAME), is(true));
assertThat(role.cluster().check(WatcherStatsAction.NAME), is(true));
assertThat(role.runAs().check(randomAlphaOfLengthBetween(1, 30)), is(false));
assertThat(role.indices().allowedIndicesMatcher(IndexAction.NAME).test("foo"), is(false));
DateTime now = DateTime.now(DateTimeZone.UTC);
String historyIndex = HistoryStore.getHistoryIndexNameForTime(now);
for (String index : new String[]{ Watch.INDEX, historyIndex, TriggeredWatchStore.INDEX_NAME }) {
assertOnlyReadAllowed(role, index);
}
}
public void testWatcherUserRole() {
RoleDescriptor roleDescriptor = new ReservedRolesStore().roleDescriptor("watcher_user");
assertNotNull(roleDescriptor);
assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true));
Role role = Role.builder(roleDescriptor, null).build();
assertThat(role.cluster().check(PutWatchAction.NAME), is(false));
assertThat(role.cluster().check(GetWatchAction.NAME), is(true));
assertThat(role.cluster().check(DeleteWatchAction.NAME), is(false));
assertThat(role.cluster().check(ExecuteWatchAction.NAME), is(false));
assertThat(role.cluster().check(AckWatchAction.NAME), is(false));
assertThat(role.cluster().check(ActivateWatchAction.NAME), is(false));
assertThat(role.cluster().check(WatcherServiceAction.NAME), is(false));
assertThat(role.cluster().check(WatcherStatsAction.NAME), is(true));
assertThat(role.runAs().check(randomAlphaOfLengthBetween(1, 30)), is(false));
assertThat(role.indices().allowedIndicesMatcher(IndexAction.NAME).test("foo"), is(false));
assertThat(role.indices().allowedIndicesMatcher(IndexAction.NAME).test(TriggeredWatchStore.INDEX_NAME), is(false));
DateTime now = DateTime.now(DateTimeZone.UTC);
String historyIndex = HistoryStore.getHistoryIndexNameForTime(now);
for (String index : new String[]{ Watch.INDEX, historyIndex }) {
assertOnlyReadAllowed(role, index);
}
}
private void assertOnlyReadAllowed(Role role, String index) {
assertThat(role.indices().allowedIndicesMatcher(DeleteIndexAction.NAME).test(index), is(false));
assertThat(role.indices().allowedIndicesMatcher(CreateIndexAction.NAME).test(index), is(false));
assertThat(role.indices().allowedIndicesMatcher(UpdateSettingsAction.NAME).test(index), is(false));
assertThat(role.indices().allowedIndicesMatcher(SearchAction.NAME).test(index), is(true));
assertThat(role.indices().allowedIndicesMatcher(GetAction.NAME).test(index), is(true));
assertThat(role.indices().allowedIndicesMatcher(IndexAction.NAME).test(index), is(false));
assertThat(role.indices().allowedIndicesMatcher(UpdateAction.NAME).test(index), is(false));
assertThat(role.indices().allowedIndicesMatcher(DeleteAction.NAME).test(index), is(false));
assertThat(role.indices().allowedIndicesMatcher(BulkAction.NAME).test(index), is(false));
}
}