github.com/blixtra/nomad@v0.7.2-0.20171221000451-da9a1d7bb050/command/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  // KeyringCommand is a Command implementation that handles querying, installing,
    13  // and removing gossip encryption keys from a keyring.
    14  type KeyringCommand struct {
    15  	Meta
    16  }
    17  
    18  func (c *KeyringCommand) Help() string {
    19  	helpText := `
    20  Usage: nomad 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  General Options:
    35  
    36    ` + generalOptionsUsage() + `
    37  
    38  Keyring Options:
    39  
    40    -install=<key>            Install a new encryption key. This will broadcast
    41                              the new key to all members in the cluster.
    42    -list                     List all keys currently in use within the cluster.
    43    -remove=<key>             Remove the given key from the cluster. This
    44                              operation may only be performed on keys which are
    45                              not currently the primary key.
    46    -use=<key>                Change the primary encryption key, which is used to
    47                              encrypt messages. The key must already be installed
    48                              before this operation can succeed.
    49  `
    50  	return strings.TrimSpace(helpText)
    51  }
    52  
    53  func (c *KeyringCommand) Synopsis() string {
    54  	return "Manages gossip layer encryption keys"
    55  }
    56  
    57  func (c *KeyringCommand) AutocompleteFlags() complete.Flags {
    58  	return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
    59  		complete.Flags{
    60  			"-install": complete.PredictAnything,
    61  			"-list":    complete.PredictNothing,
    62  			"-remove":  complete.PredictAnything,
    63  			"-use":     complete.PredictAnything,
    64  		})
    65  }
    66  func (c *KeyringCommand) AutocompleteArgs() complete.Predictor {
    67  	return complete.PredictNothing
    68  }
    69  
    70  func (c *KeyringCommand) Run(args []string) int {
    71  	var installKey, useKey, removeKey string
    72  	var listKeys bool
    73  
    74  	flags := c.Meta.FlagSet("keys", FlagSetClient)
    75  	flags.Usage = func() { c.Ui.Output(c.Help()) }
    76  
    77  	flags.StringVar(&installKey, "install", "", "install key")
    78  	flags.StringVar(&useKey, "use", "", "use key")
    79  	flags.StringVar(&removeKey, "remove", "", "remove key")
    80  	flags.BoolVar(&listKeys, "list", false, "list keys")
    81  
    82  	if err := flags.Parse(args); err != nil {
    83  		return 1
    84  	}
    85  
    86  	c.Ui = &cli.PrefixedUi{
    87  		OutputPrefix: "",
    88  		InfoPrefix:   "==> ",
    89  		ErrorPrefix:  "",
    90  		Ui:           c.Ui,
    91  	}
    92  
    93  	// Only accept a single argument
    94  	found := listKeys
    95  	for _, arg := range []string{installKey, useKey, removeKey} {
    96  		if found && len(arg) > 0 {
    97  			c.Ui.Error("Only a single action is allowed")
    98  			return 1
    99  		}
   100  		found = found || len(arg) > 0
   101  	}
   102  
   103  	// Fail fast if no actionable args were passed
   104  	if !found {
   105  		c.Ui.Error(c.Help())
   106  		return 1
   107  	}
   108  
   109  	// All other operations will require a client connection
   110  	client, err := c.Meta.Client()
   111  	if err != nil {
   112  		c.Ui.Error(fmt.Sprintf("Error creating nomad cli client: %s", err))
   113  		return 1
   114  	}
   115  
   116  	if listKeys {
   117  		c.Ui.Info("Gathering installed encryption keys...")
   118  		r, err := client.Agent().ListKeys()
   119  		if err != nil {
   120  			c.Ui.Error(fmt.Sprintf("error: %s", err))
   121  			return 1
   122  		}
   123  		c.handleKeyResponse(r)
   124  		return 0
   125  	}
   126  
   127  	if installKey != "" {
   128  		c.Ui.Info("Installing new gossip encryption key...")
   129  		_, err := client.Agent().InstallKey(installKey)
   130  		if err != nil {
   131  			c.Ui.Error(fmt.Sprintf("error: %s", err))
   132  			return 1
   133  		}
   134  		return 0
   135  	}
   136  
   137  	if useKey != "" {
   138  		c.Ui.Info("Changing primary gossip encryption key...")
   139  		_, err := client.Agent().UseKey(useKey)
   140  		if err != nil {
   141  			c.Ui.Error(fmt.Sprintf("error: %s", err))
   142  			return 1
   143  		}
   144  		return 0
   145  	}
   146  
   147  	if removeKey != "" {
   148  		c.Ui.Info("Removing gossip encryption key...")
   149  		_, err := client.Agent().RemoveKey(removeKey)
   150  		if err != nil {
   151  			c.Ui.Error(fmt.Sprintf("error: %s", err))
   152  			return 1
   153  		}
   154  		return 0
   155  	}
   156  
   157  	// Should never make it here
   158  	return 0
   159  }
   160  
   161  func (c *KeyringCommand) handleKeyResponse(resp *api.KeyringResponse) {
   162  	out := make([]string, len(resp.Keys)+1)
   163  	out[0] = "Key"
   164  	i := 1
   165  	for k := range resp.Keys {
   166  		out[i] = fmt.Sprintf("%s", k)
   167  		i = i + 1
   168  	}
   169  	c.Ui.Output(formatList(out))
   170  }