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:
parent
9030c3ae77
commit
c22f618a1a
|
@ -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;
|
||||
|
@ -37,22 +38,32 @@ import java.util.function.Function;
|
|||
*/
|
||||
public class SetupPasswordTool extends MultiCommand {
|
||||
|
||||
private static final char[] CHARS = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" +
|
||||
private static final char[] CHARS = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" +
|
||||
"~!@#$%^&*-_=+?").toCharArray();
|
||||
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));
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in New Issue