Backport: Deprecate the kibana reserved user (#54967) (#55822)

This commit is contained in:
Larry Gregory 2020-04-28 10:30:25 -04:00 committed by GitHub
parent ddc7305ac9
commit 47d252424b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 284 additions and 112 deletions

View File

@ -198,10 +198,11 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
List<User> users = new ArrayList<>(3);
users.addAll(response.getUsers());
assertNotNull(response);
// 9 users are expected to be returned
// 10 users are expected to be returned
// test_users (3): user1, user2, user3
// system_users (6): elastic, beats_system, apm_system, logstash_system, kibana, remote_monitoring_user
assertThat(users.size(), equalTo(9));
// system_users (6): elastic, beats_system, apm_system, logstash_system, kibana, kibana_system, remote_monitoring_user
logger.info(users);
assertThat(users.size(), equalTo(10));
}
{

View File

@ -92,4 +92,40 @@ To avoid deprecation warnings, discontinue use of the `node.local_storage`
setting.
====
[float]
[[builtin-users-changes]]
==== Changes to built-in users
[float]
===== The `kibana` user has been deprecated in favor of the `kibana_system` user
The `kibana` user was historically used to authenticate {kib} to {es}.
The name of this user was confusing, and was often mistakenly used to login to {kib}.
This has been renamed to `kibana_system` in order to reduce confusion, and to better
align with other built-in system accounts.
If your `kibana.yml` used to contain:
[source,yaml]
--------------------------------------------------
elasticsearch.username: kibana
--------------------------------------------------
then you should update to use the new `kibana_system` user instead:
[source,yaml]
--------------------------------------------------
elasticsearch.username: kibana_system
--------------------------------------------------
[float]
[[builtin-roles-changes]]
==== Changes to built-in roles
[float]
===== The `kibana_user` role has been deprecated in favor of the `kibana_admin` role
Users who were previously assigned the `kibana_user` role should instead be assigned
the `kibana_admin` role. This role grants the same set of privileges as `kibana_user`, but has been
renamed to better reflect its intended use.
//end::notable-breaking-changes[]

View File

@ -139,18 +139,18 @@ non-matching events are printed as usual.
All policies are defined under the `xpack.security.audit.logfile.events.ignore_filters`
settings namespace. For example, the following policy named _example1_ matches
events from the _kibana_ or _admin_user_ principals **and** operating over indices of the
events from the _kibana_system_ or _admin_user_ principals **and** operating over indices of the
wildcard form _app-logs*_:
[source,yaml]
----------------------------
xpack.security.audit.logfile.events.ignore_filters:
example1:
users: ["kibana", "admin_user"]
users: ["kibana_system", "admin_user"]
indices: ["app-logs*"]
----------------------------
An audit event generated by the _kibana_ user and operating over multiple indices
An audit event generated by the _kibana_system_ user and operating over multiple indices
, some of which do not match the indices wildcard, will not match.
As expected, operations generated by all other users (even operating only on indices that
match the _indices_ filter) will not match this policy either.

View File

@ -65,7 +65,7 @@ The +elasticsearch-setup-passwords+ tool is the simplest method to set the
built-in users' passwords for the first time. It uses the `elastic` user's
bootstrap password to run user management API requests. For example, you can run
the command in an "interactive" mode, which prompts you to enter new passwords
for the `elastic`, `kibana`, `logstash_system`, `beats_system`, `apm_system`,
for the `elastic`, `kibana_system`, `logstash_system`, `beats_system`, `apm_system`,
and `remote_monitoring_user` users:
[source,shell]
@ -107,7 +107,7 @@ since at that point the bootstrap password is no longer required.
[[add-built-in-user-kibana]]
==== Adding built-in user passwords to {kib}
After the `kibana` user password is set, you need to update the {kib} server
After the `kibana_system` user password is set, you need to update the {kib} server
with the new password by setting `elasticsearch.password` in the `kibana.yml`
configuration file:

View File

@ -1,6 +1,6 @@
// tag::create-users[]
There are <<built-in-users,built-in users>> that you can use for specific
administrative purposes: `apm_system`, `beats_system`, `elastic`, `kibana`,
administrative purposes: `apm_system`, `beats_system`, `elastic`, `kibana_system`,
`logstash_system`, and `remote_monitoring_user`.
// end::create-users[]

View File

@ -2,9 +2,9 @@ When the {es} {security-features} are enabled, users must log in to {kib}
with a valid user ID and password.
{kib} also performs some tasks under the covers that require use of the
built-in `kibana` user.
built-in `kibana_system` user.
. Configure {kib} to use the built-in `kibana` user and the password that you
. Configure {kib} to use the built-in `kibana_system` user and the password that you
created:
** If you don't mind having passwords visible in your configuration file,
@ -21,7 +21,7 @@ For example, add the following settings:
[source,yaml]
----
elasticsearch.username: "kibana"
elasticsearch.username: "kibana_system"
elasticsearch.password: "your_password"
----
@ -42,7 +42,7 @@ keystore and add the secure settings:
./bin/kibana-keystore add elasticsearch.password
----------------------------------------------------------------------
When prompted, specify the `kibana` built-in user and its password for these
When prompted, specify the `kibana_system` built-in user and its password for these
setting values. The settings are automatically applied when you start {kib}.
To learn more, see {kibana-ref}/secure-settings.html[Secure settings].
// end::store-kibana-user[]

View File

@ -27,7 +27,7 @@ TIP: The `ES_PATH_CONF` environment variable contains the path for the {es}
configuration files. If you installed {es} using archive distributions (`zip` or
`tar.gz`), it defaults to `ES_HOME/config`. If you used package distributions
(Debian or RPM), it defaults to `/etc/elasticsearch`. For more information, see
{ref}/settings.html[Configuring {es}].
<<settings>>.
The default cluster name is `elasticsearch`. You should choose a unique name,
however, to ensure that your nodes join the right cluster.
@ -154,11 +154,11 @@ you can skip this step.
include::{xes-repo-dir}/security/get-started-builtin-users.asciidoc[tag=create-users]
After you setup the password for the `kibana` built-in user,
After you setup the password for the `kibana_system` built-in user,
<<get-started-kibana-user,configure {kib} to use it>>.
For example, run the following commands to create the {kib} keystore and add the
`kibana` built-in user and its password in secure settings:
`kibana_system` built-in user and its password in secure settings:
include::{xes-repo-dir}/security/get-started-kibana-users.asciidoc[tag=store-kibana-user]
--

View File

@ -16,6 +16,7 @@ public class ClientReservedRealm {
assert username != null;
switch (username) {
case UsernamesField.ELASTIC_NAME:
case UsernamesField.DEPRECATED_KIBANA_NAME:
case UsernamesField.KIBANA_NAME:
case UsernamesField.LOGSTASH_NAME:
case UsernamesField.BEATS_NAME:

View File

@ -0,0 +1,21 @@
/*
* 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.security.user;
import org.elasticsearch.xpack.core.security.support.MetadataUtils;
/**
* Built in user for the kibana server
*/
public class KibanaSystemUser extends User {
public static final String NAME = UsernamesField.KIBANA_NAME;
public static final String ROLE_NAME = UsernamesField.KIBANA_ROLE;
public KibanaSystemUser(boolean enabled) {
super(NAME, new String[]{ ROLE_NAME }, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA, enabled);
}
}

View File

@ -9,13 +9,16 @@ import org.elasticsearch.xpack.core.security.support.MetadataUtils;
/**
* Built in user for the kibana server
* @deprecated use KibanaSystemUser
*/
@Deprecated
public class KibanaUser extends User {
public static final String NAME = UsernamesField.KIBANA_NAME;
public static final String NAME = UsernamesField.DEPRECATED_KIBANA_NAME;
public static final String ROLE_NAME = UsernamesField.KIBANA_ROLE;
public KibanaUser(boolean enabled) {
super(NAME, new String[]{ ROLE_NAME }, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA, enabled);
super(NAME, new String[]{ ROLE_NAME }, null, null,
MetadataUtils.getDeprecatedReservedMetadata("Please use the [kibana_system] user instead."), enabled);
}
}

View File

@ -8,7 +8,8 @@ package org.elasticsearch.xpack.core.security.user;
public final class UsernamesField {
public static final String ELASTIC_NAME = "elastic";
public static final String ELASTIC_ROLE = "superuser";
public static final String KIBANA_NAME = "kibana";
public static final String DEPRECATED_KIBANA_NAME = "kibana";
public static final String KIBANA_NAME = "kibana_system";
public static final String KIBANA_ROLE = "kibana_system";
public static final String SYSTEM_NAME = "_system";
public static final String SYSTEM_ROLE = "_system";

View File

@ -46,7 +46,7 @@ public class IdentityProviderAuthenticationIT extends IdpRestTestCase {
@Before
public void setupSecurityData() throws IOException {
setUserPassword("kibana", new SecureString("kibana".toCharArray()));
setUserPassword("kibana_system", new SecureString("kibana_system".toCharArray()));
createApplicationPrivileges("elastic-cloud", org.elasticsearch.common.collect.Map.of(
"deployment_admin", Set.of("sso:admin"),
"deployment_viewer", Set.of("sso:viewer"))
@ -114,7 +114,7 @@ public class IdentityProviderAuthenticationIT extends IdpRestTestCase {
private SamlPrepareAuthenticationResponse generateSamlAuthnRequest(String realmName) throws Exception {
final Request request = new Request("POST", "/_security/saml/prepare");
request.setJsonEntity("{\"realm\":\"" + realmName + "\"}");
try (RestClient kibanaClient = restClientAsKibana()) {
try (RestClient kibanaClient = restClientAsKibanaSystem()) {
final Response response = kibanaClient.performRequest(request);
final Map<String, Object> map = entityAsMap(response);
assertThat(ObjectPath.eval("realm", map), equalTo(realmName));
@ -153,7 +153,7 @@ public class IdentityProviderAuthenticationIT extends IdpRestTestCase {
request.setJsonEntity("{\"content\":\"" + encodedResponse + "\", \"realm\":\"" + REALM_NAME + "\"}");
}
final String accessToken;
try (RestClient kibanaClient = restClientAsKibana()) {
try (RestClient kibanaClient = restClientAsKibanaSystem()) {
final Response response = kibanaClient.performRequest(request);
final Map<String, Object> map = entityAsMap(response);
assertThat(ObjectPath.eval("username", map), instanceOf(String.class));
@ -185,10 +185,10 @@ public class IdentityProviderAuthenticationIT extends IdpRestTestCase {
getClusterHosts().toArray(new HttpHost[getClusterHosts().size()]));
}
private RestClient restClientAsKibana() throws IOException {
private RestClient restClientAsKibanaSystem() throws IOException {
return buildClient(
Settings.builder().put(ThreadContext.PREFIX + ".Authorization", basicAuthHeaderValue("kibana",
new SecureString("kibana".toCharArray()))).build(),
Settings.builder().put(ThreadContext.PREFIX + ".Authorization", basicAuthHeaderValue("kibana_system",
new SecureString("kibana_system".toCharArray()))).build(),
getClusterHosts().toArray(new HttpHost[getClusterHosts().size()]));
}
}

View File

@ -9,6 +9,7 @@ import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.util.Supplier;
import org.elasticsearch.Version;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.logging.DeprecationLogger;
import org.elasticsearch.common.settings.KeyStoreWrapper;
import org.elasticsearch.common.settings.SecureSetting;
import org.elasticsearch.common.settings.SecureString;
@ -24,10 +25,12 @@ import org.elasticsearch.xpack.core.security.authc.esnative.ClientReservedRealm;
import org.elasticsearch.xpack.core.security.authc.support.Hasher;
import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.core.security.support.Exceptions;
import org.elasticsearch.xpack.core.security.support.MetadataUtils;
import org.elasticsearch.xpack.core.security.user.APMSystemUser;
import org.elasticsearch.xpack.core.security.user.AnonymousUser;
import org.elasticsearch.xpack.core.security.user.BeatsSystemUser;
import org.elasticsearch.xpack.core.security.user.ElasticUser;
import org.elasticsearch.xpack.core.security.user.KibanaSystemUser;
import org.elasticsearch.xpack.core.security.user.KibanaUser;
import org.elasticsearch.xpack.core.security.user.LogstashSystemUser;
import org.elasticsearch.xpack.core.security.user.RemoteMonitoringUser;
@ -41,6 +44,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* A realm for predefined users. These users can only be modified in terms of changing their passwords; no other modifications are allowed.
@ -66,6 +70,8 @@ public class ReservedRealm extends CachingUsernamePasswordRealm {
private final ReservedUserInfo disabledDefaultUserInfo;
private final ReservedUserInfo enabledDefaultUserInfo;
private final DeprecationLogger deprecationLogger = new DeprecationLogger(logger);
public ReservedRealm(Environment env, Settings settings, NativeUsersStore nativeUsersStore, AnonymousUser anonymousUser,
SecurityIndexManager securityIndex, ThreadPool threadPool) {
super(new RealmConfig(new RealmConfig.RealmIdentifier(TYPE, TYPE), settings, env, threadPool.getThreadContext()), threadPool);
@ -98,6 +104,7 @@ public class ReservedRealm extends CachingUsernamePasswordRealm {
result = AuthenticationResult.terminate("failed to authenticate user [" + token.principal() + "]", null);
} else if (userInfo.verifyPassword(token.credentials())) {
final User user = getUser(token.principal(), userInfo);
logDeprecatedUser(user);
result = AuthenticationResult.success(user);
} else {
result = AuthenticationResult.terminate("failed to authenticate user [" + token.principal() + "]", null);
@ -147,6 +154,8 @@ public class ReservedRealm extends CachingUsernamePasswordRealm {
return new ElasticUser(userInfo.enabled);
case KibanaUser.NAME:
return new KibanaUser(userInfo.enabled);
case KibanaSystemUser.NAME:
return new KibanaSystemUser(userInfo.enabled);
case LogstashSystemUser.NAME:
return new LogstashSystemUser(userInfo.enabled);
case BeatsSystemUser.NAME:
@ -177,6 +186,9 @@ public class ReservedRealm extends CachingUsernamePasswordRealm {
userInfo = reservedUserInfos.get(KibanaUser.NAME);
users.add(new KibanaUser(userInfo == null || userInfo.enabled));
userInfo = reservedUserInfos.get(KibanaSystemUser.NAME);
users.add(new KibanaSystemUser(userInfo == null || userInfo.enabled));
userInfo = reservedUserInfos.get(LogstashSystemUser.NAME);
users.add(new LogstashSystemUser(userInfo == null || userInfo.enabled));
@ -223,6 +235,15 @@ public class ReservedRealm extends CachingUsernamePasswordRealm {
}
}
private void logDeprecatedUser(final User user){
Map<String, Object> metadata = user.metadata();
if (Boolean.TRUE.equals(metadata.get(MetadataUtils.DEPRECATED_METADATA_KEY))) {
deprecationLogger.deprecatedAndMaybeLog("deprecated_user-" + user.principal(), "The user [" + user.principal() +
"] is deprecated and will be removed in a future version of Elasticsearch. " +
metadata.get(MetadataUtils.DEPRECATED_REASON_METADATA_KEY));
}
}
private ReservedUserInfo getDefaultUserInfo(String username) {
if (ElasticUser.NAME.equals(username)) {
return bootstrapUserInfo.deepClone();

View File

@ -30,6 +30,7 @@ import org.elasticsearch.xpack.core.security.support.Validation;
import org.elasticsearch.xpack.core.security.user.APMSystemUser;
import org.elasticsearch.xpack.core.security.user.BeatsSystemUser;
import org.elasticsearch.xpack.core.security.user.ElasticUser;
import org.elasticsearch.xpack.core.security.user.KibanaSystemUser;
import org.elasticsearch.xpack.core.security.user.KibanaUser;
import org.elasticsearch.xpack.core.security.user.LogstashSystemUser;
import org.elasticsearch.xpack.core.security.user.RemoteMonitoringUser;
@ -46,7 +47,9 @@ import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -65,8 +68,14 @@ import static java.util.Arrays.asList;
public class SetupPasswordTool extends LoggingAwareMultiCommand {
private static final char[] CHARS = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789").toCharArray();
public static final List<String> USERS = asList(ElasticUser.NAME, APMSystemUser.NAME, KibanaUser.NAME, LogstashSystemUser.NAME,
BeatsSystemUser.NAME, RemoteMonitoringUser.NAME);
public static final List<String> USERS = asList(ElasticUser.NAME, APMSystemUser.NAME, KibanaUser.NAME, KibanaSystemUser.NAME,
LogstashSystemUser.NAME, BeatsSystemUser.NAME, RemoteMonitoringUser.NAME);
public static final Map<String, String> USERS_WITH_SHARED_PASSWORDS = Collections.unmodifiableMap(new HashMap<String, String>() {
{
put(KibanaSystemUser.NAME, KibanaUser.NAME);
}
});
private final BiFunction<Environment, Settings, CommandLineHttpClient> clientFunction;
private final CheckedFunction<Environment, KeyStoreWrapper, Exception> keyStoreFunction;
@ -503,10 +512,18 @@ public class SetupPasswordTool extends LoggingAwareMultiCommand {
*/
void changePasswords(CheckedFunction<String, SecureString, UserException> passwordFn,
CheckedBiConsumer<String, SecureString, Exception> successCallback, Terminal terminal) throws Exception {
Map<String, SecureString> passwordsMap = new HashMap<>(USERS.size());
Map<String, SecureString> passwordsMap = new LinkedHashMap<>(USERS.size());
try {
for (String user : USERS) {
passwordsMap.put(user, passwordFn.apply(user));
if (USERS_WITH_SHARED_PASSWORDS.containsValue(user)) {
continue;
}
SecureString password = passwordFn.apply(user);
passwordsMap.put(user, password);
if (USERS_WITH_SHARED_PASSWORDS.containsKey(user)) {
passwordsMap.put(USERS_WITH_SHARED_PASSWORDS.get(user), password.clone());
}
}
/*
* Change elastic user last. This tool will not run after the elastic user

View File

@ -27,13 +27,13 @@ public class KibanaSystemRoleIntegTests extends SecurityIntegTestCase {
public String configUsers() {
final String usersPasswdHashed = new String(getFastStoredHashAlgoForTests().hash(USERS_PASSWD));
return super.configUsers() +
"kibana_system:" + usersPasswdHashed;
"my_kibana_system:" + usersPasswdHashed;
}
@Override
public String configUsersRoles() {
return super.configUsersRoles() +
"kibana_system:kibana_system";
"kibana_system:my_kibana_system";
}
@ -42,13 +42,14 @@ public class KibanaSystemRoleIntegTests extends SecurityIntegTestCase {
if (randomBoolean()) {
CreateIndexResponse createIndexResponse = client().filterWithHeader(singletonMap("Authorization",
UsernamePasswordToken.basicAuthHeaderValue("kibana_system", USERS_PASSWD)))
UsernamePasswordToken.basicAuthHeaderValue("my_kibana_system", USERS_PASSWD)))
.admin().indices().prepareCreate(index).get();
assertThat(createIndexResponse.isAcknowledged(), is(true));
}
IndexResponse response = client()
.filterWithHeader(singletonMap("Authorization", UsernamePasswordToken.basicAuthHeaderValue("kibana_system", USERS_PASSWD)))
.filterWithHeader(singletonMap("Authorization",
UsernamePasswordToken.basicAuthHeaderValue("my_kibana_system", USERS_PASSWD)))
.prepareIndex()
.setIndex(index)
.setType("dashboard")
@ -58,7 +59,8 @@ public class KibanaSystemRoleIntegTests extends SecurityIntegTestCase {
assertEquals(DocWriteResponse.Result.CREATED, response.getResult());
DeleteResponse deleteResponse = client()
.filterWithHeader(singletonMap("Authorization", UsernamePasswordToken.basicAuthHeaderValue("kibana_system", USERS_PASSWD)))
.filterWithHeader(singletonMap("Authorization",
UsernamePasswordToken.basicAuthHeaderValue("my_kibana_system", USERS_PASSWD)))
.prepareDelete(index, "dashboard", response.getId())
.get();
assertEquals(DocWriteResponse.Result.DELETED, deleteResponse.getResult());

View File

@ -17,6 +17,7 @@ import org.elasticsearch.xpack.core.security.client.SecurityClient;
import org.elasticsearch.xpack.core.security.user.APMSystemUser;
import org.elasticsearch.xpack.core.security.user.BeatsSystemUser;
import org.elasticsearch.xpack.core.security.user.ElasticUser;
import org.elasticsearch.xpack.core.security.user.KibanaSystemUser;
import org.elasticsearch.xpack.core.security.user.KibanaUser;
import org.elasticsearch.xpack.core.security.user.LogstashSystemUser;
import org.elasticsearch.xpack.core.security.user.RemoteMonitoringUser;
@ -103,8 +104,8 @@ public abstract class NativeRealmIntegTestCase extends SecurityIntegTestCase {
RequestOptions.Builder optionsBuilder = RequestOptions.DEFAULT.toBuilder();
optionsBuilder.addHeader("Authorization", UsernamePasswordToken.basicAuthHeaderValue(ElasticUser.NAME, reservedPassword));
RequestOptions options = optionsBuilder.build();
final List<String> usernames = Arrays.asList(KibanaUser.NAME, LogstashSystemUser.NAME, BeatsSystemUser.NAME, APMSystemUser.NAME,
RemoteMonitoringUser.NAME);
final List<String> usernames = Arrays.asList(KibanaUser.NAME, KibanaSystemUser.NAME, LogstashSystemUser.NAME, BeatsSystemUser.NAME,
APMSystemUser.NAME, RemoteMonitoringUser.NAME);
for (String username : usernames) {
Request request = new Request("PUT", "/_security/user/" + username + "/_password");
request.setJsonEntity("{\"password\": \"" + new String(reservedPassword.getChars()) + "\"}");

View File

@ -31,6 +31,7 @@ import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames;
import org.elasticsearch.xpack.core.security.user.APMSystemUser;
import org.elasticsearch.xpack.core.security.user.BeatsSystemUser;
import org.elasticsearch.xpack.core.security.user.ElasticUser;
import org.elasticsearch.xpack.core.security.user.KibanaSystemUser;
import org.elasticsearch.xpack.core.security.user.KibanaUser;
import org.elasticsearch.xpack.core.security.user.LogstashSystemUser;
import org.elasticsearch.xpack.core.security.user.RemoteMonitoringUser;
@ -86,8 +87,8 @@ public class NativeUsersStoreTests extends ESTestCase {
public void testPasswordUpsertWhenSetEnabledOnReservedUser() throws Exception {
final NativeUsersStore nativeUsersStore = startNativeUsersStore();
final String user = randomFrom(ElasticUser.NAME, KibanaUser.NAME, LogstashSystemUser.NAME,
BeatsSystemUser.NAME, APMSystemUser.NAME, RemoteMonitoringUser.NAME);
final String user = randomFrom(ElasticUser.NAME, KibanaUser.NAME, KibanaSystemUser.NAME,
LogstashSystemUser.NAME, BeatsSystemUser.NAME, APMSystemUser.NAME, RemoteMonitoringUser.NAME);
final PlainActionFuture<Void> future = new PlainActionFuture<>();
nativeUsersStore.setEnabled(user, true, WriteRequest.RefreshPolicy.IMMEDIATE, future);
@ -105,8 +106,8 @@ public class NativeUsersStoreTests extends ESTestCase {
public void testBlankPasswordInIndexImpliesDefaultPassword() throws Exception {
final NativeUsersStore nativeUsersStore = startNativeUsersStore();
final String user = randomFrom(ElasticUser.NAME, KibanaUser.NAME, LogstashSystemUser.NAME,
BeatsSystemUser.NAME, APMSystemUser.NAME, RemoteMonitoringUser.NAME);
final String user = randomFrom(ElasticUser.NAME, KibanaUser.NAME, KibanaSystemUser.NAME,
LogstashSystemUser.NAME, BeatsSystemUser.NAME, APMSystemUser.NAME, RemoteMonitoringUser.NAME);
final Map<String, Object> values = new HashMap<>();
values.put(ENABLED_FIELD, Boolean.TRUE);
values.put(PASSWORD_FIELD, BLANK_PASSWORD);

View File

@ -16,6 +16,7 @@ import org.elasticsearch.xpack.core.security.client.SecurityClient;
import org.elasticsearch.xpack.core.security.user.APMSystemUser;
import org.elasticsearch.xpack.core.security.user.BeatsSystemUser;
import org.elasticsearch.xpack.core.security.user.ElasticUser;
import org.elasticsearch.xpack.core.security.user.KibanaSystemUser;
import org.elasticsearch.xpack.core.security.user.KibanaUser;
import org.elasticsearch.xpack.core.security.user.LogstashSystemUser;
import org.elasticsearch.xpack.core.security.user.RemoteMonitoringUser;
@ -52,8 +53,8 @@ public class ReservedRealmIntegTests extends NativeRealmIntegTestCase {
}
public void testAuthenticate() {
final List<String> usernames = Arrays.asList(ElasticUser.NAME, KibanaUser.NAME, LogstashSystemUser.NAME,
BeatsSystemUser.NAME, APMSystemUser.NAME, RemoteMonitoringUser.NAME);
final List<String> usernames = Arrays.asList(ElasticUser.NAME, KibanaUser.NAME, KibanaSystemUser.NAME,
LogstashSystemUser.NAME, BeatsSystemUser.NAME, APMSystemUser.NAME, RemoteMonitoringUser.NAME);
for (String username : usernames) {
ClusterHealthResponse response = client()
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(username, getReservedPassword())))
@ -72,8 +73,8 @@ public class ReservedRealmIntegTests extends NativeRealmIntegTestCase {
*/
public void testAuthenticateAfterEnablingUser() {
final SecurityClient c = securityClient();
final List<String> usernames = Arrays.asList(ElasticUser.NAME, KibanaUser.NAME, LogstashSystemUser.NAME,
BeatsSystemUser.NAME, APMSystemUser.NAME, RemoteMonitoringUser.NAME);
final List<String> usernames = Arrays.asList(ElasticUser.NAME, KibanaUser.NAME, KibanaSystemUser.NAME,
LogstashSystemUser.NAME, BeatsSystemUser.NAME, APMSystemUser.NAME, RemoteMonitoringUser.NAME);
for (String username : usernames) {
c.prepareSetEnabled(username, true).get();
ClusterHealthResponse response = client()
@ -88,8 +89,8 @@ public class ReservedRealmIntegTests extends NativeRealmIntegTestCase {
}
public void testChangingPassword() {
String username = randomFrom(ElasticUser.NAME, KibanaUser.NAME, LogstashSystemUser.NAME,
BeatsSystemUser.NAME, APMSystemUser.NAME, RemoteMonitoringUser.NAME);
String username = randomFrom(ElasticUser.NAME, KibanaUser.NAME, KibanaSystemUser.NAME,
LogstashSystemUser.NAME, BeatsSystemUser.NAME, APMSystemUser.NAME, RemoteMonitoringUser.NAME);
final char[] newPassword = "supersecretvalue".toCharArray();
if (randomBoolean()) {

View File

@ -25,6 +25,7 @@ import org.elasticsearch.xpack.core.security.user.APMSystemUser;
import org.elasticsearch.xpack.core.security.user.AnonymousUser;
import org.elasticsearch.xpack.core.security.user.BeatsSystemUser;
import org.elasticsearch.xpack.core.security.user.ElasticUser;
import org.elasticsearch.xpack.core.security.user.KibanaSystemUser;
import org.elasticsearch.xpack.core.security.user.KibanaUser;
import org.elasticsearch.xpack.core.security.user.LogstashSystemUser;
import org.elasticsearch.xpack.core.security.user.RemoteMonitoringUser;
@ -169,6 +170,11 @@ public class ReservedRealmTests extends ESTestCase {
verify(securityIndex, times(2)).checkMappingVersion(predicateCaptor.capture());
verifyVersionPredicate(principal, predicateCaptor.getValue());
verifyNoMoreInteractions(usersStore);
if (new KibanaUser(enabled).equals(expectedUser)) {
assertWarnings("The user [kibana] is deprecated and will be removed in a future version of Elasticsearch. " +
"Please use the [kibana_system] user instead.");
}
}
public void testLookup() throws Exception {
@ -264,8 +270,8 @@ public class ReservedRealmTests extends ESTestCase {
PlainActionFuture<Collection<User>> userFuture = new PlainActionFuture<>();
reservedRealm.users(userFuture);
assertThat(userFuture.actionGet(),
containsInAnyOrder(new ElasticUser(true), new KibanaUser(true), new LogstashSystemUser(true),
new BeatsSystemUser(true), new APMSystemUser(true), new RemoteMonitoringUser(true)));
containsInAnyOrder(new ElasticUser(true), new KibanaUser(true), new KibanaSystemUser(true),
new LogstashSystemUser(true), new BeatsSystemUser(true), new APMSystemUser(true), new RemoteMonitoringUser(true)));
}
public void testGetUsersDisabled() {
@ -397,8 +403,8 @@ public class ReservedRealmTests extends ESTestCase {
new AnonymousUser(Settings.EMPTY), securityIndex, threadPool);
PlainActionFuture<AuthenticationResult> listener = new PlainActionFuture<>();
final String principal = randomFrom(KibanaUser.NAME, LogstashSystemUser.NAME, BeatsSystemUser.NAME, APMSystemUser.NAME,
RemoteMonitoringUser.NAME);
final String principal = randomFrom(KibanaUser.NAME, KibanaSystemUser.NAME, LogstashSystemUser.NAME, BeatsSystemUser.NAME,
APMSystemUser.NAME, RemoteMonitoringUser.NAME);
doAnswer((i) -> {
ActionListener callback = (ActionListener) i.getArguments()[1];
callback.onResponse(null);
@ -420,16 +426,16 @@ public class ReservedRealmTests extends ESTestCase {
new AnonymousUser(Settings.EMPTY), securityIndex, threadPool);
PlainActionFuture<AuthenticationResult> listener = new PlainActionFuture<>();
final String principal = randomFrom(KibanaUser.NAME, LogstashSystemUser.NAME, BeatsSystemUser.NAME, APMSystemUser.NAME,
RemoteMonitoringUser.NAME);
final String principal = randomFrom(KibanaUser.NAME, KibanaSystemUser.NAME, LogstashSystemUser.NAME, BeatsSystemUser.NAME,
APMSystemUser.NAME, RemoteMonitoringUser.NAME);
reservedRealm.doAuthenticate(new UsernamePasswordToken(principal, mockSecureSettings.getString("bootstrap.password")), listener);
final AuthenticationResult result = listener.get();
assertThat(result.getStatus(), is(AuthenticationResult.Status.TERMINATE));
}
private User randomReservedUser(boolean enabled) {
return randomFrom(new ElasticUser(enabled), new KibanaUser(enabled), new LogstashSystemUser(enabled),
new BeatsSystemUser(enabled), new APMSystemUser(enabled), new RemoteMonitoringUser(enabled));
return randomFrom(new ElasticUser(enabled), new KibanaUser(enabled), new KibanaSystemUser(enabled),
new LogstashSystemUser(enabled), new BeatsSystemUser(enabled), new APMSystemUser(enabled), new RemoteMonitoringUser(enabled));
}
/*

View File

@ -111,6 +111,18 @@ public class SetupPasswordToolTests extends CommandTestCase {
// elastic user is updated last
usersInSetOrder = new ArrayList<>(SetupPasswordTool.USERS);
usersInSetOrder.sort((user1, user2) -> {
if (SetupPasswordTool.USERS_WITH_SHARED_PASSWORDS.containsKey(user1)
&& SetupPasswordTool.USERS_WITH_SHARED_PASSWORDS.containsValue(user2)) {
return -1;
}
if (SetupPasswordTool.USERS_WITH_SHARED_PASSWORDS.containsKey(user2)
&& SetupPasswordTool.USERS_WITH_SHARED_PASSWORDS.containsValue(user1)) {
return 1;
}
return 0;
});
for (int i = 0; i < usersInSetOrder.size() - 1; i++) {
if (ElasticUser.NAME.equals(usersInSetOrder.get(i))) {
Collections.swap(usersInSetOrder, i, i + 1);
@ -118,6 +130,9 @@ public class SetupPasswordToolTests extends CommandTestCase {
}
for (String user : SetupPasswordTool.USERS) {
if (SetupPasswordTool.USERS_WITH_SHARED_PASSWORDS.containsValue(user)) {
continue;
}
terminal.addSecretInput(user + "-password");
terminal.addSecretInput(user + "-password");
}
@ -168,11 +183,26 @@ public class SetupPasswordToolTests extends CommandTestCase {
URL checkUrl = authenticateUrl(url);
inOrder.verify(httpClient).execute(eq("GET"), eq(checkUrl), eq(ElasticUser.NAME), eq(bootstrapPassword), any(CheckedSupplier.class),
any(CheckedFunction.class));
Map<String, String> capturedPasswords = new HashMap<>(usersInSetOrder.size());
for (String user : usersInSetOrder) {
URL urlWithRoute = passwordUrl(url, user);
ArgumentCaptor<CheckedSupplier<String, Exception>> passwordCaptor = ArgumentCaptor.forClass((Class) CheckedSupplier.class);
inOrder.verify(httpClient).execute(eq("PUT"), eq(urlWithRoute), eq(ElasticUser.NAME), eq(bootstrapPassword),
any(CheckedSupplier.class), any(CheckedFunction.class));
passwordCaptor.capture(), any(CheckedFunction.class));
String userPassword = passwordCaptor.getValue().get();
capturedPasswords.put(user, userPassword);
}
for (Map.Entry<String, String> entry : SetupPasswordTool.USERS_WITH_SHARED_PASSWORDS.entrySet()) {
assertEquals(capturedPasswords.get(entry.getKey()), capturedPasswords.get(entry.getValue()));
capturedPasswords.remove(entry.getKey());
capturedPasswords.remove(entry.getValue());
}
Set<String> uniqueCapturedPasswords = new HashSet<>(capturedPasswords.values());
assertEquals(uniqueCapturedPasswords.size(), capturedPasswords.size());
}
public void testAuthnFail() throws Exception {
@ -391,12 +421,13 @@ public class SetupPasswordToolTests extends CommandTestCase {
URL checkUrl = authenticateUrl(url);
inOrder.verify(httpClient).execute(eq("GET"), eq(checkUrl), eq(ElasticUser.NAME), eq(bootstrapPassword), any(CheckedSupplier.class),
any(CheckedFunction.class));
for (String user : usersInSetOrder) {
URL urlWithRoute = passwordUrl(url, user);
ArgumentCaptor<CheckedSupplier<String, Exception>> passwordCaptor = ArgumentCaptor.forClass((Class) CheckedSupplier.class);
inOrder.verify(httpClient).execute(eq("PUT"), eq(urlWithRoute), eq(ElasticUser.NAME), eq(bootstrapPassword),
passwordCaptor.capture(), any(CheckedFunction.class));
assertThat(passwordCaptor.getValue().get(), containsString(user + "-password"));
assertThat(passwordCaptor.getValue().get(), containsString(getExpectedPasswordForUser(user)));
}
}
@ -409,6 +440,10 @@ public class SetupPasswordToolTests extends CommandTestCase {
}
terminal.addTextInput("Y");
for (String user : SetupPasswordTool.USERS) {
if (SetupPasswordTool.USERS_WITH_SHARED_PASSWORDS.containsValue(user)) {
continue;
}
// fail in strength and match
int failCount = randomIntBetween(3, 10);
while (failCount-- > 0) {
@ -437,7 +472,7 @@ public class SetupPasswordToolTests extends CommandTestCase {
ArgumentCaptor<CheckedSupplier<String, Exception>> passwordCaptor = ArgumentCaptor.forClass((Class) CheckedSupplier.class);
inOrder.verify(httpClient).execute(eq("PUT"), eq(urlWithRoute), eq(ElasticUser.NAME), eq(bootstrapPassword),
passwordCaptor.capture(), any(CheckedFunction.class));
assertThat(passwordCaptor.getValue().get(), containsString(user + "-password"));
assertThat(passwordCaptor.getValue().get(), containsString(getExpectedPasswordForUser(user)));
}
}
@ -510,4 +545,16 @@ public class SetupPasswordToolTests extends CommandTestCase {
};
}
private String getExpectedPasswordForUser(String user) throws Exception {
if (SetupPasswordTool.USERS_WITH_SHARED_PASSWORDS.containsValue(user)) {
for(Map.Entry<String, String> entry : SetupPasswordTool.USERS_WITH_SHARED_PASSWORDS.entrySet()) {
if (entry.getValue().equals(user)) {
return entry.getKey() + "-password";
}
}
throw new Exception("Expected to find corresponding user for " + user);
}
return user + "-password";
}
}

View File

@ -83,7 +83,7 @@ public class PkiAuthDelegationIntegTests extends SecurityIntegTestCase {
"user_manage_security:" + usersPasswdHashed + "\n" +
"user_delegate_pki:" + usersPasswdHashed + "\n" +
"user_all:" + usersPasswdHashed + "\n" +
"kibana_system:" + usersPasswdHashed + "\n";
"my_kibana_system:" + usersPasswdHashed + "\n";
}
@Override
@ -109,7 +109,7 @@ public class PkiAuthDelegationIntegTests extends SecurityIntegTestCase {
"role_manage_security:user_manage_security\n" +
"role_delegate_pki:user_delegate_pki\n" +
"role_all:user_all\n" +
"kibana_system:kibana_system\n";
"kibana_system:my_kibana_system\n";
}
@Override
@ -140,7 +140,7 @@ public class PkiAuthDelegationIntegTests extends SecurityIntegTestCase {
}
try (RestHighLevelClient restClient = new TestRestHighLevelClient()) {
for (String delegateeUsername : Arrays.asList("user_all", "user_delegate_pki", "kibana_system")) {
for (String delegateeUsername : Arrays.asList("user_all", "user_delegate_pki", "my_kibana_system")) {
// delegate
RequestOptions.Builder optionsBuilder = RequestOptions.DEFAULT.toBuilder();
optionsBuilder.addHeader("Authorization",
@ -177,7 +177,7 @@ public class PkiAuthDelegationIntegTests extends SecurityIntegTestCase {
}
try (RestHighLevelClient restClient = new TestRestHighLevelClient()) {
String delegateeUsername = randomFrom("user_all", "user_delegate_pki", "kibana_system");
String delegateeUsername = randomFrom("user_all", "user_delegate_pki", "my_kibana_system");
// delegate
RequestOptions.Builder optionsBuilder = RequestOptions.DEFAULT.toBuilder();
optionsBuilder.addHeader("Authorization",

View File

@ -10,6 +10,7 @@ import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.core.security.user.AsyncSearchUser;
import org.elasticsearch.xpack.core.security.user.ElasticUser;
import org.elasticsearch.xpack.core.security.user.InternalUserSerializationHelper;
import org.elasticsearch.xpack.core.security.user.KibanaSystemUser;
import org.elasticsearch.xpack.core.security.user.KibanaUser;
import org.elasticsearch.xpack.core.security.user.SystemUser;
import org.elasticsearch.xpack.core.security.user.User;
@ -124,5 +125,12 @@ public class UserSerializationTests extends ESTestCase {
readFrom = User.readFrom(output.bytes().streamInput());
assertEquals(kibanaUser, readFrom);
final KibanaSystemUser kibanaSystemUser = new KibanaSystemUser(true);
output = new BytesStreamOutput();
User.writeTo(kibanaSystemUser, output);
readFrom = User.readFrom(output.bytes().streamInput());
assertEquals(kibanaSystemUser, readFrom);
}
}

View File

@ -9,6 +9,7 @@ import org.elasticsearch.cli.MockTerminal;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.WarningsHandler;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.common.io.PathUtils;
@ -98,7 +99,7 @@ public class SetupPasswordToolIT extends ESRestTestCase {
}
});
assertEquals(6, userPasswordMap.size());
assertEquals(7, userPasswordMap.size());
userPasswordMap.entrySet().forEach(entry -> {
final String basicHeader = "Basic " +
Base64.getEncoder().encodeToString((entry.getKey() + ":" + entry.getValue()).getBytes(StandardCharsets.UTF_8));
@ -106,6 +107,10 @@ public class SetupPasswordToolIT extends ESRestTestCase {
Request request = new Request("GET", "/_security/_authenticate");
RequestOptions.Builder options = request.getOptions().toBuilder();
options.addHeader("Authorization", basicHeader);
if ("kibana".equals(entry.getKey())) {
// the kibana user is deprecated so a warning header is expected
options.setWarningsHandler(WarningsHandler.PERMISSIVE);
}
request.setOptions(options);
Map<String, Object> userInfoMap = entityAsMap(client().performRequest(request));
assertEquals(entry.getKey(), userInfoMap.get("username"));