Skip to content

Commit

Permalink
[X-Pack] Beats centralized management: security role + licensing (#34305
Browse files Browse the repository at this point in the history
)

Backport of #30520 to `6.x`. Original description:

Resolves #30493.

This PR adds:
* a built-in role, `beats_admin` that provides unfettered access to the `.management-beats` index. The purpose of this index is to store configuration and other peripheral information to make the Beats Centralized Management feature work.
* licensing-related logic for the Beats Centralized Management feature.
  • Loading branch information
ycombinator authored Oct 10, 2018
1 parent 662422d commit 3ac2b31
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ public class XPackLicenseState {
messages.put(XPackField.LOGSTASH, new String[] {
"Logstash will continue to poll centrally-managed pipelines"
});
messages.put(XPackField.BEATS, new String[] {
"Beats will continue to poll centrally-managed configuration"
});
messages.put(XPackField.DEPRECATION, new String[] {
"Deprecation APIs are disabled"
});
Expand Down Expand Up @@ -84,6 +87,7 @@ public class XPackLicenseState {
messages.put(XPackField.GRAPH, XPackLicenseState::graphAcknowledgementMessages);
messages.put(XPackField.MACHINE_LEARNING, XPackLicenseState::machineLearningAcknowledgementMessages);
messages.put(XPackField.LOGSTASH, XPackLicenseState::logstashAcknowledgementMessages);
messages.put(XPackField.BEATS, XPackLicenseState::beatsAcknowledgementMessages);
messages.put(XPackField.SQL, XPackLicenseState::sqlAcknowledgementMessages);
ACKNOWLEDGMENT_MESSAGES = Collections.unmodifiableMap(messages);
}
Expand Down Expand Up @@ -208,12 +212,19 @@ private static String[] machineLearningAcknowledgementMessages(OperationMode cur
private static String[] logstashAcknowledgementMessages(OperationMode currentMode, OperationMode newMode) {
switch (newMode) {
case BASIC:
switch (currentMode) {
case TRIAL:
case STANDARD:
case GOLD:
case PLATINUM:
return new String[] { "Logstash will no longer poll for centrally-managed pipelines" };
if (isBasic(currentMode) == false) {
return new String[] { "Logstash will no longer poll for centrally-managed pipelines" };
}
break;
}
return Strings.EMPTY_ARRAY;
}

private static String[] beatsAcknowledgementMessages(OperationMode currentMode, OperationMode newMode) {
switch (newMode) {
case BASIC:
if (isBasic(currentMode) == false) {
return new String[] { "Beats will no longer be able to use centrally-managed configuration" };
}
break;
}
Expand All @@ -235,6 +246,10 @@ private static String[] sqlAcknowledgementMessages(OperationMode currentMode, Op
return Strings.EMPTY_ARRAY;
}

private static boolean isBasic(OperationMode mode) {
return mode == OperationMode.BASIC;
}

/** A wrapper for the license mode and state, to allow atomically swapping. */
private static class Status {

Expand Down Expand Up @@ -555,20 +570,17 @@ public synchronized boolean isRollupAllowed() {
*/
public synchronized boolean isLogstashAllowed() {
Status localStatus = status;
return localStatus.active && (isBasic(localStatus.mode) == false);
}

if (localStatus.active == false) {
return false;
}
/**
* Beats is allowed as long as there is an active license of type TRIAL, STANDARD, GOLD or PLATINUM
* @return {@code true} as long as there is a valid license
*/
public boolean isBeatsAllowed() {
Status localStatus = status;
return localStatus.active && (isBasic(localStatus.mode) == false);

switch (localStatus.mode) {
case TRIAL:
case GOLD:
case PLATINUM:
case STANDARD:
return true;
default:
return false;
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.elasticsearch.xpack.core.graph.GraphFeatureSetUsage;
import org.elasticsearch.xpack.core.graph.action.GraphExploreAction;
import org.elasticsearch.xpack.core.logstash.LogstashFeatureSetUsage;
import org.elasticsearch.xpack.core.beats.BeatsFeatureSetUsage;
import org.elasticsearch.xpack.core.ml.MachineLearningFeatureSetUsage;
import org.elasticsearch.xpack.core.ml.MlMetadata;
import org.elasticsearch.xpack.core.ml.action.CloseJobAction;
Expand Down Expand Up @@ -327,6 +328,8 @@ public List<NamedWriteableRegistry.Entry> getNamedWriteables() {
new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.GRAPH, GraphFeatureSetUsage::new),
// logstash
new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.LOGSTASH, LogstashFeatureSetUsage::new),
// beats
new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.BEATS, BeatsFeatureSetUsage::new),
// ML - Custom metadata
new NamedWriteableRegistry.Entry(MetaData.Custom.class, "ml", MlMetadata::new),
new NamedWriteableRegistry.Entry(NamedDiff.class, "ml", MlMetadata.MlMetadataDiff::new),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public final class XPackField {
public static final String MACHINE_LEARNING = "ml";
/** Name constant for the Logstash feature. */
public static final String LOGSTASH = "logstash";
/** Name constant for the Beats feature. */
public static final String BEATS = "beats";
/** Name constant for the Deprecation API feature. */
public static final String DEPRECATION = "deprecation";
/** Name constant for the upgrade feature. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ private XPackSettings() {
public static final Setting<Boolean> LOGSTASH_ENABLED = Setting.boolSetting("xpack.logstash.enabled", true,
Setting.Property.NodeScope);

/** Setting for enabling or disabling Beats extensions. Defaults to true. */
public static final Setting<Boolean> BEATS_ENABLED = Setting.boolSetting("xpack.beats.enabled", true,
Setting.Property.NodeScope);

/** Setting for enabling or disabling TLS. Defaults to false. */
public static final Setting<Boolean> TRANSPORT_SSL_ENABLED = Setting.boolSetting("xpack.security.transport.ssl.enabled", false,
Property.NodeScope);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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.xpack.core.beats;

import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.xpack.core.XPackFeatureSet;
import org.elasticsearch.xpack.core.XPackField;

import java.io.IOException;

public final class BeatsFeatureSetUsage extends XPackFeatureSet.Usage {

public BeatsFeatureSetUsage(StreamInput in) throws IOException {
super(in);
}

public BeatsFeatureSetUsage(boolean available, boolean enabled) {
super(XPackField.BEATS, available, enabled);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,21 @@ private static Map<String, RoleDescriptor> initializeReservedRoles() {
new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder().indices(".kibana*", ".reporting-*").privileges("all").build(),
RoleDescriptor.IndicesPrivileges.builder()
.indices(".monitoring-*").privileges("read", "read_cross_cluster").build()
.indices(".monitoring-*").privileges("read", "read_cross_cluster").build(),
RoleDescriptor.IndicesPrivileges.builder()
.indices(".management-beats").privileges("create_index", "read", "write").build()
},
null,
new ConditionalClusterPrivilege[] { new ManageApplicationPrivileges(Collections.singleton("kibana-*")) },
null, MetadataUtils.DEFAULT_RESERVED_METADATA, null))
.put("logstash_system", new RoleDescriptor("logstash_system", new String[] { "monitor", MonitoringBulkAction.NAME},
null, null, MetadataUtils.DEFAULT_RESERVED_METADATA))
.put("beats_admin", new RoleDescriptor("beats_admin",
null,
new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder().indices(".management-beats").privileges("all").build()
},
null, MetadataUtils.DEFAULT_RESERVED_METADATA))
.put(UsernamesField.BEATS_ROLE, new RoleDescriptor(UsernamesField.BEATS_ROLE,
new String[] { "monitor", MonitoringBulkAction.NAME}, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA))
.put(UsernamesField.APM_ROLE, new RoleDescriptor(UsernamesField.APM_ROLE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ public void testIsReserved() {
assertThat(ReservedRolesStore.isReserved("watcher_user"), is(true));
assertThat(ReservedRolesStore.isReserved("watcher_admin"), is(true));
assertThat(ReservedRolesStore.isReserved("kibana_dashboard_only_user"), is(true));
assertThat(ReservedRolesStore.isReserved("beats_admin"), is(true));
assertThat(ReservedRolesStore.isReserved(XPackUser.ROLE_NAME), is(true));
assertThat(ReservedRolesStore.isReserved(LogstashSystemUser.ROLE_NAME), is(true));
assertThat(ReservedRolesStore.isReserved(BeatsSystemUser.ROLE_NAME), is(true));
Expand Down Expand Up @@ -259,6 +260,20 @@ public void testKibanaSystemRole() {
assertThat(kibanaRole.indices().allowedIndicesMatcher(GetAction.NAME).test(index), is(true));
assertThat(kibanaRole.indices().allowedIndicesMatcher(READ_CROSS_CLUSTER_NAME).test(index), is(true));
});

// Beats management index
final String index = ".management-beats";
assertThat(kibanaRole.indices().allowedIndicesMatcher("indices:foo").test(index), is(false));
assertThat(kibanaRole.indices().allowedIndicesMatcher("indices:bar").test(index), is(false));
assertThat(kibanaRole.indices().allowedIndicesMatcher(DeleteIndexAction.NAME).test(index), is(false));
assertThat(kibanaRole.indices().allowedIndicesMatcher(CreateIndexAction.NAME).test(index), is(true));
assertThat(kibanaRole.indices().allowedIndicesMatcher(IndexAction.NAME).test(index), is(true));
assertThat(kibanaRole.indices().allowedIndicesMatcher(DeleteAction.NAME).test(index), is(true));
assertThat(kibanaRole.indices().allowedIndicesMatcher(UpdateSettingsAction.NAME).test(index), is(false));
assertThat(kibanaRole.indices().allowedIndicesMatcher(SearchAction.NAME).test(index), is(true));
assertThat(kibanaRole.indices().allowedIndicesMatcher(MultiSearchAction.NAME).test(index), is(true));
assertThat(kibanaRole.indices().allowedIndicesMatcher(GetAction.NAME).test(index), is(true));
assertThat(kibanaRole.indices().allowedIndicesMatcher(READ_CROSS_CLUSTER_NAME).test(index), is(false));
}

public void testKibanaUserRole() {
Expand Down Expand Up @@ -555,6 +570,41 @@ public void testLogstashSystemRole() {
is(false));
}

public void testBeatsAdminRole() {
final TransportRequest request = mock(TransportRequest.class);

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

final Role beatsAdminRole = Role.builder(roleDescriptor, null).build();
assertThat(beatsAdminRole.cluster().check(ClusterHealthAction.NAME, request), is(false));
assertThat(beatsAdminRole.cluster().check(ClusterStateAction.NAME, request), is(false));
assertThat(beatsAdminRole.cluster().check(ClusterStatsAction.NAME, request), is(false));
assertThat(beatsAdminRole.cluster().check(PutIndexTemplateAction.NAME, request), is(false));
assertThat(beatsAdminRole.cluster().check(ClusterRerouteAction.NAME, request), is(false));
assertThat(beatsAdminRole.cluster().check(ClusterUpdateSettingsAction.NAME, request), is(false));
assertThat(beatsAdminRole.cluster().check(MonitoringBulkAction.NAME, request), is(false));

assertThat(beatsAdminRole.runAs().check(randomAlphaOfLengthBetween(1, 30)), is(false));

assertThat(beatsAdminRole.indices().allowedIndicesMatcher("indices:foo").test(randomAlphaOfLengthBetween(8, 24)),
is(false));

final String index = ".management-beats";
logger.info("index name [{}]", index);
assertThat(beatsAdminRole.indices().allowedIndicesMatcher("indices:foo").test(index), is(true));
assertThat(beatsAdminRole.indices().allowedIndicesMatcher("indices:bar").test(index), is(true));
assertThat(beatsAdminRole.indices().allowedIndicesMatcher(DeleteIndexAction.NAME).test(index), is(true));
assertThat(beatsAdminRole.indices().allowedIndicesMatcher(CreateIndexAction.NAME).test(index), is(true));
assertThat(beatsAdminRole.indices().allowedIndicesMatcher(IndexAction.NAME).test(index), is(true));
assertThat(beatsAdminRole.indices().allowedIndicesMatcher(DeleteAction.NAME).test(index), is(true));
assertThat(beatsAdminRole.indices().allowedIndicesMatcher(UpdateSettingsAction.NAME).test(index), is(true));
assertThat(beatsAdminRole.indices().allowedIndicesMatcher(SearchAction.NAME).test(index), is(true));
assertThat(beatsAdminRole.indices().allowedIndicesMatcher(MultiSearchAction.NAME).test(index), is(true));
assertThat(beatsAdminRole.indices().allowedIndicesMatcher(GetAction.NAME).test(index), is(true));
}

public void testBeatsSystemRole() {
final TransportRequest request = mock(TransportRequest.class);

Expand Down

0 comments on commit 3ac2b31

Please sign in to comment.