Skip to content

Commit

Permalink
Add new predefined reserved roles (#71710) (#71904)
Browse files Browse the repository at this point in the history
This change adds two new reserved roles in Elasticsearch, viewer
and editor
Solutions will use these roles in order to provide,out of the box,
a role for full access to data and configuration with no access to
cluster management(editor) and a role for read only access to data
again with no access to cluster management (viewer).
  • Loading branch information
jkakavas authored Apr 20, 2021
1 parent 2257f8c commit 1776a4f
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ public void testGetRoles() throws Exception {
List<Role> roles = response.getRoles();
assertNotNull(response);
// 29 system roles plus the three we created
assertThat(roles.size(), equalTo(29 + 3));
assertThat(roles.size(), equalTo(31 + 3));
}

{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,9 +336,60 @@ private static Map<String, RoleDescriptor> initializeReservedRoles() {
.indices(".enrich-*")
.privileges("manage", "read", "write")
.build() }, null, MetadataUtils.DEFAULT_RESERVED_METADATA))
.put("viewer", buildViewerRoleDescriptor())
.put("editor", buildEditorRoleDescriptor())
.immutableMap();
}

private static RoleDescriptor buildViewerRoleDescriptor() {
return new RoleDescriptor(
"viewer",
new String[] {},
new RoleDescriptor.IndicesPrivileges[] {
// Stack
RoleDescriptor.IndicesPrivileges.builder()
.indices("/~(([.]|ilm-history-).*)/")
.privileges("read", "view_index_metadata").build(),
// Security
RoleDescriptor.IndicesPrivileges.builder().indices(".siem-signals-*").privileges("read", "view_index_metadata").build() },
new RoleDescriptor.ApplicationResourcePrivileges[] {
RoleDescriptor.ApplicationResourcePrivileges.builder()
.application("kibana-.kibana")
.resources("*")
.privileges("read").build() },
null,
null,
MetadataUtils.DEFAULT_RESERVED_METADATA,
null);
}

private static RoleDescriptor buildEditorRoleDescriptor() {
return new RoleDescriptor("editor",
new String[] {},
new RoleDescriptor.IndicesPrivileges[] {
// Stack
RoleDescriptor.IndicesPrivileges.builder()
.indices("/~(([.]|ilm-history-).*)/")
.privileges("read", "view_index_metadata").build(),
// Observability
RoleDescriptor.IndicesPrivileges.builder()
.indices("observability-annotations")
.privileges("read", "view_index_metadata", "write").build(),
// Security
RoleDescriptor.IndicesPrivileges.builder()
.indices(".siem-signals-*", ".lists-*", ".items-*")
.privileges("read", "view_index_metadata", "write", "maintenance").build() },
new RoleDescriptor.ApplicationResourcePrivileges[] {
RoleDescriptor.ApplicationResourcePrivileges.builder()
.application("kibana-.kibana")
.resources("*")
.privileges("all").build() },
null,
null,
MetadataUtils.DEFAULT_RESERVED_METADATA,
null);
}

private static RoleDescriptor kibanaAdminUser(String name, Map<String, Object> metadata) {
return new RoleDescriptor(name, null, null,
new RoleDescriptor.ApplicationResourcePrivileges[] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,8 @@ public void testIsReserved() {
assertThat(ReservedRolesStore.isReserved("snapshot_user"), is(true));
assertThat(ReservedRolesStore.isReserved("code_admin"), is(false));
assertThat(ReservedRolesStore.isReserved("code_user"), is(false));
assertThat(ReservedRolesStore.isReserved("viewer"), is(true));
assertThat(ReservedRolesStore.isReserved("editor"), is(true));
}

public void testSnapshotUserRole() {
Expand Down Expand Up @@ -1666,6 +1668,123 @@ public void testWatcherUserRole() {
assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2));
}

public void testPredefinedViewerRole() {
final TransportRequest request = mock(TransportRequest.class);
final Authentication authentication = mock(Authentication.class);

RoleDescriptor roleDescriptor = new ReservedRolesStore().roleDescriptor("viewer");
assertNotNull(roleDescriptor);
assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true));

Role role = Role.builder(roleDescriptor, null).build();
// No cluster privileges
assertThat(role.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false));
assertThat(role.cluster().check(ClusterStateAction.NAME, request, authentication), is(false));
assertThat(role.cluster().check(ClusterStatsAction.NAME, request, authentication), is(false));
assertThat(role.cluster().check(PutIndexTemplateAction.NAME, request, authentication), is(false));
assertThat(role.cluster().check(ClusterRerouteAction.NAME, request, authentication), is(false));
assertThat(role.cluster().check(ClusterUpdateSettingsAction.NAME, request, authentication), is(false));
assertThat(role.cluster().check(MonitoringBulkAction.NAME, request, authentication), is(false));
assertThat(role.cluster().check(DelegatePkiAuthenticationAction.NAME, request, authentication), is(false));
// Check index privileges
assertOnlyReadAllowed(role, "observability-annotations");
assertOnlyReadAllowed(role, "logs-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "metrics-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "synthetics-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "apm-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "traces-apm." + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "filebeat-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "metricbeat-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "heardbeat-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "kibana_sample_data_-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, ".siem-signals-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "apm-" + randomIntBetween(0, 5) + "-transaction-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "logs-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "auditbeat-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "filebeat-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "packetbeat-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "winlogbeat-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "endgame-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, randomAlphaOfLength(5));

assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(role, "." + randomAlphaOfLengthBetween(6, 10));
assertNoAccessAllowed(role, "ilm-history-" + randomIntBetween(0, 5));
// Check application privileges
assertThat(role.application().grants(new ApplicationPrivilege("kibana-.kibana", "kibana-read", "read"), "*"), is(true));
assertThat(role.application().grants(new ApplicationPrivilege("kibana-.kibana", "kibana-all", "all"), "*"), is(false));

assertThat(role.runAs().check(randomAlphaOfLengthBetween(1, 20)), is(false));
}

public void testPredefinedEditorRole() {
final TransportRequest request = mock(TransportRequest.class);
final Authentication authentication = mock(Authentication.class);

RoleDescriptor roleDescriptor = new ReservedRolesStore().roleDescriptor("editor");
assertNotNull(roleDescriptor);
assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true));

Role role = Role.builder(roleDescriptor, null).build();

// No cluster privileges
assertThat(role.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false));
assertThat(role.cluster().check(ClusterStateAction.NAME, request, authentication), is(false));
assertThat(role.cluster().check(ClusterStatsAction.NAME, request, authentication), is(false));
assertThat(role.cluster().check(PutIndexTemplateAction.NAME, request, authentication), is(false));
assertThat(role.cluster().check(ClusterRerouteAction.NAME, request, authentication), is(false));
assertThat(role.cluster().check(ClusterUpdateSettingsAction.NAME, request, authentication), is(false));
assertThat(role.cluster().check(MonitoringBulkAction.NAME, request, authentication), is(false));
assertThat(role.cluster().check(DelegatePkiAuthenticationAction.NAME, request, authentication), is(false));

// Check index privileges
assertOnlyReadAllowed(role, "logs-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "metrics-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "synthetics-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "apm-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "traces-apm." + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "filebeat-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "metricbeat-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "heardbeat-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "kibana_sample_data_-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "apm-" + randomIntBetween(0, 5) + "-transaction-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "logs-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "auditbeat-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "filebeat-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "packetbeat-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "winlogbeat-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, "endgame-" + randomIntBetween(0, 5));
assertOnlyReadAllowed(role, randomAlphaOfLength(5));

assertReadWriteDocsAndMaintenanceButNotDeleteIndexAllowed(role, ".siem-signals-" + randomIntBetween(0, 5));
assertReadWriteDocsAndMaintenanceButNotDeleteIndexAllowed(role, ".lists-" + randomIntBetween(0, 5));
assertReadWriteDocsAndMaintenanceButNotDeleteIndexAllowed(role, ".items-" + randomIntBetween(0, 5));
assertReadWriteDocsButNotDeleteIndexAllowed(role, "observability-annotations");

assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES);
assertNoAccessAllowed(role, "." + randomAlphaOfLengthBetween(6, 10));
assertNoAccessAllowed(role, "ilm-history-" + randomIntBetween(0, 5));

// Check application privileges
assertThat(role.application().grants(new ApplicationPrivilege("kibana-.kibana", "kibana-all", "all"), "*"), is(true));

assertThat(role.runAs().check(randomAlphaOfLengthBetween(1, 20)), is(false));
}

private void assertReadWriteDocsAndMaintenanceButNotDeleteIndexAllowed(Role role, String index) {
assertThat(role.indices().allowedIndicesMatcher(DeleteIndexAction.NAME).test(mockIndexAbstraction(index)), is(false));
assertThat(role.indices().allowedIndicesMatcher(SearchAction.NAME).test(mockIndexAbstraction(index)), is(true));
assertThat(role.indices().allowedIndicesMatcher(GetAction.NAME).test(mockIndexAbstraction(index)), is(true));
assertThat(role.indices().allowedIndicesMatcher(IndexAction.NAME).test(mockIndexAbstraction(index)), is(true));
assertThat(role.indices().allowedIndicesMatcher(UpdateAction.NAME).test(mockIndexAbstraction(index)), is(true));
assertThat(role.indices().allowedIndicesMatcher(DeleteAction.NAME).test(mockIndexAbstraction(index)), is(true));
assertThat(role.indices().allowedIndicesMatcher(BulkAction.NAME).test(mockIndexAbstraction(index)), is(true));
assertThat(role.indices().allowedIndicesMatcher("indices:admin/refresh*").test(mockIndexAbstraction(index)), is(true));
assertThat(role.indices().allowedIndicesMatcher("indices:admin/flush*").test(mockIndexAbstraction(index)), is(true));
assertThat(role.indices().allowedIndicesMatcher("indices:admin/synced_flush").test(mockIndexAbstraction(index)), is(true));
assertThat(role.indices().allowedIndicesMatcher("indices:admin/forcemerge*").test(mockIndexAbstraction(index)), is(true));
}

private void assertReadWriteDocsButNotDeleteIndexAllowed(Role role, String index) {
assertThat(role.indices().allowedIndicesMatcher(DeleteIndexAction.NAME).test(mockIndexAbstraction(index)), is(false));
assertThat(role.indices().allowedIndicesMatcher(SearchAction.NAME).test(mockIndexAbstraction(index)), is(true));
Expand Down

0 comments on commit 1776a4f

Please sign in to comment.