Separated the `elastic` user from the internal xpack user

Also,

- changed the anonymous username to `_anonymous` (used to be `__es_anonymous_user` which I found needlessly, overly, redundantly and not to mention unnecessarily complex 🤷)
- changed the system username and role name to `_system` (used to be `__es_system_user` and `__es_system_role`... it introduced gratuitous and totally un-called for naming complexity 🤦)

Closes elastic/elasticsearch#2079

Original commit: elastic/x-pack-elasticsearch@63b6de2bba
This commit is contained in:
uboness 2016-05-09 13:45:27 +02:00
parent 1f6b401b9d
commit 12102f433d
15 changed files with 111 additions and 48 deletions

View File

@ -18,7 +18,7 @@ import org.elasticsearch.shield.support.Exceptions;
import org.elasticsearch.shield.user.AnonymousUser;
import org.elasticsearch.shield.user.KibanaUser;
import org.elasticsearch.shield.user.User;
import org.elasticsearch.shield.user.XPackUser;
import org.elasticsearch.shield.user.ElasticUser;
import java.util.Arrays;
import java.util.Collection;
@ -86,7 +86,7 @@ public class ReservedRealm extends CachingUsernamePasswordRealm {
public static boolean isReserved(String username) {
assert username != null;
switch (username) {
case XPackUser.NAME:
case ElasticUser.NAME:
case KibanaUser.NAME:
return true;
default:
@ -97,8 +97,8 @@ public class ReservedRealm extends CachingUsernamePasswordRealm {
public static User getUser(String username) {
assert username != null;
switch (username) {
case XPackUser.NAME:
return XPackUser.INSTANCE;
case ElasticUser.NAME:
return ElasticUser.INSTANCE;
case KibanaUser.NAME:
return KibanaUser.INSTANCE;
default:
@ -111,9 +111,9 @@ public class ReservedRealm extends CachingUsernamePasswordRealm {
public static Collection<User> users() {
if (AnonymousUser.enabled()) {
return Arrays.asList(XPackUser.INSTANCE, KibanaUser.INSTANCE, AnonymousUser.INSTANCE);
return Arrays.asList(ElasticUser.INSTANCE, KibanaUser.INSTANCE, AnonymousUser.INSTANCE);
}
return Arrays.asList(XPackUser.INSTANCE, KibanaUser.INSTANCE);
return Arrays.asList(ElasticUser.INSTANCE, KibanaUser.INSTANCE);
}
private char[] getPasswordHash(final String username) {

View File

@ -25,7 +25,7 @@ import static org.elasticsearch.shield.Security.setting;
*/
public class AnonymousUser extends ReservedUser {
public static final String DEFAULT_ANONYMOUS_USERNAME = "_es_anonymous_user";
public static final String DEFAULT_ANONYMOUS_USERNAME = "_anonymous";
public static final Setting<String> USERNAME_SETTING =
new Setting<>(setting("authc.anonymous.username"), DEFAULT_ANONYMOUS_USERNAME, s -> s, Property.NodeScope);
public static final Setting<List<String>> ROLES_SETTING =

View File

@ -0,0 +1,42 @@
/*
* 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.shield.user;
import org.elasticsearch.shield.authz.permission.SuperuserRole;
import org.elasticsearch.shield.user.User.ReservedUser;
/**
* The reserved {@code elastic} superuser. As full permission/access to the cluster/indices and can
* run as any other user.
*/
public class ElasticUser extends ReservedUser {
public static final String NAME = "elastic";
public static final String ROLE_NAME = SuperuserRole.NAME;
public static final ElasticUser INSTANCE = new ElasticUser();
private ElasticUser() {
super(NAME, ROLE_NAME);
}
@Override
public boolean equals(Object o) {
return INSTANCE == o;
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
public static boolean is(User user) {
return INSTANCE.equals(user);
}
public static boolean is(String principal) {
return NAME.equals(principal);
}
}

View File

@ -16,8 +16,8 @@ import java.util.function.Predicate;
*/
public class SystemUser extends User {
public static final String NAME = "__es_system_user";
public static final String ROLE_NAME = "__es_system_role";
public static final String NAME = "_system";
public static final String ROLE_NAME = "_system";
public static final User INSTANCE = new SystemUser();
@ -41,6 +41,10 @@ public class SystemUser extends User {
return INSTANCE.equals(user);
}
public static boolean is(String principal) {
return NAME.equals(principal);
}
public static boolean isAuthorized(String action) {
return PREDICATE.test(action);
}

View File

@ -187,12 +187,14 @@ public class User implements ToXContent {
public static User readFrom(StreamInput input) throws IOException {
if (input.readBoolean()) {
String name = input.readString();
if (SystemUser.NAME.equals(name)) {
if (SystemUser.is(name)) {
return SystemUser.INSTANCE;
} else if (XPackUser.is(name)) {
return XPackUser.INSTANCE;
}
User user = ReservedRealm.getUser(name);
if (user == null) {
throw new IllegalStateException("invalid internal user");
throw new IllegalStateException("invalid reserved user");
}
return user;
}
@ -217,6 +219,9 @@ public class User implements ToXContent {
if (SystemUser.is(user)) {
output.writeBoolean(true);
output.writeString(SystemUser.NAME);
} else if (XPackUser.is(user)) {
output.writeBoolean(true);
output.writeString(XPackUser.NAME);
} else if (ReservedRealm.isReserved(user.principal())) {
output.writeBoolean(true);
output.writeString(user.principal());

View File

@ -12,9 +12,9 @@ import org.elasticsearch.shield.user.User.ReservedUser;
* XPack internal user that manages xpack. Has all cluster/indices permissions for watcher,
* shield and monitoring to operate.
*/
public class XPackUser extends ReservedUser {
public class XPackUser extends User {
public static final String NAME = "elastic";
public static final String NAME = "_xpack";
public static final String ROLE_NAME = SuperuserRole.NAME;
public static final XPackUser INSTANCE = new XPackUser();

View File

@ -11,10 +11,10 @@ import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.SecurityContext;
import org.elasticsearch.shield.user.ElasticUser;
import org.elasticsearch.shield.user.KibanaUser;
import org.elasticsearch.shield.user.SystemUser;
import org.elasticsearch.shield.user.User;
import org.elasticsearch.shield.user.XPackUser;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
@ -83,7 +83,7 @@ public class TransportAuthenticateActionTests extends ESTestCase {
}
public void testValidUser() {
final User user = randomFrom(XPackUser.INSTANCE, KibanaUser.INSTANCE, new User("joe"));
final User user = randomFrom(ElasticUser.INSTANCE, KibanaUser.INSTANCE, new User("joe"));
SecurityContext securityContext = mock(SecurityContext.class);
when(securityContext.getUser()).thenReturn(user);
TransportAuthenticateAction action = new TransportAuthenticateAction(Settings.EMPTY, mock(ThreadPool.class),

View File

@ -14,10 +14,10 @@ import org.elasticsearch.shield.authc.esnative.NativeUsersStore;
import org.elasticsearch.shield.authc.support.Hasher;
import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.shield.user.AnonymousUser;
import org.elasticsearch.shield.user.ElasticUser;
import org.elasticsearch.shield.user.KibanaUser;
import org.elasticsearch.shield.user.SystemUser;
import org.elasticsearch.shield.user.User;
import org.elasticsearch.shield.user.XPackUser;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
@ -109,7 +109,7 @@ public class TransportChangePasswordActionTests extends ESTestCase {
}
public void testValidUser() {
final User user = randomFrom(XPackUser.INSTANCE, KibanaUser.INSTANCE, new User("joe"));
final User user = randomFrom(ElasticUser.INSTANCE, KibanaUser.INSTANCE, new User("joe"));
NativeUsersStore usersStore = mock(NativeUsersStore.class);
ChangePasswordRequest request = new ChangePasswordRequest();
request.username(user.principal());
@ -147,7 +147,7 @@ public class TransportChangePasswordActionTests extends ESTestCase {
}
public void testException() {
final User user = randomFrom(XPackUser.INSTANCE, KibanaUser.INSTANCE, new User("joe"));
final User user = randomFrom(ElasticUser.INSTANCE, KibanaUser.INSTANCE, new User("joe"));
NativeUsersStore usersStore = mock(NativeUsersStore.class);
ChangePasswordRequest request = new ChangePasswordRequest();
request.username(user.principal());

View File

@ -16,25 +16,25 @@ import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.shield.ShieldTemplateService;
import org.elasticsearch.shield.action.role.DeleteRoleResponse;
import org.elasticsearch.shield.action.role.GetRolesResponse;
import org.elasticsearch.shield.action.user.AuthenticateAction;
import org.elasticsearch.shield.action.user.AuthenticateRequest;
import org.elasticsearch.shield.action.user.AuthenticateResponse;
import org.elasticsearch.shield.action.user.ChangePasswordResponse;
import org.elasticsearch.shield.authz.permission.KibanaRole;
import org.elasticsearch.shield.authz.permission.SuperuserRole;
import org.elasticsearch.shield.user.AnonymousUser;
import org.elasticsearch.shield.user.KibanaUser;
import org.elasticsearch.shield.user.SystemUser;
import org.elasticsearch.shield.user.User;
import org.elasticsearch.shield.action.role.DeleteRoleResponse;
import org.elasticsearch.shield.action.role.GetRolesResponse;
import org.elasticsearch.shield.action.user.DeleteUserResponse;
import org.elasticsearch.shield.action.user.GetUsersResponse;
import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.shield.authz.RoleDescriptor;
import org.elasticsearch.shield.authz.permission.KibanaRole;
import org.elasticsearch.shield.authz.permission.Role;
import org.elasticsearch.shield.authz.permission.SuperuserRole;
import org.elasticsearch.shield.client.SecurityClient;
import org.elasticsearch.shield.user.XPackUser;
import org.elasticsearch.shield.user.AnonymousUser;
import org.elasticsearch.shield.user.ElasticUser;
import org.elasticsearch.shield.user.KibanaUser;
import org.elasticsearch.shield.user.SystemUser;
import org.elasticsearch.shield.user.User;
import org.elasticsearch.test.NativeRealmIntegTestCase;
import org.elasticsearch.test.ShieldSettingsSource;
import org.junit.BeforeClass;
@ -457,7 +457,7 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
}
public void testOperationsOnReservedUsers() throws Exception {
final String username = randomFrom(XPackUser.NAME, KibanaUser.NAME);
final String username = randomFrom(ElasticUser.NAME, KibanaUser.NAME);
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class,
() -> securityClient().preparePutUser(username, randomBoolean() ? "changeme".toCharArray() : null, "admin").get());
assertThat(exception.getMessage(), containsString("user [" + username + "] is reserved"));

View File

@ -7,8 +7,8 @@ package org.elasticsearch.shield.authc.esnative;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.shield.user.ElasticUser;
import org.elasticsearch.shield.user.KibanaUser;
import org.elasticsearch.shield.user.XPackUser;
import org.elasticsearch.shield.action.user.ChangePasswordResponse;
import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.test.NativeRealmIntegTestCase;
@ -29,7 +29,7 @@ public class ReservedRealmIntegTests extends NativeRealmIntegTestCase {
private static final SecuredString DEFAULT_PASSWORD = new SecuredString("changeme".toCharArray());
public void testAuthenticate() {
for (String username : Arrays.asList(XPackUser.NAME, KibanaUser.NAME)) {
for (String username : Arrays.asList(ElasticUser.NAME, KibanaUser.NAME)) {
ClusterHealthResponse response = client()
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(username, DEFAULT_PASSWORD)))
.admin()
@ -42,7 +42,7 @@ public class ReservedRealmIntegTests extends NativeRealmIntegTestCase {
}
public void testChangingPassword() {
String username = randomFrom(XPackUser.NAME, KibanaUser.NAME);
String username = randomFrom(ElasticUser.NAME, KibanaUser.NAME);
final char[] newPassword = "supersecretvalue".toCharArray();
if (randomBoolean()) {

View File

@ -13,9 +13,9 @@ import org.elasticsearch.shield.authc.support.Hasher;
import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
import org.elasticsearch.shield.user.AnonymousUser;
import org.elasticsearch.shield.user.ElasticUser;
import org.elasticsearch.shield.user.KibanaUser;
import org.elasticsearch.shield.user.User;
import org.elasticsearch.shield.user.XPackUser;
import org.elasticsearch.test.ESTestCase;
import org.junit.Before;
@ -49,7 +49,7 @@ public class ReservedRealmTests extends ESTestCase {
public void testUserStoreNotStarted() {
when(usersStore.started()).thenReturn(false);
final ReservedRealm reservedRealm = new ReservedRealm(mock(Environment.class), Settings.EMPTY, usersStore);
final String principal = randomFrom(XPackUser.NAME, KibanaUser.NAME);
final String principal = randomFrom(ElasticUser.NAME, KibanaUser.NAME);
ElasticsearchSecurityException expected = expectThrows(ElasticsearchSecurityException.class,
() -> reservedRealm.doAuthenticate(new UsernamePasswordToken(principal, DEFAULT_PASSWORD)));
@ -65,7 +65,7 @@ public class ReservedRealmTests extends ESTestCase {
when(usersStore.shieldIndexExists()).thenReturn(true);
}
final ReservedRealm reservedRealm = new ReservedRealm(mock(Environment.class), Settings.EMPTY, usersStore);
final User expected = randomFrom((User) XPackUser.INSTANCE, (User) KibanaUser.INSTANCE);
final User expected = randomFrom((User) ElasticUser.INSTANCE, KibanaUser.INSTANCE);
final String principal = expected.principal();
final User authenticated = reservedRealm.doAuthenticate(new UsernamePasswordToken(principal, DEFAULT_PASSWORD));
@ -81,7 +81,7 @@ public class ReservedRealmTests extends ESTestCase {
public void testAuthenticationWithStoredPassword() throws Throwable {
final ReservedRealm reservedRealm = new ReservedRealm(mock(Environment.class), Settings.EMPTY, usersStore);
final User expectedUser = randomFrom((User) XPackUser.INSTANCE, (User) KibanaUser.INSTANCE);
final User expectedUser = randomFrom((User) ElasticUser.INSTANCE, KibanaUser.INSTANCE);
final String principal = expectedUser.principal();
final SecuredString newPassword = new SecuredString("foobar".toCharArray());
when(usersStore.shieldIndexExists()).thenReturn(true);
@ -107,7 +107,7 @@ public class ReservedRealmTests extends ESTestCase {
public void testLookup() {
final ReservedRealm reservedRealm = new ReservedRealm(mock(Environment.class), Settings.EMPTY, usersStore);
final User expectedUser = randomFrom((User) XPackUser.INSTANCE, (User) KibanaUser.INSTANCE);
final User expectedUser = randomFrom((User) ElasticUser.INSTANCE, KibanaUser.INSTANCE);
final String principal = expectedUser.principal();
final User user = reservedRealm.doLookupUser(principal);
@ -120,7 +120,7 @@ public class ReservedRealmTests extends ESTestCase {
}
public void testHelperMethods() {
final User expectedUser = randomFrom((User) XPackUser.INSTANCE, KibanaUser.INSTANCE);
final User expectedUser = randomFrom((User) ElasticUser.INSTANCE, KibanaUser.INSTANCE);
final String principal = expectedUser.principal();
assertThat(ReservedRealm.isReserved(principal), is(true));
assertThat(ReservedRealm.getUser(principal), sameInstance(expectedUser));
@ -129,19 +129,20 @@ public class ReservedRealmTests extends ESTestCase {
assertThat(ReservedRealm.isReserved(notExpected), is(false));
assertThat(ReservedRealm.getUser(notExpected), nullValue());
assertThat(ReservedRealm.users(), containsInAnyOrder((User) XPackUser.INSTANCE, KibanaUser.INSTANCE));
assertThat(ReservedRealm.users(), containsInAnyOrder((User) ElasticUser.INSTANCE, KibanaUser.INSTANCE));
}
public void testFailedAuthentication() {
final ReservedRealm reservedRealm = new ReservedRealm(mock(Environment.class), Settings.EMPTY, usersStore);
// maybe cache a successful auth
if (randomBoolean()) {
User user = reservedRealm.authenticate(new UsernamePasswordToken(XPackUser.NAME, new SecuredString("changeme".toCharArray())));
assertThat(user, sameInstance(XPackUser.INSTANCE));
User user = reservedRealm.authenticate(
new UsernamePasswordToken(ElasticUser.NAME, new SecuredString("changeme".toCharArray())));
assertThat(user, sameInstance(ElasticUser.INSTANCE));
}
try {
reservedRealm.authenticate(new UsernamePasswordToken(XPackUser.NAME, new SecuredString("foobar".toCharArray())));
reservedRealm.authenticate(new UsernamePasswordToken(ElasticUser.NAME, new SecuredString("foobar".toCharArray())));
fail("authentication should throw an exception otherwise we may allow others to impersonate reserved users...");
} catch (ElasticsearchSecurityException e) {
assertThat(e.getMessage(), containsString("failed to authenticate"));

View File

@ -358,7 +358,7 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(messages, notNullValue());
assertThat(messages, hasSize(4));
// the system role will always be checked first
assertThat(messages.get(0).text, containsString("role [__es_system_role] is reserved"));
assertThat(messages.get(0).text, containsString("role [_system] is reserved"));
assertThat(messages.get(1).text, containsString("role [superuser] is reserved"));
assertThat(messages.get(2).text, containsString("role [kibana] is reserved"));
assertThat(messages.get(3).text, containsString("role [transport_client] is reserved"));

View File

@ -9,10 +9,10 @@ import org.elasticsearch.shield.SecurityContext;
import org.elasticsearch.shield.authz.permission.KibanaRole;
import org.elasticsearch.shield.authz.permission.SuperuserRole;
import org.elasticsearch.shield.authz.permission.TransportClientRole;
import org.elasticsearch.shield.user.ElasticUser;
import org.elasticsearch.shield.user.KibanaUser;
import org.elasticsearch.shield.user.SystemUser;
import org.elasticsearch.shield.user.User;
import org.elasticsearch.shield.user.XPackUser;
import org.elasticsearch.test.ESTestCase;
import org.junit.Before;
@ -41,7 +41,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
public void testRetrievingReservedRolesNonKibanaUser() {
if (randomBoolean()) {
when(securityContext.getUser()).thenReturn(XPackUser.INSTANCE);
when(securityContext.getUser()).thenReturn(ElasticUser.INSTANCE);
}
assertThat(reservedRolesStore.role(SuperuserRole.NAME), sameInstance(SuperuserRole.INSTANCE));

View File

@ -10,6 +10,7 @@ import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.ByteBufferStreamInput;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.XPackClient;
import java.util.Arrays;
import java.util.Collections;
@ -58,7 +59,7 @@ public class UserTests extends ESTestCase {
assertThat(readFromRunAs.runAs(), is(nullValue()));
}
public void testSystemReadAndWrite() throws Exception {
public void testSystemUserReadAndWrite() throws Exception {
BytesStreamOutput output = new BytesStreamOutput();
User.writeTo(SystemUser.INSTANCE, output);
@ -68,6 +69,16 @@ public class UserTests extends ESTestCase {
assertThat(readFrom.runAs(), is(nullValue()));
}
public void testXPackUserReadAndWrite() throws Exception {
BytesStreamOutput output = new BytesStreamOutput();
User.writeTo(XPackUser.INSTANCE, output);
User readFrom = User.readFrom(ByteBufferStreamInput.wrap(output.bytes()));
assertThat(readFrom, is(sameInstance(XPackUser.INSTANCE)));
assertThat(readFrom.runAs(), is(nullValue()));
}
public void testFakeInternalUserSerialization() throws Exception {
BytesStreamOutput output = new BytesStreamOutput();
output.writeBoolean(true);
@ -102,10 +113,10 @@ public class UserTests extends ESTestCase {
public void testReservedUserSerialization() throws Exception {
BytesStreamOutput output = new BytesStreamOutput();
User.writeTo(XPackUser.INSTANCE, output);
User.writeTo(ElasticUser.INSTANCE, output);
User readFrom = User.readFrom(ByteBufferStreamInput.wrap(output.bytes()));
assertThat(readFrom, is(sameInstance(XPackUser.INSTANCE)));
assertThat(readFrom, is(sameInstance(ElasticUser.INSTANCE)));
output = new BytesStreamOutput();
User.writeTo(KibanaUser.INSTANCE, output);

View File

@ -5,7 +5,7 @@ admin:
- names: '*'
privileges: [ all ]
__es_system_role:
_system:
cluster:
- all
indices: