security: add a built in kibana_user role

This commit adds a `kibana_user` role that can be used to grant the minimum
set of privileges necessary to access kibana.

Closes elastic/elasticsearch#2166

Original commit: elastic/x-pack-elasticsearch@00e129e342
This commit is contained in:
jaymode 2016-05-26 16:45:59 -04:00
parent 28f89314cb
commit 2a7e2a6cd6
4 changed files with 121 additions and 5 deletions

View File

@ -0,0 +1,28 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.shield.authz.permission;
import org.elasticsearch.shield.authz.RoleDescriptor;
import org.elasticsearch.shield.authz.privilege.ClusterPrivilege;
import org.elasticsearch.shield.authz.privilege.Privilege.Name;
public class KibanaUserRole extends Role {
private static final String[] CLUSTER_PRIVILEGES = new String[] { "monitor" };
private static final RoleDescriptor.IndicesPrivileges[] INDICES_PRIVILEGES = new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder().indices(".kibana*").privileges("manage", "read", "index").build() };
public static final String NAME = "kibana_user";
public static final RoleDescriptor DESCRIPTOR = new RoleDescriptor(NAME, CLUSTER_PRIVILEGES, INDICES_PRIVILEGES, null);
public static final KibanaUserRole INSTANCE = new KibanaUserRole();
private KibanaUserRole() {
super(DESCRIPTOR.getName(),
new ClusterPermission.Core(ClusterPrivilege.get(new Name(DESCRIPTOR.getClusterPrivileges()))),
new IndicesPermission.Core(Role.Builder.convertFromIndicesPrivileges(DESCRIPTOR.getIndicesPrivileges())),
RunAsPermission.Core.NONE);
}
}

View File

@ -10,6 +10,7 @@ import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.shield.SecurityContext;
import org.elasticsearch.shield.authz.RoleDescriptor;
import org.elasticsearch.shield.authz.permission.KibanaRole;
import org.elasticsearch.shield.authz.permission.KibanaUserRole;
import org.elasticsearch.shield.authz.permission.Role;
import org.elasticsearch.shield.authz.permission.SuperuserRole;
import org.elasticsearch.shield.authz.permission.TransportClientRole;
@ -39,6 +40,8 @@ public class ReservedRolesStore implements RolesStore {
return SuperuserRole.INSTANCE;
case TransportClientRole.NAME:
return TransportClientRole.INSTANCE;
case KibanaUserRole.NAME:
return KibanaUserRole.INSTANCE;
case KibanaRole.NAME:
// The only user that should know about this role is the kibana user itself (who has this role). The reason we want to hide
// this role is that it was created specifically for kibana, with all the permissions that the kibana user needs.
@ -58,6 +61,8 @@ public class ReservedRolesStore implements RolesStore {
return SuperuserRole.DESCRIPTOR;
case TransportClientRole.NAME:
return TransportClientRole.DESCRIPTOR;
case KibanaUserRole.NAME:
return KibanaUserRole.DESCRIPTOR;
case KibanaRole.NAME:
// The only user that should know about this role is the kibana user itself (who has this role). The reason we want to hide
// this role is that it was created specifically for kibana, with all the permissions that the kibana user needs.
@ -73,19 +78,21 @@ public class ReservedRolesStore implements RolesStore {
public Collection<RoleDescriptor> roleDescriptors() {
if (KibanaUser.is(securityContext.getUser())) {
return Arrays.asList(SuperuserRole.DESCRIPTOR, TransportClientRole.DESCRIPTOR, KibanaRole.DESCRIPTOR);
return Arrays.asList(SuperuserRole.DESCRIPTOR, TransportClientRole.DESCRIPTOR, KibanaUserRole.DESCRIPTOR,
KibanaRole.DESCRIPTOR);
}
return Arrays.asList(SuperuserRole.DESCRIPTOR, TransportClientRole.DESCRIPTOR);
return Arrays.asList(SuperuserRole.DESCRIPTOR, TransportClientRole.DESCRIPTOR, KibanaUserRole.DESCRIPTOR);
}
public static Set<String> names() {
return Sets.newHashSet(SuperuserRole.NAME, KibanaRole.NAME, TransportClientRole.NAME);
return Sets.newHashSet(SuperuserRole.NAME, KibanaRole.NAME, TransportClientRole.NAME, KibanaUserRole.NAME);
}
public static boolean isReserved(String role) {
switch (role) {
case SuperuserRole.NAME:
case KibanaRole.NAME:
case KibanaUserRole.NAME:
case TransportClientRole.NAME:
case SystemUser.ROLE_NAME:
return true;

View File

@ -0,0 +1,72 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.shield.authz.permission;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction;
import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteAction;
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsAction;
import org.elasticsearch.action.admin.cluster.state.ClusterStateAction;
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsAction;
import org.elasticsearch.action.admin.indices.create.CreateIndexAction;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexAction;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsAction;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction;
import org.elasticsearch.action.delete.DeleteAction;
import org.elasticsearch.action.index.IndexAction;
import org.elasticsearch.action.search.MultiSearchAction;
import org.elasticsearch.action.search.SearchAction;
import org.elasticsearch.marvel.action.MonitoringBulkAction;
import org.elasticsearch.shield.user.User;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.transport.TransportRequest;
import java.util.Arrays;
import static org.hamcrest.Matchers.is;
public class KibanaUserRoleTests extends ESTestCase {
public void testCluster() {
final User user = new User("joe");
final TransportRequest request = new TransportRequest.Empty();
assertThat(KibanaUserRole.INSTANCE.cluster().check(ClusterHealthAction.NAME, request, user), is(true));
assertThat(KibanaUserRole.INSTANCE.cluster().check(ClusterStateAction.NAME, request, user), is(true));
assertThat(KibanaUserRole.INSTANCE.cluster().check(ClusterStatsAction.NAME, request, user), is(true));
assertThat(KibanaUserRole.INSTANCE.cluster().check(PutIndexTemplateAction.NAME, request, user), is(false));
assertThat(KibanaUserRole.INSTANCE.cluster().check(ClusterRerouteAction.NAME, request, user), is(false));
assertThat(KibanaUserRole.INSTANCE.cluster().check(ClusterUpdateSettingsAction.NAME, request, user), is(false));
assertThat(KibanaUserRole.INSTANCE.cluster().check(MonitoringBulkAction.NAME, request, user), is(false));
}
public void testRunAs() {
assertThat(KibanaUserRole.INSTANCE.runAs().isEmpty(), is(true));
}
public void testUnauthorizedIndices() {
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher(IndexAction.NAME).test("foo"), is(false));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher(IndexAction.NAME).test(".reporting"), is(false));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher("indices:foo")
.test(randomAsciiOfLengthBetween(8, 24)), is(false));
}
public void testKibanaIndices() {
Arrays.asList(".kibana", ".kibana-devnull").forEach(this::testIndexAccess);
}
private void testIndexAccess(String index) {
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher("indices:foo").test(index), is(false));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher("indices:bar").test(index), is(false));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher(DeleteAction.NAME).test(index), is(false));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher(DeleteIndexAction.NAME).test(index), is(true));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher(CreateIndexAction.NAME).test(index), is(true));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher(IndexAction.NAME).test(index), is(true));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher(SearchAction.NAME).test(index), is(true));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher(MultiSearchAction.NAME).test(index), is(true));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher(UpdateSettingsAction.NAME).test(index), is(true));
}
}

View File

@ -7,6 +7,7 @@ package org.elasticsearch.shield.authz.store;
import org.elasticsearch.shield.SecurityContext;
import org.elasticsearch.shield.authz.permission.KibanaRole;
import org.elasticsearch.shield.authz.permission.KibanaUserRole;
import org.elasticsearch.shield.authz.permission.SuperuserRole;
import org.elasticsearch.shield.authz.permission.TransportClientRole;
import org.elasticsearch.shield.user.ElasticUser;
@ -50,7 +51,11 @@ public class ReservedRolesStoreTests extends ESTestCase {
assertThat(reservedRolesStore.role(TransportClientRole.NAME), sameInstance(TransportClientRole.INSTANCE));
assertThat(reservedRolesStore.roleDescriptor(TransportClientRole.NAME), sameInstance(TransportClientRole.DESCRIPTOR));
assertThat(reservedRolesStore.roleDescriptors(), contains(SuperuserRole.DESCRIPTOR, TransportClientRole.DESCRIPTOR));
assertThat(reservedRolesStore.role(KibanaUserRole.NAME), sameInstance(KibanaUserRole.INSTANCE));
assertThat(reservedRolesStore.roleDescriptor(KibanaUserRole.NAME), sameInstance(KibanaUserRole.DESCRIPTOR));
assertThat(reservedRolesStore.roleDescriptors(),
contains(SuperuserRole.DESCRIPTOR, TransportClientRole.DESCRIPTOR, KibanaUserRole.DESCRIPTOR));
assertThat(reservedRolesStore.role(KibanaRole.NAME), nullValue());
assertThat(reservedRolesStore.roleDescriptor(KibanaRole.NAME), nullValue());
@ -66,10 +71,13 @@ public class ReservedRolesStoreTests extends ESTestCase {
assertThat(reservedRolesStore.role(TransportClientRole.NAME), sameInstance(TransportClientRole.INSTANCE));
assertThat(reservedRolesStore.roleDescriptor(TransportClientRole.NAME), sameInstance(TransportClientRole.DESCRIPTOR));
assertThat(reservedRolesStore.role(KibanaUserRole.NAME), sameInstance(KibanaUserRole.INSTANCE));
assertThat(reservedRolesStore.roleDescriptor(KibanaUserRole.NAME), sameInstance(KibanaUserRole.DESCRIPTOR));
assertThat(reservedRolesStore.role(KibanaRole.NAME), sameInstance(KibanaRole.INSTANCE));
assertThat(reservedRolesStore.roleDescriptor(KibanaRole.NAME), sameInstance(KibanaRole.DESCRIPTOR));
assertThat(reservedRolesStore.roleDescriptors(),
contains(SuperuserRole.DESCRIPTOR, TransportClientRole.DESCRIPTOR, KibanaRole.DESCRIPTOR));
contains(SuperuserRole.DESCRIPTOR, TransportClientRole.DESCRIPTOR, KibanaUserRole.DESCRIPTOR, KibanaRole.DESCRIPTOR));
assertThat(reservedRolesStore.role(SystemUser.ROLE_NAME), nullValue());
}
@ -80,5 +88,6 @@ public class ReservedRolesStoreTests extends ESTestCase {
assertThat(ReservedRolesStore.isReserved("foobar"), is(false));
assertThat(ReservedRolesStore.isReserved(SystemUser.ROLE_NAME), is(true));
assertThat(ReservedRolesStore.isReserved(TransportClientRole.NAME), is(true));
assertThat(ReservedRolesStore.isReserved(KibanaUserRole.NAME), is(true));
}
}