Added -relay-factor param to keyring operations

This commit is contained in:
Kyle Havlovitz 2017-02-01 21:42:41 -05:00
parent ed5bf524ba
commit 5d888f5303
No known key found for this signature in database
GPG Key ID: 8A5E6B173056AD6C
13 changed files with 162 additions and 65 deletions

View File

@ -121,31 +121,44 @@ func (a *Agent) keyringProcess(args *structs.KeyringRequest) (*structs.KeyringRe
return &reply, nil return &reply, nil
} }
// ParseRelayFactor validates and converts the given relay factor to uint8
func ParseRelayFactor(n int) (uint8, error) {
if n < 0 || n > 5 {
return 0, fmt.Errorf("Relay factor must be in range: [0, 5]")
}
return uint8(n), nil
}
// ListKeys lists out all keys installed on the collective Consul cluster. This // ListKeys lists out all keys installed on the collective Consul cluster. This
// includes both servers and clients in all DC's. // includes both servers and clients in all DC's.
func (a *Agent) ListKeys(token string) (*structs.KeyringResponses, error) { func (a *Agent) ListKeys(token string, relayFactor uint8) (*structs.KeyringResponses, error) {
args := structs.KeyringRequest{Operation: structs.KeyringList} args := structs.KeyringRequest{Operation: structs.KeyringList}
args.Token = token parseKeyringRequest(&args, token, relayFactor)
return a.keyringProcess(&args) return a.keyringProcess(&args)
} }
// InstallKey installs a new gossip encryption key // InstallKey installs a new gossip encryption key
func (a *Agent) InstallKey(key, token string) (*structs.KeyringResponses, error) { func (a *Agent) InstallKey(key, token string, relayFactor uint8) (*structs.KeyringResponses, error) {
args := structs.KeyringRequest{Key: key, Operation: structs.KeyringInstall} args := structs.KeyringRequest{Key: key, Operation: structs.KeyringInstall}
args.Token = token parseKeyringRequest(&args, token, relayFactor)
return a.keyringProcess(&args) return a.keyringProcess(&args)
} }
// UseKey changes the primary encryption key used to encrypt messages // UseKey changes the primary encryption key used to encrypt messages
func (a *Agent) UseKey(key, token string) (*structs.KeyringResponses, error) { func (a *Agent) UseKey(key, token string, relayFactor uint8) (*structs.KeyringResponses, error) {
args := structs.KeyringRequest{Key: key, Operation: structs.KeyringUse} args := structs.KeyringRequest{Key: key, Operation: structs.KeyringUse}
args.Token = token parseKeyringRequest(&args, token, relayFactor)
return a.keyringProcess(&args) return a.keyringProcess(&args)
} }
// RemoveKey will remove a gossip encryption key from the keyring // RemoveKey will remove a gossip encryption key from the keyring
func (a *Agent) RemoveKey(key, token string) (*structs.KeyringResponses, error) { func (a *Agent) RemoveKey(key, token string, relayFactor uint8) (*structs.KeyringResponses, error) {
args := structs.KeyringRequest{Key: key, Operation: structs.KeyringRemove} args := structs.KeyringRequest{Key: key, Operation: structs.KeyringRemove}
args.Token = token parseKeyringRequest(&args, token, relayFactor)
return a.keyringProcess(&args) return a.keyringProcess(&args)
} }
func parseKeyringRequest(req *structs.KeyringRequest, token string, relayFactor uint8) {
req.Token = token
req.RelayFactor = relayFactor
}

View File

@ -132,49 +132,49 @@ func TestAgentKeyring_ACL(t *testing.T) {
testutil.WaitForLeader(t, agent.RPC, "dc1") testutil.WaitForLeader(t, agent.RPC, "dc1")
// List keys without access fails // List keys without access fails
_, err := agent.ListKeys("") _, err := agent.ListKeys("", 0)
if err == nil || !strings.Contains(err.Error(), "denied") { if err == nil || !strings.Contains(err.Error(), "denied") {
t.Fatalf("expected denied error, got: %#v", err) t.Fatalf("expected denied error, got: %#v", err)
} }
// List keys with access works // List keys with access works
_, err = agent.ListKeys("root") _, err = agent.ListKeys("root", 0)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
// Install without access fails // Install without access fails
_, err = agent.InstallKey(key2, "") _, err = agent.InstallKey(key2, "", 0)
if err == nil || !strings.Contains(err.Error(), "denied") { if err == nil || !strings.Contains(err.Error(), "denied") {
t.Fatalf("expected denied error, got: %#v", err) t.Fatalf("expected denied error, got: %#v", err)
} }
// Install with access works // Install with access works
_, err = agent.InstallKey(key2, "root") _, err = agent.InstallKey(key2, "root", 0)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
// Use without access fails // Use without access fails
_, err = agent.UseKey(key2, "") _, err = agent.UseKey(key2, "", 0)
if err == nil || !strings.Contains(err.Error(), "denied") { if err == nil || !strings.Contains(err.Error(), "denied") {
t.Fatalf("expected denied error, got: %#v", err) t.Fatalf("expected denied error, got: %#v", err)
} }
// Use with access works // Use with access works
_, err = agent.UseKey(key2, "root") _, err = agent.UseKey(key2, "root", 0)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
// Remove without access fails // Remove without access fails
_, err = agent.RemoveKey(key1, "") _, err = agent.RemoveKey(key1, "", 0)
if err == nil || !strings.Contains(err.Error(), "denied") { if err == nil || !strings.Contains(err.Error(), "denied") {
t.Fatalf("expected denied error, got: %#v", err) t.Fatalf("expected denied error, got: %#v", err)
} }
// Remove with access works // Remove with access works
_, err = agent.RemoveKey(key1, "root") _, err = agent.RemoveKey(key1, "root", 0)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }

View File

@ -7,6 +7,7 @@ import (
"github.com/hashicorp/consul/consul/structs" "github.com/hashicorp/consul/consul/structs"
multierror "github.com/hashicorp/go-multierror" multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/raft" "github.com/hashicorp/raft"
"strconv"
) )
// OperatorRaftConfiguration is used to inspect the current Raft configuration. // OperatorRaftConfiguration is used to inspect the current Raft configuration.
@ -59,8 +60,9 @@ func (s *HTTPServer) OperatorRaftPeer(resp http.ResponseWriter, req *http.Reques
} }
type keyringArgs struct { type keyringArgs struct {
Key string Key string
Token string Token string
RelayFactor uint8
} }
// OperatorKeyringEndpoint handles keyring operations (install, list, use, remove) // OperatorKeyringEndpoint handles keyring operations (install, list, use, remove)
@ -75,6 +77,23 @@ func (s *HTTPServer) OperatorKeyringEndpoint(resp http.ResponseWriter, req *http
} }
s.parseToken(req, &args.Token) s.parseToken(req, &args.Token)
// Parse relay factor
if relayFactor := req.URL.Query().Get("relay-factor"); relayFactor != "" {
n, err := strconv.Atoi(relayFactor)
if err != nil {
resp.WriteHeader(400)
resp.Write([]byte(fmt.Sprintf("Error parsing relay factor: %v", err)))
return nil, nil
}
args.RelayFactor, err = ParseRelayFactor(n)
if err != nil {
resp.WriteHeader(400)
resp.Write([]byte(fmt.Sprintf("Invalid relay factor: %v", err)))
return nil, nil
}
}
// Switch on the method // Switch on the method
switch req.Method { switch req.Method {
case "GET": case "GET":
@ -93,7 +112,7 @@ func (s *HTTPServer) OperatorKeyringEndpoint(resp http.ResponseWriter, req *http
// KeyringInstall is used to install a new gossip encryption key into the cluster // KeyringInstall is used to install a new gossip encryption key into the cluster
func (s *HTTPServer) KeyringInstall(resp http.ResponseWriter, req *http.Request, args *keyringArgs) (interface{}, error) { func (s *HTTPServer) KeyringInstall(resp http.ResponseWriter, req *http.Request, args *keyringArgs) (interface{}, error) {
responses, err := s.agent.InstallKey(args.Key, args.Token) responses, err := s.agent.InstallKey(args.Key, args.Token, args.RelayFactor)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -103,7 +122,7 @@ func (s *HTTPServer) KeyringInstall(resp http.ResponseWriter, req *http.Request,
// KeyringList is used to list the keys installed in the cluster // KeyringList is used to list the keys installed in the cluster
func (s *HTTPServer) KeyringList(resp http.ResponseWriter, req *http.Request, args *keyringArgs) (interface{}, error) { func (s *HTTPServer) KeyringList(resp http.ResponseWriter, req *http.Request, args *keyringArgs) (interface{}, error) {
responses, err := s.agent.ListKeys(args.Token) responses, err := s.agent.ListKeys(args.Token, args.RelayFactor)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -113,7 +132,7 @@ func (s *HTTPServer) KeyringList(resp http.ResponseWriter, req *http.Request, ar
// KeyringRemove is used to list the keys installed in the cluster // KeyringRemove is used to list the keys installed in the cluster
func (s *HTTPServer) KeyringRemove(resp http.ResponseWriter, req *http.Request, args *keyringArgs) (interface{}, error) { func (s *HTTPServer) KeyringRemove(resp http.ResponseWriter, req *http.Request, args *keyringArgs) (interface{}, error) {
responses, err := s.agent.RemoveKey(args.Key, args.Token) responses, err := s.agent.RemoveKey(args.Key, args.Token, args.RelayFactor)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -123,7 +142,7 @@ func (s *HTTPServer) KeyringRemove(resp http.ResponseWriter, req *http.Request,
// KeyringUse is used to change the primary gossip encryption key // KeyringUse is used to change the primary gossip encryption key
func (s *HTTPServer) KeyringUse(resp http.ResponseWriter, req *http.Request, args *keyringArgs) (interface{}, error) { func (s *HTTPServer) KeyringUse(resp http.ResponseWriter, req *http.Request, args *keyringArgs) (interface{}, error) {
responses, err := s.agent.UseKey(args.Key, args.Token) responses, err := s.agent.UseKey(args.Key, args.Token, args.RelayFactor)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -77,7 +77,7 @@ func TestOperator_KeyringInstall(t *testing.T) {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
listResponse, err := srv.agent.ListKeys("") listResponse, err := srv.agent.ListKeys("", 0)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -155,13 +155,13 @@ func TestOperator_KeyringRemove(t *testing.T) {
c.EncryptKey = key c.EncryptKey = key
} }
httpTestWithConfig(t, func(srv *HTTPServer) { httpTestWithConfig(t, func(srv *HTTPServer) {
_, err := srv.agent.InstallKey(tempKey, "") _, err := srv.agent.InstallKey(tempKey, "", 0)
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
// Make sure the temp key is installed // Make sure the temp key is installed
list, err := srv.agent.ListKeys("") list, err := srv.agent.ListKeys("", 0)
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -191,7 +191,7 @@ func TestOperator_KeyringRemove(t *testing.T) {
} }
// Make sure the temp key has been removed // Make sure the temp key has been removed
list, err = srv.agent.ListKeys("") list, err = srv.agent.ListKeys("", 0)
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -217,7 +217,7 @@ func TestOperator_KeyringUse(t *testing.T) {
c.EncryptKey = oldKey c.EncryptKey = oldKey
} }
httpTestWithConfig(t, func(srv *HTTPServer) { httpTestWithConfig(t, func(srv *HTTPServer) {
if _, err := srv.agent.InstallKey(newKey, ""); err != nil { if _, err := srv.agent.InstallKey(newKey, "", 0); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -233,12 +233,12 @@ func TestOperator_KeyringUse(t *testing.T) {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
if _, err := srv.agent.RemoveKey(oldKey, ""); err != nil { if _, err := srv.agent.RemoveKey(oldKey, "", 0); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
// Make sure only the new key remains // Make sure only the new key remains
list, err := srv.agent.ListKeys("") list, err := srv.agent.ListKeys("", 0)
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -256,3 +256,32 @@ func TestOperator_KeyringUse(t *testing.T) {
} }
}, configFunc) }, configFunc)
} }
func TestOperator_Keyring_InvalidRelayFactor(t *testing.T) {
key := "H3/9gBxcKKRf45CaI2DlRg=="
configFunc := func(c *Config) {
c.EncryptKey = key
}
httpTestWithConfig(t, func(srv *HTTPServer) {
cases := map[string]string{
"999": "Relay factor must be in range",
"asdf": "Error parsing relay factor",
}
for relayFactor, errString := range cases {
req, err := http.NewRequest("GET", "/v1/operator/keyring?relay-factor="+relayFactor, nil)
if err != nil {
t.Fatalf("err: %v", err)
}
resp := httptest.NewRecorder()
_, err = srv.OperatorKeyringEndpoint(resp, req)
if err != nil {
t.Fatalf("err: %v", err)
}
body := resp.Body.String()
if !strings.Contains(body, errString) {
t.Fatalf("bad: %v", body)
}
}
}, configFunc)
}

View File

@ -106,7 +106,8 @@ type joinResponse struct {
} }
type keyringRequest struct { type keyringRequest struct {
Key string Key string
RelayFactor uint8
} }
type KeyringEntry struct { type KeyringEntry struct {
@ -604,21 +605,21 @@ func (i *AgentRPC) handleKeyring(client *rpcClient, seq uint64, cmd, token strin
var r keyringResponse var r keyringResponse
var err error var err error
if cmd != listKeysCommand { if err = client.dec.Decode(&req); err != nil {
if err = client.dec.Decode(&req); err != nil { return fmt.Errorf("decode failed: %v", err)
return fmt.Errorf("decode failed: %v", err)
}
} }
i.agent.logger.Printf("[INFO] agent: Sending rpc command with relay factor %d", req.RelayFactor)
switch cmd { switch cmd {
case listKeysCommand: case listKeysCommand:
queryResp, err = i.agent.ListKeys(token) queryResp, err = i.agent.ListKeys(token, req.RelayFactor)
case installKeyCommand: case installKeyCommand:
queryResp, err = i.agent.InstallKey(req.Key, token) queryResp, err = i.agent.InstallKey(req.Key, token, req.RelayFactor)
case useKeyCommand: case useKeyCommand:
queryResp, err = i.agent.UseKey(req.Key, token) queryResp, err = i.agent.UseKey(req.Key, token, req.RelayFactor)
case removeKeyCommand: case removeKeyCommand:
queryResp, err = i.agent.RemoveKey(req.Key, token) queryResp, err = i.agent.RemoveKey(req.Key, token, req.RelayFactor)
default: default:
respHeader := responseHeader{Seq: seq, Error: unsupportedCommand} respHeader := responseHeader{Seq: seq, Error: unsupportedCommand}
client.Send(&respHeader, nil) client.Send(&respHeader, nil)

View File

@ -194,48 +194,49 @@ func (c *RPCClient) WANMembers() ([]Member, error) {
return resp.Members, err return resp.Members, err
} }
func (c *RPCClient) ListKeys(token string) (keyringResponse, error) { func (c *RPCClient) ListKeys(token string, relayFactor uint8) (keyringResponse, error) {
header := requestHeader{ header := requestHeader{
Command: listKeysCommand, Command: listKeysCommand,
Seq: c.getSeq(), Seq: c.getSeq(),
Token: token, Token: token,
} }
req := keyringRequest{RelayFactor: relayFactor}
var resp keyringResponse var resp keyringResponse
err := c.genericRPC(&header, nil, &resp) err := c.genericRPC(&header, req, &resp)
return resp, err return resp, err
} }
func (c *RPCClient) InstallKey(key, token string) (keyringResponse, error) { func (c *RPCClient) InstallKey(key, token string, relayFactor uint8) (keyringResponse, error) {
header := requestHeader{ header := requestHeader{
Command: installKeyCommand, Command: installKeyCommand,
Seq: c.getSeq(), Seq: c.getSeq(),
Token: token, Token: token,
} }
req := keyringRequest{key} req := keyringRequest{Key: key, RelayFactor: relayFactor}
var resp keyringResponse var resp keyringResponse
err := c.genericRPC(&header, &req, &resp) err := c.genericRPC(&header, &req, &resp)
return resp, err return resp, err
} }
func (c *RPCClient) UseKey(key, token string) (keyringResponse, error) { func (c *RPCClient) UseKey(key, token string, relayFactor uint8) (keyringResponse, error) {
header := requestHeader{ header := requestHeader{
Command: useKeyCommand, Command: useKeyCommand,
Seq: c.getSeq(), Seq: c.getSeq(),
Token: token, Token: token,
} }
req := keyringRequest{key} req := keyringRequest{Key: key, RelayFactor: relayFactor}
var resp keyringResponse var resp keyringResponse
err := c.genericRPC(&header, &req, &resp) err := c.genericRPC(&header, &req, &resp)
return resp, err return resp, err
} }
func (c *RPCClient) RemoveKey(key, token string) (keyringResponse, error) { func (c *RPCClient) RemoveKey(key, token string, relayFactor uint8) (keyringResponse, error) {
header := requestHeader{ header := requestHeader{
Command: removeKeyCommand, Command: removeKeyCommand,
Seq: c.getSeq(), Seq: c.getSeq(),
Token: token, Token: token,
} }
req := keyringRequest{key} req := keyringRequest{Key: key, RelayFactor: relayFactor}
var resp keyringResponse var resp keyringResponse
err := c.genericRPC(&header, &req, &resp) err := c.genericRPC(&header, &req, &resp)
return resp, err return resp, err

View File

@ -371,7 +371,7 @@ func TestRPCClientInstallKey(t *testing.T) {
}) })
// install key2 // install key2
r, err := p1.client.InstallKey(key2, "") r, err := p1.client.InstallKey(key2, "", 0)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -402,7 +402,7 @@ func TestRPCClientUseKey(t *testing.T) {
defer p1.Close() defer p1.Close()
// add a second key to the ring // add a second key to the ring
r, err := p1.client.InstallKey(key2, "") r, err := p1.client.InstallKey(key2, "", 0)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -423,21 +423,21 @@ func TestRPCClientUseKey(t *testing.T) {
}) })
// can't remove key1 yet // can't remove key1 yet
r, err = p1.client.RemoveKey(key1, "") r, err = p1.client.RemoveKey(key1, "", 0)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
keyringError(t, r) keyringError(t, r)
// change primary key // change primary key
r, err = p1.client.UseKey(key2, "") r, err = p1.client.UseKey(key2, "", 0)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
keyringSuccess(t, r) keyringSuccess(t, r)
// can remove key1 now // can remove key1 now
r, err = p1.client.RemoveKey(key1, "") r, err = p1.client.RemoveKey(key1, "", 0)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -450,7 +450,7 @@ func TestRPCClientKeyOperation_encryptionDisabled(t *testing.T) {
}) })
defer p1.Close() defer p1.Close()
r, err := p1.client.ListKeys("") r, err := p1.client.ListKeys("", 0)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -458,7 +458,7 @@ func TestRPCClientKeyOperation_encryptionDisabled(t *testing.T) {
} }
func listKeys(t *testing.T, c *RPCClient) map[string]map[string]int { func listKeys(t *testing.T, c *RPCClient) map[string]map[string]int {
resp, err := c.ListKeys("") resp, err := c.ListKeys("", 0)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }

View File

@ -18,6 +18,7 @@ type KeyringCommand struct {
func (c *KeyringCommand) Run(args []string) int { func (c *KeyringCommand) Run(args []string) int {
var installKey, useKey, removeKey, token string var installKey, useKey, removeKey, token string
var listKeys bool var listKeys bool
var relay int
cmdFlags := flag.NewFlagSet("keys", flag.ContinueOnError) cmdFlags := flag.NewFlagSet("keys", flag.ContinueOnError)
cmdFlags.Usage = func() { c.Ui.Output(c.Help()) } cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
@ -27,6 +28,7 @@ func (c *KeyringCommand) Run(args []string) int {
cmdFlags.StringVar(&removeKey, "remove", "", "remove key") cmdFlags.StringVar(&removeKey, "remove", "", "remove key")
cmdFlags.BoolVar(&listKeys, "list", false, "list keys") cmdFlags.BoolVar(&listKeys, "list", false, "list keys")
cmdFlags.StringVar(&token, "token", "", "acl token") cmdFlags.StringVar(&token, "token", "", "acl token")
cmdFlags.IntVar(&relay, "relay-factor", 0, "relay factor")
rpcAddr := RPCAddrFlag(cmdFlags) rpcAddr := RPCAddrFlag(cmdFlags)
if err := cmdFlags.Parse(args); err != nil { if err := cmdFlags.Parse(args); err != nil {
@ -56,6 +58,13 @@ func (c *KeyringCommand) Run(args []string) int {
return 1 return 1
} }
// Validate the relay factor
relayFactor, err := agent.ParseRelayFactor(relay)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error parsing relay factor: %s", err))
return 1
}
// All other operations will require a client connection // All other operations will require a client connection
client, err := RPCClient(*rpcAddr) client, err := RPCClient(*rpcAddr)
if err != nil { if err != nil {
@ -66,7 +75,7 @@ func (c *KeyringCommand) Run(args []string) int {
if listKeys { if listKeys {
c.Ui.Info("Gathering installed encryption keys...") c.Ui.Info("Gathering installed encryption keys...")
r, err := client.ListKeys(token) r, err := client.ListKeys(token, relayFactor)
if err != nil { if err != nil {
c.Ui.Error(fmt.Sprintf("error: %s", err)) c.Ui.Error(fmt.Sprintf("error: %s", err))
return 1 return 1
@ -80,7 +89,7 @@ func (c *KeyringCommand) Run(args []string) int {
if installKey != "" { if installKey != "" {
c.Ui.Info("Installing new gossip encryption key...") c.Ui.Info("Installing new gossip encryption key...")
r, err := client.InstallKey(installKey, token) r, err := client.InstallKey(installKey, token, relayFactor)
if err != nil { if err != nil {
c.Ui.Error(fmt.Sprintf("error: %s", err)) c.Ui.Error(fmt.Sprintf("error: %s", err))
return 1 return 1
@ -90,7 +99,7 @@ func (c *KeyringCommand) Run(args []string) int {
if useKey != "" { if useKey != "" {
c.Ui.Info("Changing primary gossip encryption key...") c.Ui.Info("Changing primary gossip encryption key...")
r, err := client.UseKey(useKey, token) r, err := client.UseKey(useKey, token, relayFactor)
if err != nil { if err != nil {
c.Ui.Error(fmt.Sprintf("error: %s", err)) c.Ui.Error(fmt.Sprintf("error: %s", err))
return 1 return 1
@ -100,7 +109,7 @@ func (c *KeyringCommand) Run(args []string) int {
if removeKey != "" { if removeKey != "" {
c.Ui.Info("Removing gossip encryption key...") c.Ui.Info("Removing gossip encryption key...")
r, err := client.RemoveKey(removeKey, token) r, err := client.RemoveKey(removeKey, token, relayFactor)
if err != nil { if err != nil {
c.Ui.Error(fmt.Sprintf("error: %s", err)) c.Ui.Error(fmt.Sprintf("error: %s", err))
return 1 return 1
@ -206,6 +215,11 @@ Options:
not currently the primary key. not currently the primary key.
-token="" ACL token to use during requests. Defaults to that -token="" ACL token to use during requests. Defaults to that
of the agent. of the agent.
-relay-factor Added in Consul 0.7.4, setting this to a non-zero
value will cause nodes to relay their response to
the operation through this many randomly-chosen
other nodes in the cluster. The maximum allowed
value is 5.
-use=<key> Change the primary encryption key, which is used to -use=<key> Change the primary encryption key, which is used to
encrypt messages. The key must already be installed encrypt messages. The key must already be installed
before this operation can succeed. before this operation can succeed.

View File

@ -89,6 +89,17 @@ func TestKeyringCommandRun_failedConnection(t *testing.T) {
} }
} }
func TestKeyringCommandRun_invalidRelayFactor(t *testing.T) {
ui := new(cli.MockUi)
c := &KeyringCommand{Ui: ui}
args := []string{"-list", "-relay-factor=6"}
code := c.Run(args)
if code != 1 {
t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String())
}
}
func listKeys(t *testing.T, addr string) string { func listKeys(t *testing.T, addr string) string {
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &KeyringCommand{Ui: ui} c := &KeyringCommand{Ui: ui}

View File

@ -147,15 +147,16 @@ func (m *Internal) executeKeyringOp(
mgr = m.srv.KeyManagerLAN() mgr = m.srv.KeyManagerLAN()
} }
opts := &serf.KeyRequestOptions{RelayFactor: args.RelayFactor}
switch args.Operation { switch args.Operation {
case structs.KeyringList: case structs.KeyringList:
serfResp, err = mgr.ListKeys() serfResp, err = mgr.ListKeysWithOptions(opts)
case structs.KeyringInstall: case structs.KeyringInstall:
serfResp, err = mgr.InstallKey(args.Key) serfResp, err = mgr.InstallKeyWithOptions(args.Key, opts)
case structs.KeyringUse: case structs.KeyringUse:
serfResp, err = mgr.UseKey(args.Key) serfResp, err = mgr.UseKeyWithOptions(args.Key, opts)
case structs.KeyringRemove: case structs.KeyringRemove:
serfResp, err = mgr.RemoveKey(args.Key) serfResp, err = mgr.RemoveKeyWithOptions(args.Key, opts)
} }
errStr := "" errStr := ""

View File

@ -1000,10 +1000,11 @@ const (
// KeyringRequest encapsulates a request to modify an encryption keyring. // KeyringRequest encapsulates a request to modify an encryption keyring.
// It can be used for install, remove, or use key type operations. // It can be used for install, remove, or use key type operations.
type KeyringRequest struct { type KeyringRequest struct {
Operation KeyringOp Operation KeyringOp
Key string Key string
Datacenter string Datacenter string
Forwarded bool Forwarded bool
RelayFactor uint8
QueryOptions QueryOptions
} }

View File

@ -138,6 +138,9 @@ Available in Consul 0.7.2 and later, the keyring endpoint supports the
This endpoint supports the use of ACL tokens using either the `X-CONSUL-TOKEN` This endpoint supports the use of ACL tokens using either the `X-CONSUL-TOKEN`
header or the `?token=` query parameter. header or the `?token=` query parameter.
Added in Consul 0.7.4, this endpoint supports the `?relay-factor=` query parameter.
See the [Keyring Command](/docs/commands/keyring.html#_relay_factor) for more details.
#### GET Method #### GET Method
Using the `GET` method, this endpoint will list the gossip encryption keys Using the `GET` method, this endpoint will list the gossip encryption keys

View File

@ -48,6 +48,10 @@ The list of available flags are:
* `-token=""` - ACL token to use during requests. Defaults to that of the agent. * `-token=""` - ACL token to use during requests. Defaults to that of the agent.
* `-relay-factor` - Added in Consul 0.7.4, setting this to a non-zero value will
cause nodes to relay their response to the operation through this many
randomly-chosen other nodes in the cluster. The maximum allowed value is 5.
* `-rpc-addr` - Address to the RPC server of the agent you want to contact * `-rpc-addr` - Address to the RPC server of the agent you want to contact
to send this command. If this isn't specified, the command will contact to send this command. If this isn't specified, the command will contact
"127.0.0.1:8400" which is the default RPC address of a Consul agent. "127.0.0.1:8400" which is the default RPC address of a Consul agent.