Skip to content

Commit d8cb1f2

Browse files
ywangdtvernum
andauthored
Phase 1 support for operator privileges (#65256) (#65802)
In some Elastic Stack environments, there is a distinction between the operator of the cluster infrastructure and the administrator of the cluster. This distinction cannot be supported currently because the "administrator" often has the superuser role which grants each and every privilege of the cluster. This PR adds a new feature to protect a fixed set of APIs from the "administrator" even when it is a highly privileged user such as superuser. It enhances the Elasticsearch security model to have an additional layer of restriction in addition to the RBAC. Co-authored-by: Tim Vernum <[email protected]>
1 parent 743fcd7 commit d8cb1f2

File tree

31 files changed

+1837
-41
lines changed

31 files changed

+1837
-41
lines changed

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,9 @@ public enum Feature {
104104

105105
AGGREGATE_METRIC(OperationMode.MISSING, true),
106106

107-
SEARCHABLE_SNAPSHOTS(OperationMode.ENTERPRISE, true);
107+
SEARCHABLE_SNAPSHOTS(OperationMode.ENTERPRISE, true),
108+
109+
OPERATOR_PRIVILEGES(OperationMode.ENTERPRISE, true);
108110

109111
final OperationMode minimumOperationMode;
110112
final boolean needsActive;

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

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ public final class XPackField {
6565
public static final String DATA_TIERS = "data_tiers";
6666
/** Name constant for the aggregate_metric plugin. */
6767
public static final String AGGREGATE_METRIC = "aggregate_metric";
68+
/** Name constant for the operator privileges feature. */
69+
public static final String OPERATOR_PRIVILEGES = "operator_privileges";
6870

6971
private XPackField() {}
7072

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

+11-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class SecurityFeatureSetUsage extends XPackFeatureSet.Usage {
2828
private static final String IP_FILTER_XFIELD = "ipfilter";
2929
private static final String ANONYMOUS_XFIELD = "anonymous";
3030
private static final String FIPS_140_XFIELD = "fips_140";
31+
private static final String OPERATOR_PRIVILEGES_XFIELD = XPackField.OPERATOR_PRIVILEGES;
3132

3233
private Map<String, Object> realmsUsage;
3334
private Map<String, Object> rolesStoreUsage;
@@ -39,6 +40,7 @@ public class SecurityFeatureSetUsage extends XPackFeatureSet.Usage {
3940
private Map<String, Object> anonymousUsage;
4041
private Map<String, Object> roleMappingStoreUsage;
4142
private Map<String, Object> fips140Usage;
43+
private Map<String, Object> operatorPrivilegesUsage;
4244

4345
public SecurityFeatureSetUsage(StreamInput in) throws IOException {
4446
super(in);
@@ -60,14 +62,17 @@ public SecurityFeatureSetUsage(StreamInput in) throws IOException {
6062
if (in.getVersion().onOrAfter(Version.V_7_5_0)) {
6163
fips140Usage = in.readMap();
6264
}
65+
if (in.getVersion().onOrAfter(Version.V_7_11_0)) {
66+
operatorPrivilegesUsage = in.readMap();
67+
}
6368
}
6469

6570
public SecurityFeatureSetUsage(boolean available, boolean enabled, Map<String, Object> realmsUsage,
6671
Map<String, Object> rolesStoreUsage, Map<String, Object> roleMappingStoreUsage,
6772
Map<String, Object> sslUsage, Map<String, Object> auditUsage,
6873
Map<String, Object> ipFilterUsage, Map<String, Object> anonymousUsage,
6974
Map<String, Object> tokenServiceUsage, Map<String, Object> apiKeyServiceUsage,
70-
Map<String, Object> fips140Usage) {
75+
Map<String, Object> fips140Usage, Map<String, Object> operatorPrivilegesUsage) {
7176
super(XPackField.SECURITY, available, enabled);
7277
this.realmsUsage = realmsUsage;
7378
this.rolesStoreUsage = rolesStoreUsage;
@@ -79,6 +84,7 @@ public SecurityFeatureSetUsage(boolean available, boolean enabled, Map<String, O
7984
this.ipFilterUsage = ipFilterUsage;
8085
this.anonymousUsage = anonymousUsage;
8186
this.fips140Usage = fips140Usage;
87+
this.operatorPrivilegesUsage = operatorPrivilegesUsage;
8288
}
8389

8490
@Override
@@ -107,6 +113,9 @@ public void writeTo(StreamOutput out) throws IOException {
107113
if (out.getVersion().onOrAfter(Version.V_7_5_0)) {
108114
out.writeMap(fips140Usage);
109115
}
116+
if (out.getVersion().onOrAfter(Version.V_7_11_0)) {
117+
out.writeMap(operatorPrivilegesUsage);
118+
}
110119
}
111120

112121
@Override
@@ -123,6 +132,7 @@ protected void innerXContent(XContentBuilder builder, Params params) throws IOEx
123132
builder.field(IP_FILTER_XFIELD, ipFilterUsage);
124133
builder.field(ANONYMOUS_XFIELD, anonymousUsage);
125134
builder.field(FIPS_140_XFIELD, fips140Usage);
135+
builder.field(OPERATOR_PRIVILEGES_XFIELD, operatorPrivilegesUsage);
126136
} else if (sslUsage.isEmpty() == false) {
127137
// A trial (or basic) license can have SSL without security.
128138
// This is because security defaults to disabled on that license, but that dynamic-default does not disable SSL.

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/AuthenticationField.java

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ public final class AuthenticationField {
1010
public static final String AUTHENTICATION_KEY = "_xpack_security_authentication";
1111
public static final String API_KEY_ROLE_DESCRIPTORS_KEY = "_security_api_key_role_descriptors";
1212
public static final String API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY = "_security_api_key_limited_by_role_descriptors";
13+
public static final String PRIVILEGE_CATEGORY_KEY = "_security_privilege_category";
14+
public static final String PRIVILEGE_CATEGORY_VALUE_OPERATOR = "operator";
1315

1416
private AuthenticationField() {}
1517
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
apply plugin: 'elasticsearch.esplugin'
2+
apply plugin: 'elasticsearch.java-rest-test'
3+
4+
esplugin {
5+
name 'operator-privileges-test'
6+
description 'An test plugin for testing hard to get internals'
7+
classname 'org.elasticsearch.xpack.security.operator.OperatorPrivilegesTestPlugin'
8+
}
9+
10+
dependencies {
11+
compileOnly project(':x-pack:plugin:core')
12+
javaRestTestImplementation project(':x-pack:plugin:core')
13+
javaRestTestImplementation project(':client:rest-high-level')
14+
javaRestTestImplementation project(':x-pack:plugin:security')
15+
// let the javaRestTest see the classpath of main
16+
javaRestTestImplementation project.sourceSets.main.runtimeClasspath
17+
}
18+
19+
testClusters.all {
20+
testDistribution = 'DEFAULT'
21+
numberOfNodes = 3
22+
23+
extraConfigFile 'operator_users.yml', file('src/javaRestTest/resources/operator_users.yml')
24+
extraConfigFile 'roles.yml', file('src/javaRestTest/resources/roles.yml')
25+
26+
setting 'xpack.license.self_generated.type', 'trial'
27+
setting 'xpack.security.enabled', 'true'
28+
setting 'xpack.security.http.ssl.enabled', 'false'
29+
setting 'xpack.security.operator_privileges.enabled', "true"
30+
31+
user username: "test_admin", password: 'x-pack-test-password', role: "superuser"
32+
user username: "test_operator", password: 'x-pack-test-password', role: "limited_operator"
33+
}

0 commit comments

Comments
 (0)