github.com/criteo-forks/consul@v1.4.5-criteonogrpc/command/keyring/keyring.go (about)

     1  package keyring
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  
     7  	"github.com/hashicorp/consul/agent"
     8  	consulapi "github.com/hashicorp/consul/api"
     9  	"github.com/hashicorp/consul/command/flags"
    10  	"github.com/mitchellh/cli"
    11  )
    12  
    13  func New(ui cli.Ui) *cmd {
    14  	c := &cmd{UI: ui}
    15  	c.init()
    16  	return c
    17  }
    18  
    19  type cmd struct {
    20  	UI    cli.Ui
    21  	flags *flag.FlagSet
    22  	http  *flags.HTTPFlags
    23  	help  string
    24  
    25  	// flags
    26  	installKey string
    27  	useKey     string
    28  	removeKey  string
    29  	listKeys   bool
    30  	relay      int
    31  }
    32  
    33  func (c *cmd) init() {
    34  	c.flags = flag.NewFlagSet("", flag.ContinueOnError)
    35  	c.flags.StringVar(&c.installKey, "install", "",
    36  		"Install a new encryption key. This will broadcast the new key to "+
    37  			"all members in the cluster.")
    38  	c.flags.StringVar(&c.useKey, "use", "",
    39  		"Change the primary encryption key, which is used to encrypt "+
    40  			"messages. The key must already be installed before this operation "+
    41  			"can succeed.")
    42  	c.flags.StringVar(&c.removeKey, "remove", "",
    43  		"Remove the given key from the cluster. This operation may only be "+
    44  			"performed on keys which are not currently the primary key.")
    45  	c.flags.BoolVar(&c.listKeys, "list", false,
    46  		"List all keys currently in use within the cluster.")
    47  	c.flags.IntVar(&c.relay, "relay-factor", 0,
    48  		"Setting this to a non-zero value will cause nodes to relay their response "+
    49  			"to the operation through this many randomly-chosen other nodes in the "+
    50  			"cluster. The maximum allowed value is 5.")
    51  
    52  	c.http = &flags.HTTPFlags{}
    53  	flags.Merge(c.flags, c.http.ClientFlags())
    54  	c.help = flags.Usage(help, c.flags)
    55  }
    56  
    57  func (c *cmd) Run(args []string) int {
    58  	if err := c.flags.Parse(args); err != nil {
    59  		return 1
    60  	}
    61  
    62  	c.UI = &cli.PrefixedUi{
    63  		OutputPrefix: "",
    64  		InfoPrefix:   "==> ",
    65  		ErrorPrefix:  "",
    66  		Ui:           c.UI,
    67  	}
    68  
    69  	// Only accept a single argument
    70  	found := c.listKeys
    71  	for _, arg := range []string{c.installKey, c.useKey, c.removeKey} {
    72  		if found && len(arg) > 0 {
    73  			c.UI.Error("Only a single action is allowed")
    74  			return 1
    75  		}
    76  		found = found || len(arg) > 0
    77  	}
    78  
    79  	// Fail fast if no actionable args were passed
    80  	if !found {
    81  		c.UI.Error(c.Help())
    82  		return 1
    83  	}
    84  
    85  	// Validate the relay factor
    86  	relayFactor, err := agent.ParseRelayFactor(c.relay)
    87  	if err != nil {
    88  		c.UI.Error(fmt.Sprintf("Error parsing relay factor: %s", err))
    89  		return 1
    90  	}
    91  
    92  	// All other operations will require a client connection
    93  	client, err := c.http.APIClient()
    94  	if err != nil {
    95  		c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
    96  		return 1
    97  	}
    98  
    99  	if c.listKeys {
   100  		c.UI.Info("Gathering installed encryption keys...")
   101  		responses, err := client.Operator().KeyringList(&consulapi.QueryOptions{RelayFactor: relayFactor})
   102  		if err != nil {
   103  			c.UI.Error(fmt.Sprintf("error: %s", err))
   104  			return 1
   105  		}
   106  		c.handleList(responses)
   107  		return 0
   108  	}
   109  
   110  	opts := &consulapi.WriteOptions{RelayFactor: relayFactor}
   111  	if c.installKey != "" {
   112  		c.UI.Info("Installing new gossip encryption key...")
   113  		err := client.Operator().KeyringInstall(c.installKey, opts)
   114  		if err != nil {
   115  			c.UI.Error(fmt.Sprintf("error: %s", err))
   116  			return 1
   117  		}
   118  		return 0
   119  	}
   120  
   121  	if c.useKey != "" {
   122  		c.UI.Info("Changing primary gossip encryption key...")
   123  		err := client.Operator().KeyringUse(c.useKey, opts)
   124  		if err != nil {
   125  			c.UI.Error(fmt.Sprintf("error: %s", err))
   126  			return 1
   127  		}
   128  		return 0
   129  	}
   130  
   131  	if c.removeKey != "" {
   132  		c.UI.Info("Removing gossip encryption key...")
   133  		err := client.Operator().KeyringRemove(c.removeKey, opts)
   134  		if err != nil {
   135  			c.UI.Error(fmt.Sprintf("error: %s", err))
   136  			return 1
   137  		}
   138  		return 0
   139  	}
   140  
   141  	// Should never make it here
   142  	return 0
   143  }
   144  
   145  func (c *cmd) handleList(responses []*consulapi.KeyringResponse) {
   146  	for _, response := range responses {
   147  		pool := response.Datacenter + " (LAN)"
   148  		if response.Segment != "" {
   149  			pool += fmt.Sprintf(" [%s]", response.Segment)
   150  		}
   151  		if response.WAN {
   152  			pool = "WAN"
   153  		}
   154  
   155  		c.UI.Output("")
   156  		c.UI.Output(pool + ":")
   157  
   158  		for from, msg := range response.Messages {
   159  			c.UI.Output(fmt.Sprintf("  ===> %s: %s", from, msg))
   160  		}
   161  
   162  		for key, num := range response.Keys {
   163  			c.UI.Output(fmt.Sprintf("  %s [%d/%d]", key, num, response.NumNodes))
   164  		}
   165  	}
   166  }
   167  
   168  func (c *cmd) Synopsis() string {
   169  	return synopsis
   170  }
   171  
   172  func (c *cmd) Help() string {
   173  	return c.help
   174  }
   175  
   176  const synopsis = "Manages gossip layer encryption keys"
   177  const help = `
   178  Usage: consul keyring [options]
   179  
   180    Manages encryption keys used for gossip messages. Gossip encryption is
   181    optional. When enabled, this command may be used to examine active encryption
   182    keys in the cluster, add new keys, and remove old ones. When combined, this
   183    functionality provides the ability to perform key rotation cluster-wide,
   184    without disrupting the cluster.
   185  
   186    All operations performed by this command can only be run against server nodes,
   187    and affect both the LAN and WAN keyrings in lock-step.
   188  
   189    All variations of the keyring command return 0 if all nodes reply and there
   190    are no errors. If any node fails to reply or reports failure, the exit code
   191    will be 1.
   192  `