mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
keyring command line (#13169)
Co-authored-by: Charlie Voiselle <464492+angrycub@users.noreply.github.com>
This commit is contained in:
@@ -80,9 +80,9 @@ func (k *Keyring) Rotate(opts *KeyringRotateOptions, w *WriteOptions) (*RootKeyM
|
||||
qp.Set("full", "true")
|
||||
}
|
||||
}
|
||||
resp := &RootKeyMeta{}
|
||||
resp := &struct{ Key *RootKeyMeta }{}
|
||||
wm, err := k.client.write("/v1/operator/keyring/rotate?"+qp.Encode(), nil, resp, w)
|
||||
return resp, wm, err
|
||||
return resp.Key, wm, err
|
||||
}
|
||||
|
||||
// KeyringRotateOptions are parameters for the Rotate API
|
||||
|
||||
@@ -591,6 +591,11 @@ func Commands(metaPtr *Meta, agentUi cli.Ui) map[string]cli.CommandFactory {
|
||||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
"operator secure-variables keyring": func() (cli.Command, error) {
|
||||
return &OperatorSecureVariablesKeyringCommand{
|
||||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
"operator snapshot": func() (cli.Command, error) {
|
||||
return &OperatorSnapshotCommand{
|
||||
Meta: meta,
|
||||
|
||||
217
command/operator_secure_variables_keyring.go
Normal file
217
command/operator_secure_variables_keyring.go
Normal file
@@ -0,0 +1,217 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/posener/complete"
|
||||
|
||||
"github.com/hashicorp/nomad/api"
|
||||
)
|
||||
|
||||
// OperatorSecureVariablesKeyringCommand is a Command implementation
|
||||
// that handles querying, installing, and removing secure variables
|
||||
// encryption keys from a keyring.
|
||||
type OperatorSecureVariablesKeyringCommand struct {
|
||||
Meta
|
||||
}
|
||||
|
||||
func (c *OperatorSecureVariablesKeyringCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: nomad operator secure-variables keyring [options]
|
||||
|
||||
Manages encryption keys used for storing secure variables. This command may be
|
||||
used to examine active encryption keys in the cluster, rotate keys, add new
|
||||
keys from backups, or remove unused keys.
|
||||
|
||||
If ACLs are enabled, this command requires a management token.
|
||||
|
||||
General Options:
|
||||
|
||||
` + generalOptionsUsage(usageOptsDefault|usageOptsNoNamespace) + `
|
||||
|
||||
Keyring Options:
|
||||
|
||||
-install=<file> Install a new encryption key from file. The key file must
|
||||
be a JSON file previously written by Nomad to the keystore.
|
||||
|
||||
-list List the currently installed keys. This list returns key
|
||||
metadata and not sensitive key material.
|
||||
|
||||
-remove=<key> Remove the given key from the cluster. This operation may
|
||||
only be performed on keys that are not the active key.
|
||||
|
||||
-rotate Generate a new encryption key for all future variables.
|
||||
Allows the use of the -full flag.
|
||||
|
||||
-full When used with -rotate, decrypt all variables and
|
||||
re-encrypt with the new key. The command will immediately
|
||||
return and the re-encryption process will run asynchronously
|
||||
on the leader.
|
||||
|
||||
-verbose Show full information.
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (c *OperatorSecureVariablesKeyringCommand) Synopsis() string {
|
||||
return "Manages secure variables encryption keys"
|
||||
}
|
||||
|
||||
func (c *OperatorSecureVariablesKeyringCommand) AutocompleteFlags() complete.Flags {
|
||||
return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
|
||||
complete.Flags{
|
||||
"-install": complete.PredictFiles("*.json"),
|
||||
"-list": complete.PredictNothing,
|
||||
"-remove": complete.PredictAnything,
|
||||
"-rotate": complete.PredictNothing,
|
||||
"-full": complete.PredictNothing,
|
||||
"-verbose": complete.PredictNothing,
|
||||
})
|
||||
}
|
||||
func (c *OperatorSecureVariablesKeyringCommand) AutocompleteArgs() complete.Predictor {
|
||||
return complete.PredictNothing
|
||||
}
|
||||
|
||||
func (c *OperatorSecureVariablesKeyringCommand) Name() string { return "secure-variables keyring" }
|
||||
|
||||
func (c *OperatorSecureVariablesKeyringCommand) Run(args []string) int {
|
||||
var installKey, removeKey string
|
||||
var listKeys, rotateKey, rotateFull, verbose bool
|
||||
|
||||
flags := c.Meta.FlagSet("secure-variables keyring", FlagSetClient)
|
||||
flags.Usage = func() { c.Ui.Output(c.Help()) }
|
||||
|
||||
flags.StringVar(&installKey, "install", "", "install key")
|
||||
flags.BoolVar(&listKeys, "list", false, "list keys")
|
||||
flags.StringVar(&removeKey, "remove", "", "remove key")
|
||||
flags.BoolVar(&rotateKey, "rotate", false, "rotate key")
|
||||
|
||||
flags.BoolVar(&rotateFull, "full", false, "full key rotation")
|
||||
flags.BoolVar(&verbose, "verbose", false, "")
|
||||
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Only accept a single argument
|
||||
if rotateKey && listKeys {
|
||||
c.Ui.Error("Only a single action is allowed")
|
||||
c.Ui.Error(commandErrorText(c))
|
||||
return 1
|
||||
}
|
||||
found := listKeys || rotateKey
|
||||
for _, arg := range []string{installKey, removeKey} {
|
||||
if found && len(arg) > 0 {
|
||||
c.Ui.Error("Only a single action is allowed")
|
||||
c.Ui.Error(commandErrorText(c))
|
||||
return 1
|
||||
}
|
||||
found = found || len(arg) > 0
|
||||
}
|
||||
|
||||
// Fail fast if no actionable args were passed
|
||||
if !found {
|
||||
c.Ui.Error("No actionable argument was passed")
|
||||
c.Ui.Error("One of '-install', '-list', '-remove' or '-rotate' flags must be set")
|
||||
c.Ui.Error(commandErrorText(c))
|
||||
return 1
|
||||
}
|
||||
|
||||
client, err := c.Meta.Client()
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error creating nomad cli client: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
if listKeys {
|
||||
resp, _, err := client.Keyring().List(nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("error: %s", err))
|
||||
return 1
|
||||
}
|
||||
c.handleKeysResponse(resp, verbose)
|
||||
return 0
|
||||
}
|
||||
|
||||
if rotateKey {
|
||||
resp, _, err := client.Keyring().Rotate(
|
||||
&api.KeyringRotateOptions{Full: rotateFull}, nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("error: %s", err))
|
||||
return 1
|
||||
}
|
||||
c.handleKeysResponse([]*api.RootKeyMeta{resp}, verbose)
|
||||
return 0
|
||||
}
|
||||
|
||||
if removeKey != "" {
|
||||
_, err := client.Keyring().Delete(&api.KeyringDeleteOptions{
|
||||
KeyID: removeKey,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("error: %s", err))
|
||||
return 1
|
||||
}
|
||||
c.Ui.Output(fmt.Sprintf("Removed encryption key %s", removeKey))
|
||||
return 0
|
||||
}
|
||||
|
||||
if installKey != "" {
|
||||
|
||||
if fi, err := os.Stat(installKey); (installKey == "-" || err == nil) && !fi.IsDir() {
|
||||
var buf []byte
|
||||
if installKey == "-" {
|
||||
buf, err = ioutil.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to read stdin: %v", err))
|
||||
return 1
|
||||
}
|
||||
} else {
|
||||
buf, err = ioutil.ReadFile(installKey)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to read file: %v", err))
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
key := &api.RootKey{}
|
||||
dec := json.NewDecoder(bytes.NewBuffer(buf))
|
||||
if err := dec.Decode(key); err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to parse key file: %v", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
_, err := client.Keyring().Update(key, nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("error: %s", err))
|
||||
return 1
|
||||
}
|
||||
c.Ui.Output(fmt.Sprintf("Installed encryption key %s", key.Meta.KeyID))
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Should never make it here
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *OperatorSecureVariablesKeyringCommand) handleKeysResponse(keys []*api.RootKeyMeta, verbose bool) {
|
||||
length := fullId
|
||||
if !verbose {
|
||||
length = 8
|
||||
}
|
||||
out := make([]string, len(keys)+1)
|
||||
out[0] = "Key|Active|Create Time"
|
||||
i := 1
|
||||
for _, k := range keys {
|
||||
out[i] = fmt.Sprintf("%s|%v|%s",
|
||||
k.KeyID[:length], k.Active, formatTime(k.CreateTime))
|
||||
i = i + 1
|
||||
}
|
||||
c.Ui.Output(formatList(out))
|
||||
}
|
||||
Reference in New Issue
Block a user