-
Notifications
You must be signed in to change notification settings - Fork 25.2k
Add infrastructure for elasticsearch keystore #22335
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fb690ef
d4288cc
4e4a40d
6e406ae
eb596d7
42ebfe7
cd6e3f4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
package org.elasticsearch.common.settings; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.InputStream; | ||
import java.io.InputStreamReader; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.Arrays; | ||
|
||
import joptsimple.OptionSet; | ||
import joptsimple.OptionSpec; | ||
import org.elasticsearch.cli.EnvironmentAwareCommand; | ||
import org.elasticsearch.cli.ExitCodes; | ||
import org.elasticsearch.cli.Terminal; | ||
import org.elasticsearch.cli.UserException; | ||
import org.elasticsearch.env.Environment; | ||
|
||
/** | ||
* A subcommand for the keystore cli which adds a string setting. | ||
*/ | ||
class AddStringKeyStoreCommand extends EnvironmentAwareCommand { | ||
|
||
private final OptionSpec<Void> stdinOption; | ||
private final OptionSpec<Void> forceOption; | ||
private final OptionSpec<String> arguments; | ||
|
||
AddStringKeyStoreCommand() { | ||
super("Add a string setting to the keystore"); | ||
this.stdinOption = parser.acceptsAll(Arrays.asList("x", "stdin"), "Read setting value from stdin"); | ||
this.forceOption = parser.acceptsAll(Arrays.asList("f", "force"), "Overwrite existing setting without prompting"); | ||
this.arguments = parser.nonOptions("setting name"); | ||
} | ||
|
||
// pkg private so tests can manipulate | ||
InputStream getStdin() { | ||
return System.in; | ||
} | ||
|
||
@Override | ||
protected void execute(Terminal terminal, OptionSet options, Environment env) throws Exception { | ||
KeyStoreWrapper keystore = KeyStoreWrapper.load(env.configFile()); | ||
if (keystore == null) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More of a user friendliness / usability aspect, but it would be great if we invoked the CreateKeyStoreCommand here. It is pretty minor since the create command would ideally only be run once. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we should. I would rather it be an error and explicit rather than magic/leniency. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's also fine with me. It's not a big deal since it's probably a one time only operation |
||
throw new UserException(ExitCodes.DATA_ERROR, "Elasticsearch keystore not found. Use 'create' command to create one."); | ||
} | ||
|
||
keystore.decrypt(new char[0] /* TODO: prompt for password when they are supported */); | ||
|
||
String setting = arguments.value(options); | ||
if (keystore.getSettings().contains(setting) && options.has(forceOption) == false) { | ||
if (terminal.promptYesNo("Setting " + setting + " already exists. Overwrite?", false) == false) { | ||
terminal.println("Exiting without modifying keystore."); | ||
return; | ||
} | ||
} | ||
|
||
final char[] value; | ||
if (options.has(stdinOption)) { | ||
BufferedReader stdinReader = new BufferedReader(new InputStreamReader(getStdin(), StandardCharsets.UTF_8)); | ||
value = stdinReader.readLine().toCharArray(); | ||
} else { | ||
value = terminal.readSecret("Enter value for " + setting + ": "); | ||
} | ||
|
||
try { | ||
keystore.setStringSetting(setting, value); | ||
} catch (IllegalArgumentException e) { | ||
throw new UserException(ExitCodes.DATA_ERROR, "String value must contain only ASCII"); | ||
} | ||
keystore.save(env.configFile()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
package org.elasticsearch.common.settings; | ||
|
||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
|
||
import joptsimple.OptionSet; | ||
import org.elasticsearch.cli.EnvironmentAwareCommand; | ||
import org.elasticsearch.cli.Terminal; | ||
import org.elasticsearch.env.Environment; | ||
|
||
/** | ||
* A subcommand for the keystore cli to create a new keystore. | ||
*/ | ||
class CreateKeyStoreCommand extends EnvironmentAwareCommand { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. final? |
||
|
||
CreateKeyStoreCommand() { | ||
super("Creates a new elasticsearch keystore"); | ||
} | ||
|
||
@Override | ||
protected void execute(Terminal terminal, OptionSet options, Environment env) throws Exception { | ||
Path keystoreFile = KeyStoreWrapper.keystorePath(env.configFile()); | ||
if (Files.exists(keystoreFile)) { | ||
if (terminal.promptYesNo("An elasticsearch keystore already exists. Overwrite?", false) == false) { | ||
terminal.println("Exiting without creating keystore."); | ||
return; | ||
} | ||
} | ||
|
||
|
||
char[] password = new char[0];// terminal.readSecret("Enter passphrase (empty for no passphrase): "); | ||
/* TODO: uncomment when entering passwords on startup is supported | ||
char[] passwordRepeat = terminal.readSecret("Enter same passphrase again: "); | ||
if (Arrays.equals(password, passwordRepeat) == false) { | ||
throw new UserException(ExitCodes.DATA_ERROR, "Passphrases are not equal, exiting."); | ||
}*/ | ||
|
||
KeyStoreWrapper keystore = KeyStoreWrapper.create(password); | ||
keystore.save(env.configFile()); | ||
terminal.println("Created elasticsearch keystore in " + env.configFile()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
package org.elasticsearch.common.settings; | ||
|
||
import org.elasticsearch.cli.MultiCommand; | ||
import org.elasticsearch.cli.Terminal; | ||
|
||
/** | ||
* A cli tool for managing secrets in the elasticsearch keystore. | ||
*/ | ||
public class KeyStoreCli extends MultiCommand { | ||
|
||
private KeyStoreCli() { | ||
super("A tool for managing settings stored in the elasticsearch keystore"); | ||
subcommands.put("create", new CreateKeyStoreCommand()); | ||
subcommands.put("list", new ListKeyStoreCommand()); | ||
subcommands.put("add", new AddStringKeyStoreCommand()); | ||
subcommands.put("remove", new RemoveSettingKeyStoreCommand()); | ||
} | ||
|
||
public static void main(String[] args) throws Exception { | ||
exit(new KeyStoreCli().main(args, Terminal.DEFAULT)); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
final?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These can't be final because they are subclassed in tests in order to manipulate eg the environment the command runs with.