Skip to content

Commit

Permalink
deprecate the kibana reserved user; introduce kibana_system user
Browse files Browse the repository at this point in the history
  • Loading branch information
legrego committed Apr 9, 2020
1 parent bafd129 commit b34e4bb
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public static boolean isReserved(String username, Settings settings) {
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:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

/*
* 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@

/**
* 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.util.Supplier;
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;
Expand All @@ -23,10 +24,12 @@
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;
Expand All @@ -40,6 +43,7 @@
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.
Expand All @@ -62,6 +66,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),
Expand Down Expand Up @@ -98,6 +104,7 @@ protected void doAuthenticate(UsernamePasswordToken token, ActionListener<Authen
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);
Expand Down Expand Up @@ -147,6 +154,8 @@ private User getUser(String username, ReservedUserInfo userInfo) {
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:
Expand Down Expand Up @@ -177,6 +186,9 @@ public void users(ActionListener<Collection<User>> listener) {
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));

Expand Down Expand Up @@ -220,6 +232,15 @@ private void getUserInfo(final String username, ActionListener<ReservedUserInfo>
}
}

private void logDeprecatedUser(final User user){
Map<String, Object> metadata = user.metadata();
boolean isDeprecatedUser = Boolean.TRUE.equals(metadata.get(MetadataUtils.DEPRECATED_METADATA_KEY));
if (isDeprecatedUser == true) {
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
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;
Expand Down Expand Up @@ -65,8 +66,8 @@
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);

private final Function<Environment, CommandLineHttpClient> clientFunction;
private final CheckedFunction<Environment, KeyStoreWrapper, Exception> keyStoreFunction;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
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;
Expand Down Expand Up @@ -108,8 +109,8 @@ public void setupReservedPasswords(RestClient restClient) throws IOException {
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()) + "\"}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
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;
Expand Down Expand Up @@ -85,8 +86,8 @@ void doExecute(ActionType<Response> action, Request request, ActionListener<Resp
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);
Expand All @@ -104,8 +105,8 @@ public void testPasswordUpsertWhenSetEnabledOnReservedUser() throws Exception {
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
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;
Expand Down Expand Up @@ -61,8 +62,8 @@ protected boolean addMockHttpTransport() {
}

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())))
Expand All @@ -81,8 +82,8 @@ public void testAuthenticate() {
*/
public void testAuthenticateAfterEnablingUser() throws IOException {
final RestHighLevelClient restClient = new TestRestHighLevelClient();
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) {
restClient.security().enableUser(new EnableUserRequest(username, RefreshPolicy.getDefault()), SECURITY_REQUEST_OPTIONS);
ClusterHealthResponse response = client()
Expand All @@ -97,8 +98,8 @@ public void testAuthenticateAfterEnablingUser() throws IOException {
}

public void testChangingPassword() throws IOException {
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()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
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;
Expand Down Expand Up @@ -249,8 +250,9 @@ public void testGetUsers() {
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() {
Expand Down Expand Up @@ -382,8 +384,8 @@ public void testNonElasticUsersCannotUseBootstrapPasswordWhenSecurityIndexExists
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);
Expand All @@ -405,16 +407,16 @@ public void testNonElasticUsersCannotUseBootstrapPasswordWhenSecurityIndexDoesNo
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));
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
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;
Expand Down Expand Up @@ -124,5 +125,12 @@ public void testReservedUserSerialization() throws Exception {
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);
}
}

0 comments on commit b34e4bb

Please sign in to comment.