Modify password tool to pull password from keystore (elastic/x-pack-elasticsearch#1951)

This is related to elastic/x-pack-elasticsearch#1217. This commit modifies the password setup tool
to pull the password for the `elastic` user from the keystore.

Original commit: elastic/x-pack-elasticsearch@fbc71f393d
This commit is contained in:
Tim Brooks 2017-07-11 12:50:53 -05:00 committed by GitHub
parent 9030c3ae77
commit c22f618a1a
2 changed files with 44 additions and 16 deletions

View File

@ -14,6 +14,7 @@ import org.elasticsearch.cli.Terminal;
import org.elasticsearch.cli.UserException;
import org.elasticsearch.common.Booleans;
import org.elasticsearch.common.CheckedFunction;
import org.elasticsearch.common.settings.KeyStoreWrapper;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.json.JsonXContent;
@ -42,17 +43,27 @@ public class SetupPasswordTool extends MultiCommand {
private static final String[] USERS = new String[]{ElasticUser.NAME, KibanaUser.NAME, LogstashSystemUser.NAME, BeatsSystemUser.NAME};
private final Function<Environment, CommandLineHttpClient> clientFunction;
private final CheckedFunction<Environment, KeyStoreWrapper, Exception> keyStoreFunction;
private CommandLineHttpClient client;
SetupPasswordTool() {
this((environment) -> new CommandLineHttpClient(environment.settings(), environment));
this((environment) -> new CommandLineHttpClient(environment.settings(), environment),
(environment) -> {
KeyStoreWrapper keyStoreWrapper = KeyStoreWrapper.load(environment.configFile());
if (keyStoreWrapper == null) {
throw new UserException(ExitCodes.CONFIG, "Keystore does not exist");
}
return keyStoreWrapper;
});
}
SetupPasswordTool(Function<Environment, CommandLineHttpClient> clientFunction) {
SetupPasswordTool(Function<Environment, CommandLineHttpClient> clientFunction,
CheckedFunction<Environment, KeyStoreWrapper, Exception> keyStoreFunction) {
super("Sets the passwords for reserved users");
subcommands.put("auto", new AutoSetup());
subcommands.put("interactive", new InteractiveSetup());
this.clientFunction = clientFunction;
this.keyStoreFunction = keyStoreFunction;
}
public static void main(String[] args) throws Exception {
@ -135,7 +146,7 @@ public class SetupPasswordTool extends MultiCommand {
try (SecureString password2 = new SecureString(terminal.readSecret("Reenter password for [" + user + "]: "))) {
if (password1.equals(password2) == false) {
password1.close();
throw new UserException(ExitCodes.USAGE, "Passwords for user [" + user+ "] do not match");
throw new UserException(ExitCodes.USAGE, "Passwords for user [" + user + "] do not match");
}
}
return password1;
@ -157,7 +168,7 @@ public class SetupPasswordTool extends MultiCommand {
private OptionSpec<String> noPromptOption;
private String elasticUser = ElasticUser.NAME;
private SecureString elasticUserPassword = ReservedRealm.EMPTY_PASSWORD_TEXT;
private SecureString elasticUserPassword;
private String url;
SetupCommand(String description) {
@ -165,11 +176,17 @@ public class SetupPasswordTool extends MultiCommand {
setParser();
}
void setupOptions(OptionSet options, Environment env) {
void setupOptions(OptionSet options, Environment env) throws Exception {
client = clientFunction.apply(env);
KeyStoreWrapper keyStore = keyStoreFunction.apply(env);
String providedUrl = urlOption.value(options);
url = providedUrl == null ? "http://localhost:9200" : providedUrl;
setShouldPrompt(options);
// TODO: We currently do not support keystore passwords
keyStore.decrypt(new char[0]);
elasticUserPassword = keyStore.getString(ReservedRealm.BOOTSTRAP_ELASTIC_PASSWORD.getKey());
}
private void setParser() {
@ -199,6 +216,7 @@ public class SetupPasswordTool extends MultiCommand {
BiConsumer<String, SecureString> callback) throws Exception {
boolean isSuperUser = user.equals(elasticUser);
SecureString password = passwordFn.apply(user);
try {
String route = url + "/_xpack/security/user/" + user + "/_password";
String response = client.postURL("PUT", route, elasticUser, elasticUserPassword, buildPayload(password));

View File

@ -9,10 +9,12 @@ import org.elasticsearch.cli.Command;
import org.elasticsearch.cli.CommandTestCase;
import org.elasticsearch.cli.ExitCodes;
import org.elasticsearch.cli.UserException;
import org.elasticsearch.common.settings.KeyStoreWrapper;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.security.user.BeatsSystemUser;
import org.elasticsearch.xpack.security.user.ElasticUser;
import org.elasticsearch.xpack.security.user.KibanaUser;
@ -23,24 +25,33 @@ import org.mockito.InOrder;
import org.mockito.Mockito;
import java.io.IOException;
import java.security.GeneralSecurityException;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.contains;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
public class SetupPasswordToolTests extends CommandTestCase {
private final String pathHomeParameter = "-Epath.home=" + createTempDir();
private SecureString bootstrapPassword = new SecureString("bootstrap-password".toCharArray());
private final String ep = "elastic-password";
private final String kp = "kibana-password";
private final String lp = "logstash-password";
private final String bp = "beats-password";
private CommandLineHttpClient httpClient;
private KeyStoreWrapper keyStore;
@Before
public void setSecrets() {
public void setSecretsAndKeyStore() throws GeneralSecurityException {
this.keyStore = mock(KeyStoreWrapper.class);
this.httpClient = mock(CommandLineHttpClient.class);
when(keyStore.getString(ReservedRealm.BOOTSTRAP_ELASTIC_PASSWORD.getKey())).thenReturn(bootstrapPassword);
terminal.addSecretInput(ep);
terminal.addSecretInput(ep);
terminal.addSecretInput(kp);
@ -53,19 +64,20 @@ public class SetupPasswordToolTests extends CommandTestCase {
@Override
protected Command newCommand() {
this.httpClient = mock(CommandLineHttpClient.class);
return new SetupPasswordTool((e) -> httpClient);
return new SetupPasswordTool((e) -> httpClient, (e) -> keyStore);
}
public void testAutoSetup() throws Exception {
execute("auto", pathHomeParameter, "-b", "true");
verify(keyStore).decrypt(new char[0]);
ArgumentCaptor<String> passwordCaptor = ArgumentCaptor.forClass(String.class);
SecureString defaultPassword = new SecureString("".toCharArray());
InOrder inOrder = Mockito.inOrder(httpClient);
String elasticUrl = "http://localhost:9200/_xpack/security/user/elastic/_password";
inOrder.verify(httpClient).postURL(eq("PUT"), eq(elasticUrl), eq(ElasticUser.NAME), eq(defaultPassword), passwordCaptor.capture());
inOrder.verify(httpClient).postURL(eq("PUT"), eq(elasticUrl), eq(ElasticUser.NAME), eq(bootstrapPassword),
passwordCaptor.capture());
String[] users = {KibanaUser.NAME, LogstashSystemUser.NAME, BeatsSystemUser.NAME};
SecureString newPassword = new SecureString(parsePassword(passwordCaptor.getValue()).toCharArray());
@ -80,11 +92,11 @@ public class SetupPasswordToolTests extends CommandTestCase {
execute("auto", pathHomeParameter, "-u", url, "-b");
ArgumentCaptor<String> passwordCaptor = ArgumentCaptor.forClass(String.class);
SecureString defaultPassword = new SecureString("".toCharArray());
InOrder inOrder = Mockito.inOrder(httpClient);
String elasticUrl = url + "/_xpack/security/user/elastic/_password";
inOrder.verify(httpClient).postURL(eq("PUT"), eq(elasticUrl), eq(ElasticUser.NAME), eq(defaultPassword), passwordCaptor.capture());
inOrder.verify(httpClient).postURL(eq("PUT"), eq(elasticUrl), eq(ElasticUser.NAME), eq(bootstrapPassword),
passwordCaptor.capture());
String[] users = {KibanaUser.NAME, LogstashSystemUser.NAME, BeatsSystemUser.NAME};
SecureString newPassword = new SecureString(parsePassword(passwordCaptor.getValue()).toCharArray());
@ -99,12 +111,10 @@ public class SetupPasswordToolTests extends CommandTestCase {
execute("interactive", pathHomeParameter);
SecureString defaultPassword = new SecureString("".toCharArray());
InOrder inOrder = Mockito.inOrder(httpClient);
String elasticUrl = "http://localhost:9200/_xpack/security/user/elastic/_password";
SecureString newPassword = new SecureString(ep.toCharArray());
inOrder.verify(httpClient).postURL(eq("PUT"), eq(elasticUrl), eq(ElasticUser.NAME), eq(defaultPassword), contains(ep));
inOrder.verify(httpClient).postURL(eq("PUT"), eq(elasticUrl), eq(ElasticUser.NAME), eq(bootstrapPassword), contains(ep));
String kibanaUrl = "http://localhost:9200/_xpack/security/user/" + KibanaUser.NAME + "/_password";
inOrder.verify(httpClient).postURL(eq("PUT"), eq(kibanaUrl), eq(ElasticUser.NAME), eq(newPassword), contains(kp));