Skip to content

Commit e045d2f

Browse files
TADAAA
1 parent 4daa9fd commit e045d2f

File tree

5 files changed

+192
-4
lines changed

5 files changed

+192
-4
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilege.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package org.elasticsearch.xpack.core.security.authz.privilege;
77

88
import org.apache.lucene.util.automaton.Automaton;
9+
import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesAction;
910
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotAction;
1011
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsAction;
1112
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusAction;
@@ -52,7 +53,7 @@ public final class ClusterPrivilege extends Privilege {
5253
private static final Automaton MANAGE_CCR_AUTOMATON =
5354
patterns("cluster:admin/xpack/ccr/*", ClusterStateAction.NAME, HasPrivilegesAction.NAME);
5455
private static final Automaton CREATE_SNAPSHOT_AUTOMATON = patterns(CreateSnapshotAction.NAME, SnapshotsStatusAction.NAME,
55-
GetSnapshotsAction.NAME);
56+
GetSnapshotsAction.NAME, GetRepositoriesAction.NAME);
5657
private static final Automaton READ_CCR_AUTOMATON = patterns(ClusterStateAction.NAME, HasPrivilegesAction.NAME);
5758
private static final Automaton MANAGE_ILM_AUTOMATON = patterns("cluster:admin/ilm/*");
5859
private static final Automaton READ_ILM_AUTOMATON = patterns(GetLifecycleAction.NAME, GetStatusAction.NAME);

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/IndexPrivilege.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ public final class IndexPrivilege extends Privilege {
6464
CloseIndexAction.NAME + "*");
6565
private static final Automaton MANAGE_ILM_AUTOMATON = patterns("indices:admin/ilm/*");
6666

67-
public static final IndexPrivilege NONE = new IndexPrivilege("none", Automatons.EMPTY);
68-
public static final IndexPrivilege ALL = new IndexPrivilege("all", ALL_AUTOMATON);
67+
public static final IndexPrivilege NONE = new IndexPrivilege("none", Automatons.EMPTY);
68+
public static final IndexPrivilege ALL = new IndexPrivilege("all", ALL_AUTOMATON);
6969
public static final IndexPrivilege READ = new IndexPrivilege("read", READ_AUTOMATON);
7070
public static final IndexPrivilege READ_CROSS_CLUSTER = new IndexPrivilege("read_cross_cluster", READ_CROSS_CLUSTER_AUTOMATON);
7171
public static final IndexPrivilege CREATE = new IndexPrivilege("create", CREATE_AUTOMATON);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ private static Map<String, RoleDescriptor> initializeReservedRoles() {
174174
.put("snapshot_user", new RoleDescriptor("snapshot_user", new String[] { "create_snapshot", GetRepositoriesAction.NAME },
175175
new RoleDescriptor.IndicesPrivileges[] { RoleDescriptor.IndicesPrivileges.builder()
176176
.indices("*")
177-
.privileges(GetIndexAction.NAME)
177+
.privileges("view_index_metadata")
178178
.allowRestrictedIndices(true)
179179
.build() }, null, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA, null))
180180
.immutableMap();

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import org.elasticsearch.Version;
99
import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction;
1010
import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesAction;
11+
import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryAction;
1112
import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteAction;
1213
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsAction;
1314
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotAction;
@@ -178,6 +179,7 @@ public void testSnapshotUserRole() {
178179
assertThat(snapshotUserRole.cluster().check(SnapshotsStatusAction.NAME, request), is(true));
179180
assertThat(snapshotUserRole.cluster().check(GetSnapshotsAction.NAME, request), is(true));
180181

182+
assertThat(snapshotUserRole.cluster().check(PutRepositoryAction.NAME, request), is(false));
181183
assertThat(snapshotUserRole.cluster().check(GetIndexTemplatesAction.NAME, request), is(false));
182184
assertThat(snapshotUserRole.cluster().check(DeleteIndexTemplateAction.NAME, request), is(false));
183185
assertThat(snapshotUserRole.cluster().check(PutPipelineAction.NAME, request), is(false));
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
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+
7+
package org.elasticsearch.xpack.security.authz;
8+
9+
import org.elasticsearch.ElasticsearchSecurityException;
10+
import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesResponse;
11+
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
12+
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsResponse;
13+
import org.elasticsearch.action.admin.indices.get.GetIndexResponse;
14+
import org.elasticsearch.common.settings.SecureString;
15+
import org.elasticsearch.common.settings.Settings;
16+
import org.elasticsearch.rest.RestStatus;
17+
import org.elasticsearch.snapshots.SnapshotState;
18+
import org.elasticsearch.test.NativeRealmIntegTestCase;
19+
import org.elasticsearch.xpack.core.security.authc.support.Hasher;
20+
import org.junit.Before;
21+
22+
import java.util.Arrays;
23+
import java.util.Collections;
24+
import java.util.Locale;
25+
26+
import static org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames.INTERNAL_SECURITY_INDEX;
27+
import static org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames.SECURITY_INDEX_NAME;
28+
29+
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
30+
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
31+
import static org.hamcrest.Matchers.is;
32+
import static org.hamcrest.Matchers.containsString;
33+
import static org.hamcrest.Matchers.contains;
34+
import static org.hamcrest.Matchers.containsInAnyOrder;
35+
36+
public class SnapshotUserRoleIT extends NativeRealmIntegTestCase {
37+
38+
private String snapshotUserToken;
39+
private String ordinaryIndex;
40+
41+
@Before
42+
public void setupClusterBeforeSnapshot() {
43+
logger.info("--> creating repository");
44+
assertAcked(client().admin().cluster().preparePutRepository("repo")
45+
.setType("fs")
46+
.setSettings(Settings.builder().put("location", randomRepoPath())));
47+
48+
logger.info("--> creating ordinary index");
49+
final int shards = between(1, 10);
50+
ordinaryIndex = randomAlphaOfLength(4).toLowerCase(Locale.ROOT);
51+
assertAcked(prepareCreate(ordinaryIndex, 0, Settings.builder().put("number_of_shards", shards).put("number_of_replicas", 0)));
52+
ensureGreen();
53+
54+
logger.info("--> creating snapshot_user user");
55+
final String user = "snapshot_user";
56+
final char[] password = new char[] {'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
57+
snapshotUserToken = basicAuthHeaderValue(user, new SecureString(password));
58+
securityClient().preparePutUser(user, password, Hasher.BCRYPT, "snapshot_user").get();
59+
ensureGreen(INTERNAL_SECURITY_INDEX);
60+
}
61+
62+
public void testSnapshotUserRoleCanSnapshotAndSeeAllIndices() {
63+
// view repositories
64+
final GetRepositoriesResponse getRepositoriesResponse = client()
65+
.filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().cluster()
66+
.prepareGetRepositories(randomFrom("*", "_all")).get();
67+
assertThat(getRepositoriesResponse.repositories().size(), is(1));
68+
assertThat(getRepositoriesResponse.repositories().get(0).name(), is("repo"));
69+
// view all indices, including restricted ones
70+
final GetIndexResponse getIndexResponse = client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken))
71+
.admin().indices().prepareGetIndex().setIndices(randomFrom("_all", "*")).get();
72+
assertThat(Arrays.asList(getIndexResponse.indices()), containsInAnyOrder(INTERNAL_SECURITY_INDEX, ordinaryIndex));
73+
// create snapshot that includes restricted indices
74+
final CreateSnapshotResponse snapshotResponse = client()
75+
.filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().cluster()
76+
.prepareCreateSnapshot("repo", "snap").setIndices(randomFrom("_all", "*")).setWaitForCompletion(true).get();
77+
assertThat(snapshotResponse.getSnapshotInfo().state(), is(SnapshotState.SUCCESS));
78+
assertThat(snapshotResponse.getSnapshotInfo().indices(), containsInAnyOrder(INTERNAL_SECURITY_INDEX, ordinaryIndex));
79+
// view snapshots for repo
80+
final GetSnapshotsResponse getSnapshotResponse = client()
81+
.filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().cluster()
82+
.prepareGetSnapshots("repo").get();
83+
assertThat(getSnapshotResponse.getSnapshots().size(), is(1));
84+
assertThat(getSnapshotResponse.getSnapshots().get(0).snapshotId().getName(), is("snap"));
85+
assertThat(getSnapshotResponse.getSnapshots().get(0).indices(), containsInAnyOrder(INTERNAL_SECURITY_INDEX, ordinaryIndex));
86+
}
87+
88+
public void testSnapshotUserRoleIsReserved() {
89+
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
90+
() -> securityClient().preparePutRole("snapshot_user").get());
91+
assertThat(e.getMessage(), containsString("role [snapshot_user] is reserved and cannot be modified"));
92+
e = expectThrows(IllegalArgumentException.class,
93+
() -> securityClient().prepareDeleteRole("snapshot_user").get());
94+
assertThat(e.getMessage(), containsString("role [snapshot_user] is reserved and cannot be deleted"));
95+
}
96+
97+
public void testSnapshotUserRoleUnathorizedForDestructiveActions() {
98+
// try search all
99+
ElasticsearchSecurityException unauthzException = expectThrows(ElasticsearchSecurityException.class,
100+
() -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken))
101+
.prepareSearch(randomFrom("_all", "*")).get());
102+
assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN));
103+
assertThat(unauthzException.getDetailedMessage(),
104+
containsString("action [indices:data/read/search] is unauthorized for user [snapshot_user]"));
105+
// try create index
106+
unauthzException = expectThrows(ElasticsearchSecurityException.class,
107+
() -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().indices().prepareCreate(ordinaryIndex + "2").get());
108+
assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN));
109+
assertThat(unauthzException.getDetailedMessage(),
110+
containsString("action [indices:admin/create] is unauthorized for user [snapshot_user]"));
111+
// try create another repo
112+
unauthzException = expectThrows(ElasticsearchSecurityException.class, () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken))
113+
.admin().cluster().preparePutRepository("some_other_repo").setType("fs").setSettings(Settings.builder().put("location", randomRepoPath())).get());
114+
assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN));
115+
assertThat(unauthzException.getDetailedMessage(),
116+
containsString("action [cluster:admin/repository/put] is unauthorized for user [snapshot_user]"));
117+
// try delete repo
118+
unauthzException = expectThrows(ElasticsearchSecurityException.class,
119+
() -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().cluster()
120+
.prepareDeleteRepository("repo").get());
121+
assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN));
122+
assertThat(unauthzException.getDetailedMessage(),
123+
containsString("action [cluster:admin/repository/delete] is unauthorized for user [snapshot_user]"));
124+
// try fumble with snapshots
125+
unauthzException = expectThrows(ElasticsearchSecurityException.class, () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken))
126+
.admin().cluster().prepareRestoreSnapshot("repo", randomAlphaOfLength(4).toLowerCase(Locale.ROOT)).get());
127+
assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN));
128+
assertThat(unauthzException.getDetailedMessage(),
129+
containsString("action [cluster:admin/snapshot/restore] is unauthorized for user [snapshot_user]"));
130+
unauthzException = expectThrows(ElasticsearchSecurityException.class, () -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken))
131+
.admin().cluster().prepareDeleteSnapshot("repo", randomAlphaOfLength(4).toLowerCase(Locale.ROOT)).get());
132+
assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN));
133+
assertThat(unauthzException.getDetailedMessage(),
134+
containsString("action [cluster:admin/snapshot/delete] is unauthorized for user [snapshot_user]"));
135+
// try destructive actions on all indices are unauthorized
136+
for (final String indexToTest : Arrays.asList(INTERNAL_SECURITY_INDEX, SECURITY_INDEX_NAME, ordinaryIndex)) {
137+
unauthzException = expectThrows(ElasticsearchSecurityException.class,
138+
() -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).prepareSearch(indexToTest).get());
139+
assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN));
140+
assertThat(unauthzException.getDetailedMessage(),
141+
containsString("action [indices:data/read/search] is unauthorized for user [snapshot_user]"));
142+
143+
unauthzException = expectThrows(ElasticsearchSecurityException.class,
144+
() -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).prepareGet(indexToTest, "doc", "1").get());
145+
assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN));
146+
assertThat(unauthzException.getDetailedMessage(),
147+
containsString("action [indices:data/read/get] is unauthorized for user [snapshot_user]"));
148+
149+
unauthzException = expectThrows(ElasticsearchSecurityException.class,
150+
() -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).prepareIndex(indexToTest, "doc").setSource("term", "val").get());
151+
assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN));
152+
assertThat(unauthzException.getDetailedMessage(),
153+
containsString("action [indices:data/write/index] is unauthorized for user [snapshot_user]"));
154+
155+
unauthzException = expectThrows(ElasticsearchSecurityException.class,
156+
() -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).prepareUpdate(indexToTest, "doc", "1").setDoc("term", "val").get());
157+
assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN));
158+
assertThat(unauthzException.getDetailedMessage(),
159+
containsString("action [indices:data/write/update] is unauthorized for user [snapshot_user]"));
160+
161+
unauthzException = expectThrows(ElasticsearchSecurityException.class,
162+
() -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).prepareDelete(indexToTest, "doc", "1").get());
163+
assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN));
164+
assertThat(unauthzException.getDetailedMessage(),
165+
containsString("action [indices:data/write/delete] is unauthorized for user [snapshot_user]"));
166+
167+
unauthzException = expectThrows(ElasticsearchSecurityException.class,
168+
() -> client().filterWithHeader(Collections.singletonMap("Authorization", snapshotUserToken)).admin().indices().prepareDelete(indexToTest).get());
169+
assertThat(unauthzException.status(), is(RestStatus.FORBIDDEN));
170+
assertThat(unauthzException.getDetailedMessage(),
171+
containsString("action [indices:admin/delete] is unauthorized for user [snapshot_user]"));
172+
}
173+
}
174+
175+
// create repo
176+
// create another index
177+
// create user with snapshot_user role
178+
179+
// test user can NOT get, search, index, change settings
180+
// test user can NOT put repositories
181+
// test user can NOT restore snapshot
182+
// test user can list all indices (including .security)
183+
// test user can snapshot all indices (including .security)
184+
185+
}

0 commit comments

Comments
 (0)