github.imxd.top/hashicorp/consul@v1.4.5/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 `