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:
parent
28f89314cb
commit
2a7e2a6cd6
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue