Skip to content

Add 'show' command to the keystore CLI#76693

Merged
tvernum merged 13 commits into
elastic:masterfrom
tvernum:keystore-show
Sep 23, 2021
Merged

Add 'show' command to the keystore CLI#76693
tvernum merged 13 commits into
elastic:masterfrom
tvernum:keystore-show

Conversation

@tvernum

@tvernum tvernum commented Aug 19, 2021

Copy link
Copy Markdown
Contributor

This adds a new "elasticsearch-keystore show" command that displays
the value of a single secure setting from the keystore.

Additionally, support for direct (stream) output has been added to
the Terminal class so that non-UTF8 values can be written to
stdout, and accordingly this command works correctly:

elasticsearch-keystore show xpack.watcher.encryption_key > watcher.key

Resolves: #57261

This adds a new "elasticsearch-keystore show" command that displays
the value of a single secure setting from the keystore.

An optional `-o` (or `--output`) parameter can be used to direct
output to a file.

The `-o` option is required for binary keystore values
because the CLI `Terminal` class does not support writing binary data.
Hence this command:

    elasticsearch-keystore show xpack.watcher.encryption_key > watcher.key

would not produce a file with the correct contents.
@tvernum tvernum requested review from jkakavas and rjernst August 19, 2021 05:03
@elasticmachine elasticmachine added the Team:Core/Infra Meta label for core/infra team label Aug 19, 2021
@elasticmachine

Copy link
Copy Markdown
Collaborator

Pinging @elastic/es-core-infra (Team:Core/Infra)

| [remove <setting>]
| [show [-o <output-file>] <setting>]
| [upgrade]
) [-h, --help] ([-s, --silent] | [-v, --verbose])

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did some reformatting here because it was getting hard to read. Happy to revert if people don't like it.

node.
Any cChanges to the keystore will take effect when you restart {es}.
Some secure settings can be explicitly <<reloadable-secure-settings, reloaded>>
without restart.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that this content was out of date, so I added some more explanation (the wording is adapted from the [reloadable-secure-settings] docs section)

* Side Public License, v 1.
*/

package org.elasticsearch.common.settings.cli;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put this in the .setting.cli package to deal with the split packages check.
I can move it back to .settings instead, and update the exceptions list, but I think our preference is to use a new package.

However, I couldn't put the test into the setting.cli package, because it needs to access package protected methods on KeyStoreWrapper.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add a TestKeyStoreWrapper that inherits KeyStoreWrapper and makes those public for tests to call. It could be in a followup though.

I tried to test `show` in packaging tests, but it doesn't work
correctly. In fact we don't even test `add` in packaging tests, so
testing `show` isn't really necessary either

@jkakavas jkakavas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added one comment and one suggestion ( prompt to overwrite ). I don't think we should block this PR for any of the above

}

private void show(KeyStoreWrapper keyStore, String setting, Terminal terminal) {
terminal.println(Terminal.Verbosity.SILENT, String.valueOf(keyStore.getString(setting)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any specific reason why we use String.valueOf to call the SecureString#toString here instead of doing toString ourselves? Just curious, happy eitherway

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Force of habit mostly (for fear of NPEs).
terminal.println requires a string (not a CharSequence) so I have to explicitly call toString, I tend to favour valueOf for that since it's null safe.

Comment thread docs/reference/commands/keystore.asciidoc Outdated
Co-authored-by: Ioannis Kakavas <ikakavas@protonmail.com>

@rjernst rjernst left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests here are great, thank you!

Regarding output of binary files, I don't think we need to have an additional option.

There are two cases for Terminal: either we are connected to a Console, or we are not. We have the "system terminal" concept to simplify our interaction as most of the time with CLIs we are interacting with users. Since Console limits the developer to character data, we likewise limit our Terminal abstraction to character data.

I think we should do the following:

  • Read the key from the keystore as binary data, into memory. Keystore values are not large, by design. We can then use a utf8 validator (or for now just use a try/catch decoding to a string) to detect whether the data is utf8. This tells us whether it is character data.
  • If it is character data, print it using Terminal
  • If it is not character data, print it using System.out. However, for testability, we should add a getOutputStream() to Terminal, so that we can mock it out. Additionally, I think we can use that method to give an error message to the use if they are trying to write a binary value to the console. ConsoleTerminal.getOutputStream() can return null, and we can give an error to the user that it's binary data, and they should redirect to a file.

@tvernum tvernum requested a review from rjernst August 30, 2021 08:04

@rjernst rjernst left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, one small ask to ensure non UTF8 terminals work with this.

* Side Public License, v 1.
*/

package org.elasticsearch.common.settings.cli;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add a TestKeyStoreWrapper that inherits KeyStoreWrapper and makes those public for tests to call. It could be in a followup though.

final BytesReference bytes = org.elasticsearch.common.io.Streams.readFully(input);
final OutputStream output = terminal.getOutputStream();
if (output != null) {
bytes.writeTo(output);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assumes that the terminal is using UTF8, which may not be the case. I think we should flip this around, so that we first try decoding utf8, and print if possible, then fall back to writing to the output stream (when we know the data is not utf8).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I had it in this sequence is because in my (somewhat limited) testing, we never seem to have an output stream unless writing to a file, and if we're writing to a file then we probably want byte-equivalent output.

If we flip it around then

bin/elasticsearch-keystore add-file some.setting my-file1
bin/elasticsearch-keystore show some.setting > my-file2

might mean that my-file1 and my-file2 are not byte identical. At the very least we need to decide whether to append a newline or not (ideally with file output we wouldn't, with terminal output we would).

I'll make the switch in the sequence and select print vs println depending on whether the terminal has an output stream. It's not ideal, but I don't think there's a perfect answer.

@tvernum tvernum merged commit 6125067 into elastic:master Sep 23, 2021
@tvernum

tvernum commented Sep 23, 2021

Copy link
Copy Markdown
Contributor Author

Argh. I left the old commit description in when I merged.
If anyone comes here because they're confused by the commit message - the commit message is wrong, the docs and PR description are correct.

tvernum added a commit to tvernum/elasticsearch that referenced this pull request Oct 5, 2021
This adds a new "elasticsearch-keystore show" command that displays
the value of a single secure setting from the keystore.

Additionally, support for direct (stream) output has been added to
the Terminal class so that non-UTF8 values can be written to
stdout, and accordingly this command works correctly:

    elasticsearch-keystore show xpack.watcher.encryption_key > watcher.key

Backport of: elastic#76693

Co-authored-by: Ioannis Kakavas <ikakavas@protonmail.com>
elasticsearchmachine pushed a commit that referenced this pull request Oct 5, 2021
This adds a new "elasticsearch-keystore show" command that displays
the value of a single secure setting from the keystore.

Additionally, support for direct (stream) output has been added to
the Terminal class so that non-UTF8 values can be written to
stdout, and accordingly this command works correctly:

    elasticsearch-keystore show xpack.watcher.encryption_key > watcher.key

Backport of: #76693

Co-authored-by: Ioannis Kakavas <ikakavas@protonmail.com>

Co-authored-by: Ioannis Kakavas <ikakavas@protonmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

:Core/Infra/Settings Settings infrastructure and APIs >enhancement Team:Core/Infra Meta label for core/infra team v7.16.0 v8.0.0-beta1

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow elastic-keystore to optionally show values.

5 participants