operator generate-root -decode: allow token from stdin (#12881)
* operator generate-root -decode: allow token from stdin Allow passing "-" as the value for -decode, causing the encoded token to be read from stdin. This is intended to prevent leaking the encoded token + otp into process logs in enterprise environments. * add changelog entry for PR12881 * add check/test for empty decode value passed via stdin
This commit is contained in:
parent
b76d2cd09c
commit
ceac6e913d
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
command: operator generate-root -decode: allow passing encoded token via stdin
|
||||||
|
```
|
|
@ -130,7 +130,8 @@ func (c *OperatorGenerateRootCommand) Flags() *FlagSets {
|
||||||
Default: "",
|
Default: "",
|
||||||
EnvVar: "",
|
EnvVar: "",
|
||||||
Completion: complete.PredictAnything,
|
Completion: complete.PredictAnything,
|
||||||
Usage: "The value to decode; setting this triggers a decode operation.",
|
Usage: "The value to decode; setting this triggers a decode operation. " +
|
||||||
|
" If the value is \"-\" then read the encoded token from stdin.",
|
||||||
})
|
})
|
||||||
|
|
||||||
f.BoolVar(&BoolVar{
|
f.BoolVar(&BoolVar{
|
||||||
|
@ -328,6 +329,27 @@ func (c *OperatorGenerateRootCommand) decode(client *api.Client, encoded, otp st
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if encoded == "-" {
|
||||||
|
// Pull our fake stdin if needed
|
||||||
|
stdin := (io.Reader)(os.Stdin)
|
||||||
|
if c.testStdin != nil {
|
||||||
|
stdin = c.testStdin
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if _, err := io.Copy(&buf, stdin); err != nil {
|
||||||
|
c.UI.Error(fmt.Sprintf("Failed to read from stdin: %s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
encoded = buf.String()
|
||||||
|
|
||||||
|
if encoded == "" {
|
||||||
|
c.UI.Error("Missing encoded value. When using -decode=\"-\" value must be passed via stdin.")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
f := client.Sys().GenerateRootStatus
|
f := client.Sys().GenerateRootStatus
|
||||||
switch kind {
|
switch kind {
|
||||||
case generateRootDR:
|
case generateRootDR:
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build !race
|
||||||
// +build !race
|
// +build !race
|
||||||
|
|
||||||
package command
|
package command
|
||||||
|
@ -158,6 +159,96 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("decode_from_stdin", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
encoded := "Bxg9JQQqOCNKBRICNwMIRzo2J3cWCBRi"
|
||||||
|
otp := "3JhHkONiyiaNYj14nnD9xZQS"
|
||||||
|
|
||||||
|
client, closer := testVaultServer(t)
|
||||||
|
defer closer()
|
||||||
|
|
||||||
|
stdinR, stdinW := io.Pipe()
|
||||||
|
go func() {
|
||||||
|
stdinW.Write([]byte(encoded))
|
||||||
|
stdinW.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
ui, cmd := testOperatorGenerateRootCommand(t)
|
||||||
|
cmd.client = client
|
||||||
|
cmd.testStdin = stdinR
|
||||||
|
|
||||||
|
// Simulate piped output to print raw output
|
||||||
|
old := os.Stdout
|
||||||
|
_, w, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
os.Stdout = w
|
||||||
|
|
||||||
|
code := cmd.Run([]string{
|
||||||
|
"-decode", "-", // read from stdin
|
||||||
|
"-otp", otp,
|
||||||
|
})
|
||||||
|
if exp := 0; code != exp {
|
||||||
|
t.Errorf("expected %d to be %d", code, exp)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Close()
|
||||||
|
os.Stdout = old
|
||||||
|
|
||||||
|
expected := "4RUmoevJ3lsLni9sTXcNnRE1"
|
||||||
|
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
|
||||||
|
if combined != expected {
|
||||||
|
t.Errorf("expected %q to be %q", combined, expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("decode_from_stdin_empty", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
encoded := ""
|
||||||
|
otp := "3JhHkONiyiaNYj14nnD9xZQS"
|
||||||
|
|
||||||
|
client, closer := testVaultServer(t)
|
||||||
|
defer closer()
|
||||||
|
|
||||||
|
stdinR, stdinW := io.Pipe()
|
||||||
|
go func() {
|
||||||
|
stdinW.Write([]byte(encoded))
|
||||||
|
stdinW.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
ui, cmd := testOperatorGenerateRootCommand(t)
|
||||||
|
cmd.client = client
|
||||||
|
cmd.testStdin = stdinR
|
||||||
|
|
||||||
|
// Simulate piped output to print raw output
|
||||||
|
old := os.Stdout
|
||||||
|
_, w, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
os.Stdout = w
|
||||||
|
|
||||||
|
code := cmd.Run([]string{
|
||||||
|
"-decode", "-", // read from stdin
|
||||||
|
"-otp", otp,
|
||||||
|
})
|
||||||
|
if exp := 1; code != exp {
|
||||||
|
t.Errorf("expected %d to be %d", code, exp)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Close()
|
||||||
|
os.Stdout = old
|
||||||
|
|
||||||
|
expected := "Missing encoded value"
|
||||||
|
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
|
||||||
|
if !strings.Contains(combined, expected) {
|
||||||
|
t.Errorf("expected %q to contain %q", combined, expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("cancel", func(t *testing.T) {
|
t.Run("cancel", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,7 @@ flags](/docs/commands) included on all commands.
|
||||||
|
|
||||||
- `-decode` `(string: "")` - Decode and output the generated root token. This
|
- `-decode` `(string: "")` - Decode and output the generated root token. This
|
||||||
option requires the `-otp` flag be set to the OTP used during initialization.
|
option requires the `-otp` flag be set to the OTP used during initialization.
|
||||||
|
If value is "-" then read the encoded token from stdin.
|
||||||
|
|
||||||
- `-generate-otp` `(bool: false)` - Generate and print a high-entropy
|
- `-generate-otp` `(bool: false)` - Generate and print a high-entropy
|
||||||
one-time-password (OTP) suitable for use with the "-init" flag.
|
one-time-password (OTP) suitable for use with the "-init" flag.
|
||||||
|
|
Loading…
Reference in New Issue