github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/command/operator_keyring.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/hashicorp/nomad/api"
     8  	"github.com/mitchellh/cli"
     9  	"github.com/posener/complete"
    10  )
    11  
    12  // OperatorKeyringCommand is a Command implementation that handles querying, installing,
    13  // and removing gossip encryption keys from a keyring.
    14  type OperatorKeyringCommand struct {
    15  	Meta
    16  }
    17  
    18  func (c *OperatorKeyringCommand) Help() string {
    19  	helpText := `
    20  Usage: nomad operator keyring [options]
    21  
    22    Manages encryption keys used for gossip messages between Nomad servers. Gossip
    23    encryption is optional. When enabled, this command may be used to examine
    24    active encryption keys in the cluster, add new keys, and remove old ones. When
    25    combined, this functionality provides the ability to perform key rotation
    26    cluster-wide, without disrupting the cluster.
    27  
    28    All operations performed by this command can only be run against server nodes.
    29  
    30    All variations of the keyring command return 0 if all nodes reply and there
    31    are no errors. If any node fails to reply or reports failure, the exit code
    32    will be 1.
    33  
    34    If ACLs are enabled, this command requires a token with the 'agent:write'
    35    capability.
    36  
    37  General Options:
    38  
    39    ` + generalOptionsUsage(usageOptsDefault|usageOptsNoNamespace) + `
    40  
    41  Keyring Options:
    42  
    43    -install=<key>            Install a new encryption key. This will broadcast
    44                              the new key to all members in the cluster.
    45    -list                     List all keys currently in use within the cluster.
    46    -remove=<key>             Remove the given key from the cluster. This
    47                              operation may only be performed on keys which are
    48                              not currently the primary key.
    49    -use=<key>                Change the primary encryption key, which is used to
    50                              encrypt messages. The key must already be installed
    51                              before this operation can succeed.
    52  `
    53  	return strings.TrimSpace(helpText)
    54  }
    55  
    56  func (c *OperatorKeyringCommand) Synopsis() string {
    57  	return "Manages gossip layer encryption keys"
    58  }
    59  
    60  func (c *OperatorKeyringCommand) AutocompleteFlags() complete.Flags {
    61  	return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
    62  		complete.Flags{
    63  			"-install": complete.PredictAnything,
    64  			"-list":    complete.PredictNothing,
    65  			"-remove":  complete.PredictAnything,
    66  			"-use":     complete.PredictAnything,
    67  		})
    68  }
    69  func (c *OperatorKeyringCommand) AutocompleteArgs() complete.Predictor {
    70  	return complete.PredictNothing
    71  }
    72  
    73  func (c *OperatorKeyringCommand) Name() string { return "operator keyring" }
    74  
    75  func (c *OperatorKeyringCommand) Run(args []string) int {
    76  	var installKey, useKey, removeKey string
    77  	var listKeys bool
    78  
    79  	flags := c.Meta.FlagSet("operator-keyring", FlagSetClient)
    80  	flags.Usage = func() { c.Ui.Output(c.Help()) }
    81  
    82  	flags.StringVar(&installKey, "install", "", "install key")
    83  	flags.StringVar(&useKey, "use", "", "use key")
    84  	flags.StringVar(&removeKey, "remove", "", "remove key")
    85  	flags.BoolVar(&listKeys, "list", false, "list keys")
    86  
    87  	if err := flags.Parse(args); err != nil {
    88  		return 1
    89  	}
    90  
    91  	c.Ui = &cli.PrefixedUi{
    92  		OutputPrefix: "",
    93  		InfoPrefix:   "==> ",
    94  		ErrorPrefix:  "",
    95  		Ui:           c.Ui,
    96  	}
    97  
    98  	// Only accept a single argument
    99  	found := listKeys
   100  	for _, arg := range []string{installKey, useKey, removeKey} {
   101  		if found && len(arg) > 0 {
   102  			c.Ui.Error("Only a single action is allowed")
   103  			c.Ui.Error(commandErrorText(c))
   104  			return 1
   105  		}
   106  		found = found || len(arg) > 0
   107  	}
   108  
   109  	// Fail fast if no actionable args were passed
   110  	if !found {
   111  		c.Ui.Error("No actionable argument was passed")
   112  		c.Ui.Error("Either the '-install', '-use', '-remove' or '-list' flag must be set")
   113  		c.Ui.Error(commandErrorText(c))
   114  		return 1
   115  	}
   116  
   117  	// All other operations will require a client connection
   118  	client, err := c.Meta.Client()
   119  	if err != nil {
   120  		c.Ui.Error(fmt.Sprintf("Error creating nomad cli client: %s", err))
   121  		return 1
   122  	}
   123  
   124  	if listKeys {
   125  		c.Ui.Output("Gathering installed encryption keys...")
   126  		r, err := client.Agent().ListKeys()
   127  		if err != nil {
   128  			c.Ui.Error(fmt.Sprintf("error: %s", err))
   129  			return 1
   130  		}
   131  		c.handleKeyResponse(r)
   132  		return 0
   133  	}
   134  
   135  	if installKey != "" {
   136  		c.Ui.Output("Installing new gossip encryption key...")
   137  		_, err := client.Agent().InstallKey(installKey)
   138  		if err != nil {
   139  			c.Ui.Error(fmt.Sprintf("error: %s", err))
   140  			return 1
   141  		}
   142  		return 0
   143  	}
   144  
   145  	if useKey != "" {
   146  		c.Ui.Output("Changing primary gossip encryption key...")
   147  		_, err := client.Agent().UseKey(useKey)
   148  		if err != nil {
   149  			c.Ui.Error(fmt.Sprintf("error: %s", err))
   150  			return 1
   151  		}
   152  		return 0
   153  	}
   154  
   155  	if removeKey != "" {
   156  		c.Ui.Output("Removing gossip encryption key...")
   157  		_, err := client.Agent().RemoveKey(removeKey)
   158  		if err != nil {
   159  			c.Ui.Error(fmt.Sprintf("error: %s", err))
   160  			return 1
   161  		}
   162  		return 0
   163  	}
   164  
   165  	// Should never make it here
   166  	return 0
   167  }
   168  
   169  func (c *OperatorKeyringCommand) handleKeyResponse(resp *api.KeyringResponse) {
   170  	out := make([]string, len(resp.Keys)+1)
   171  	out[0] = "Key"
   172  	i := 1
   173  	for k := range resp.Keys {
   174  		out[i] = k
   175  		i = i + 1
   176  	}
   177  	c.Ui.Output(formatList(out))
   178  }