Skip to content

Commit 59e513b

Browse files
committed
[X-Pack] Beats centralized management: security role + licensing (elastic#30520)
* Adding Beats x-pack plugin + index templates * Adding built-in roles for Beats central management * Fixing typo * Refactoring: extract common code into method * More refactoring for more code reuse * Use a single index for Beats management * Rename "fragment" to "block" * Adding configuration block type * Expand kibana_system role to include Beats management index privileges * Fixing syntax * Adding test * Adding asserting for reserved role * Fixing privileges * Updating template * Removing beats plugin * Fixing tests * Fixing role variable name * Fixing assertions * Switching to preferred syntax for boolean false checks * Making class final * Making variables final * Updating Basic license message to be more accurate
1 parent e83ad9a commit 59e513b

File tree

7 files changed

+120
-19
lines changed

7 files changed

+120
-19
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/license/XPackLicenseState.java

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ public class XPackLicenseState {
5555
messages.put(XPackField.LOGSTASH, new String[] {
5656
"Logstash will continue to poll centrally-managed pipelines"
5757
});
58+
messages.put(XPackField.BEATS, new String[] {
59+
"Beats will continue to poll centrally-managed configuration"
60+
});
5861
messages.put(XPackField.DEPRECATION, new String[] {
5962
"Deprecation APIs are disabled"
6063
});
@@ -84,6 +87,7 @@ public class XPackLicenseState {
8487
messages.put(XPackField.GRAPH, XPackLicenseState::graphAcknowledgementMessages);
8588
messages.put(XPackField.MACHINE_LEARNING, XPackLicenseState::machineLearningAcknowledgementMessages);
8689
messages.put(XPackField.LOGSTASH, XPackLicenseState::logstashAcknowledgementMessages);
90+
messages.put(XPackField.BEATS, XPackLicenseState::beatsAcknowledgementMessages);
8791
messages.put(XPackField.SQL, XPackLicenseState::sqlAcknowledgementMessages);
8892
ACKNOWLEDGMENT_MESSAGES = Collections.unmodifiableMap(messages);
8993
}
@@ -208,12 +212,19 @@ private static String[] machineLearningAcknowledgementMessages(OperationMode cur
208212
private static String[] logstashAcknowledgementMessages(OperationMode currentMode, OperationMode newMode) {
209213
switch (newMode) {
210214
case BASIC:
211-
switch (currentMode) {
212-
case TRIAL:
213-
case STANDARD:
214-
case GOLD:
215-
case PLATINUM:
216-
return new String[] { "Logstash will no longer poll for centrally-managed pipelines" };
215+
if (isBasic(currentMode) == false) {
216+
return new String[] { "Logstash will no longer poll for centrally-managed pipelines" };
217+
}
218+
break;
219+
}
220+
return Strings.EMPTY_ARRAY;
221+
}
222+
223+
private static String[] beatsAcknowledgementMessages(OperationMode currentMode, OperationMode newMode) {
224+
switch (newMode) {
225+
case BASIC:
226+
if (isBasic(currentMode) == false) {
227+
return new String[] { "Beats will no longer be able to use centrally-managed configuration" };
217228
}
218229
break;
219230
}
@@ -235,6 +246,10 @@ private static String[] sqlAcknowledgementMessages(OperationMode currentMode, Op
235246
return Strings.EMPTY_ARRAY;
236247
}
237248

249+
private static boolean isBasic(OperationMode mode) {
250+
return mode == OperationMode.BASIC;
251+
}
252+
238253
/** A wrapper for the license mode and state, to allow atomically swapping. */
239254
private static class Status {
240255

@@ -555,20 +570,17 @@ public synchronized boolean isRollupAllowed() {
555570
*/
556571
public synchronized boolean isLogstashAllowed() {
557572
Status localStatus = status;
573+
return localStatus.active && (isBasic(localStatus.mode) == false);
574+
}
558575

559-
if (localStatus.active == false) {
560-
return false;
561-
}
576+
/**
577+
* Beats is allowed as long as there is an active license of type TRIAL, STANDARD, GOLD or PLATINUM
578+
* @return {@code true} as long as there is a valid license
579+
*/
580+
public boolean isBeatsAllowed() {
581+
Status localStatus = status;
582+
return localStatus.active && (isBasic(localStatus.mode) == false);
562583

563-
switch (localStatus.mode) {
564-
case TRIAL:
565-
case GOLD:
566-
case PLATINUM:
567-
case STANDARD:
568-
return true;
569-
default:
570-
return false;
571-
}
572584
}
573585

574586
/**

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.elasticsearch.xpack.core.graph.GraphFeatureSetUsage;
4343
import org.elasticsearch.xpack.core.graph.action.GraphExploreAction;
4444
import org.elasticsearch.xpack.core.logstash.LogstashFeatureSetUsage;
45+
import org.elasticsearch.xpack.core.beats.BeatsFeatureSetUsage;
4546
import org.elasticsearch.xpack.core.ml.MachineLearningFeatureSetUsage;
4647
import org.elasticsearch.xpack.core.ml.MlMetadata;
4748
import org.elasticsearch.xpack.core.ml.action.CloseJobAction;
@@ -327,6 +328,8 @@ public List<NamedWriteableRegistry.Entry> getNamedWriteables() {
327328
new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.GRAPH, GraphFeatureSetUsage::new),
328329
// logstash
329330
new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.LOGSTASH, LogstashFeatureSetUsage::new),
331+
// beats
332+
new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.BEATS, BeatsFeatureSetUsage::new),
330333
// ML - Custom metadata
331334
new NamedWriteableRegistry.Entry(MetaData.Custom.class, "ml", MlMetadata::new),
332335
new NamedWriteableRegistry.Entry(NamedDiff.class, "ml", MlMetadata.MlMetadataDiff::new),

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackField.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ public final class XPackField {
1919
public static final String MACHINE_LEARNING = "ml";
2020
/** Name constant for the Logstash feature. */
2121
public static final String LOGSTASH = "logstash";
22+
/** Name constant for the Beats feature. */
23+
public static final String BEATS = "beats";
2224
/** Name constant for the Deprecation API feature. */
2325
public static final String DEPRECATION = "deprecation";
2426
/** Name constant for the upgrade feature. */

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackSettings.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ private XPackSettings() {
7777
public static final Setting<Boolean> LOGSTASH_ENABLED = Setting.boolSetting("xpack.logstash.enabled", true,
7878
Setting.Property.NodeScope);
7979

80+
/** Setting for enabling or disabling Beats extensions. Defaults to true. */
81+
public static final Setting<Boolean> BEATS_ENABLED = Setting.boolSetting("xpack.beats.enabled", true,
82+
Setting.Property.NodeScope);
83+
8084
/** Setting for enabling or disabling TLS. Defaults to false. */
8185
public static final Setting<Boolean> TRANSPORT_SSL_ENABLED = Setting.boolSetting("xpack.security.transport.ssl.enabled", false,
8286
Property.NodeScope);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
package org.elasticsearch.xpack.core.beats;
7+
8+
import org.elasticsearch.common.io.stream.StreamInput;
9+
import org.elasticsearch.xpack.core.XPackFeatureSet;
10+
import org.elasticsearch.xpack.core.XPackField;
11+
12+
import java.io.IOException;
13+
14+
public final class BeatsFeatureSetUsage extends XPackFeatureSet.Usage {
15+
16+
public BeatsFeatureSetUsage(StreamInput in) throws IOException {
17+
super(in);
18+
}
19+
20+
public BeatsFeatureSetUsage(boolean available, boolean enabled) {
21+
super(XPackField.BEATS, available, enabled);
22+
}
23+
24+
}

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,21 @@ private static Map<String, RoleDescriptor> initializeReservedRoles() {
9595
new RoleDescriptor.IndicesPrivileges[] {
9696
RoleDescriptor.IndicesPrivileges.builder().indices(".kibana*", ".reporting-*").privileges("all").build(),
9797
RoleDescriptor.IndicesPrivileges.builder()
98-
.indices(".monitoring-*").privileges("read", "read_cross_cluster").build()
98+
.indices(".monitoring-*").privileges("read", "read_cross_cluster").build(),
99+
RoleDescriptor.IndicesPrivileges.builder()
100+
.indices(".management-beats").privileges("create_index", "read", "write").build()
99101
},
100102
null,
101103
new ConditionalClusterPrivilege[] { new ManageApplicationPrivileges(Collections.singleton("kibana-*")) },
102104
null, MetadataUtils.DEFAULT_RESERVED_METADATA, null))
103105
.put("logstash_system", new RoleDescriptor("logstash_system", new String[] { "monitor", MonitoringBulkAction.NAME},
104106
null, null, MetadataUtils.DEFAULT_RESERVED_METADATA))
107+
.put("beats_admin", new RoleDescriptor("beats_admin",
108+
null,
109+
new RoleDescriptor.IndicesPrivileges[] {
110+
RoleDescriptor.IndicesPrivileges.builder().indices(".management-beats").privileges("all").build()
111+
},
112+
null, MetadataUtils.DEFAULT_RESERVED_METADATA))
105113
.put(UsernamesField.BEATS_ROLE, new RoleDescriptor(UsernamesField.BEATS_ROLE,
106114
new String[] { "monitor", MonitoringBulkAction.NAME}, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA))
107115
.put(UsernamesField.APM_ROLE, new RoleDescriptor(UsernamesField.APM_ROLE,

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ public void testIsReserved() {
144144
assertThat(ReservedRolesStore.isReserved("watcher_user"), is(true));
145145
assertThat(ReservedRolesStore.isReserved("watcher_admin"), is(true));
146146
assertThat(ReservedRolesStore.isReserved("kibana_dashboard_only_user"), is(true));
147+
assertThat(ReservedRolesStore.isReserved("beats_admin"), is(true));
147148
assertThat(ReservedRolesStore.isReserved(XPackUser.ROLE_NAME), is(true));
148149
assertThat(ReservedRolesStore.isReserved(LogstashSystemUser.ROLE_NAME), is(true));
149150
assertThat(ReservedRolesStore.isReserved(BeatsSystemUser.ROLE_NAME), is(true));
@@ -259,6 +260,20 @@ public void testKibanaSystemRole() {
259260
assertThat(kibanaRole.indices().allowedIndicesMatcher(GetAction.NAME).test(index), is(true));
260261
assertThat(kibanaRole.indices().allowedIndicesMatcher(READ_CROSS_CLUSTER_NAME).test(index), is(true));
261262
});
263+
264+
// Beats management index
265+
final String index = ".management-beats";
266+
assertThat(kibanaRole.indices().allowedIndicesMatcher("indices:foo").test(index), is(false));
267+
assertThat(kibanaRole.indices().allowedIndicesMatcher("indices:bar").test(index), is(false));
268+
assertThat(kibanaRole.indices().allowedIndicesMatcher(DeleteIndexAction.NAME).test(index), is(false));
269+
assertThat(kibanaRole.indices().allowedIndicesMatcher(CreateIndexAction.NAME).test(index), is(true));
270+
assertThat(kibanaRole.indices().allowedIndicesMatcher(IndexAction.NAME).test(index), is(true));
271+
assertThat(kibanaRole.indices().allowedIndicesMatcher(DeleteAction.NAME).test(index), is(true));
272+
assertThat(kibanaRole.indices().allowedIndicesMatcher(UpdateSettingsAction.NAME).test(index), is(false));
273+
assertThat(kibanaRole.indices().allowedIndicesMatcher(SearchAction.NAME).test(index), is(true));
274+
assertThat(kibanaRole.indices().allowedIndicesMatcher(MultiSearchAction.NAME).test(index), is(true));
275+
assertThat(kibanaRole.indices().allowedIndicesMatcher(GetAction.NAME).test(index), is(true));
276+
assertThat(kibanaRole.indices().allowedIndicesMatcher(READ_CROSS_CLUSTER_NAME).test(index), is(false));
262277
}
263278

264279
public void testKibanaUserRole() {
@@ -555,6 +570,39 @@ public void testLogstashSystemRole() {
555570
is(false));
556571
}
557572

573+
public void testBeatsAdminRole() {
574+
final RoleDescriptor roleDescriptor = new ReservedRolesStore().roleDescriptor("beats_admin");
575+
assertNotNull(roleDescriptor);
576+
assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true));
577+
578+
final Role beatsAdminRole = Role.builder(roleDescriptor, null).build();
579+
assertThat(beatsAdminRole.cluster().check(ClusterHealthAction.NAME), is(false));
580+
assertThat(beatsAdminRole.cluster().check(ClusterStateAction.NAME), is(false));
581+
assertThat(beatsAdminRole.cluster().check(ClusterStatsAction.NAME), is(false));
582+
assertThat(beatsAdminRole.cluster().check(PutIndexTemplateAction.NAME), is(false));
583+
assertThat(beatsAdminRole.cluster().check(ClusterRerouteAction.NAME), is(false));
584+
assertThat(beatsAdminRole.cluster().check(ClusterUpdateSettingsAction.NAME), is(false));
585+
assertThat(beatsAdminRole.cluster().check(MonitoringBulkAction.NAME), is(false));
586+
587+
assertThat(beatsAdminRole.runAs().check(randomAlphaOfLengthBetween(1, 30)), is(false));
588+
589+
assertThat(beatsAdminRole.indices().allowedIndicesMatcher("indices:foo").test(randomAlphaOfLengthBetween(8, 24)),
590+
is(false));
591+
592+
final String index = ".management-beats";
593+
logger.info("index name [{}]", index);
594+
assertThat(beatsAdminRole.indices().allowedIndicesMatcher("indices:foo").test(index), is(true));
595+
assertThat(beatsAdminRole.indices().allowedIndicesMatcher("indices:bar").test(index), is(true));
596+
assertThat(beatsAdminRole.indices().allowedIndicesMatcher(DeleteIndexAction.NAME).test(index), is(true));
597+
assertThat(beatsAdminRole.indices().allowedIndicesMatcher(CreateIndexAction.NAME).test(index), is(true));
598+
assertThat(beatsAdminRole.indices().allowedIndicesMatcher(IndexAction.NAME).test(index), is(true));
599+
assertThat(beatsAdminRole.indices().allowedIndicesMatcher(DeleteAction.NAME).test(index), is(true));
600+
assertThat(beatsAdminRole.indices().allowedIndicesMatcher(UpdateSettingsAction.NAME).test(index), is(true));
601+
assertThat(beatsAdminRole.indices().allowedIndicesMatcher(SearchAction.NAME).test(index), is(true));
602+
assertThat(beatsAdminRole.indices().allowedIndicesMatcher(MultiSearchAction.NAME).test(index), is(true));
603+
assertThat(beatsAdminRole.indices().allowedIndicesMatcher(GetAction.NAME).test(index), is(true));
604+
}
605+
558606
public void testBeatsSystemRole() {
559607
final TransportRequest request = mock(TransportRequest.class);
560608

0 commit comments

Comments
 (0)