Skip to content

Commit

Permalink
Merge pull request #2704 from hashicorp/f-relay-query-responses
Browse files Browse the repository at this point in the history
Add relay-factor arg to keyring operations
  • Loading branch information
kyhavlov authored Feb 2, 2017
2 parents a5c8cca + 5d888f5 commit 5b80490
Show file tree
Hide file tree
Showing 21 changed files with 387 additions and 130 deletions.
29 changes: 21 additions & 8 deletions command/agent/keyring.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,31 +121,44 @@ func (a *Agent) keyringProcess(args *structs.KeyringRequest) (*structs.KeyringRe
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
// 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.Token = token
parseKeyringRequest(&args, token, relayFactor)
return a.keyringProcess(&args)
}

// 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.Token = token
parseKeyringRequest(&args, token, relayFactor)
return a.keyringProcess(&args)
}

// 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.Token = token
parseKeyringRequest(&args, token, relayFactor)
return a.keyringProcess(&args)
}

// 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.Token = token
parseKeyringRequest(&args, token, relayFactor)
return a.keyringProcess(&args)
}

func parseKeyringRequest(req *structs.KeyringRequest, token string, relayFactor uint8) {
req.Token = token
req.RelayFactor = relayFactor
}
16 changes: 8 additions & 8 deletions command/agent/keyring_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,49 +132,49 @@ func TestAgentKeyring_ACL(t *testing.T) {
testutil.WaitForLeader(t, agent.RPC, "dc1")

// List keys without access fails
_, err := agent.ListKeys("")
_, err := agent.ListKeys("", 0)
if err == nil || !strings.Contains(err.Error(), "denied") {
t.Fatalf("expected denied error, got: %#v", err)
}

// List keys with access works
_, err = agent.ListKeys("root")
_, err = agent.ListKeys("root", 0)
if err != nil {
t.Fatalf("err: %s", err)
}

// Install without access fails
_, err = agent.InstallKey(key2, "")
_, err = agent.InstallKey(key2, "", 0)
if err == nil || !strings.Contains(err.Error(), "denied") {
t.Fatalf("expected denied error, got: %#v", err)
}

// Install with access works
_, err = agent.InstallKey(key2, "root")
_, err = agent.InstallKey(key2, "root", 0)
if err != nil {
t.Fatalf("err: %s", err)
}

// Use without access fails
_, err = agent.UseKey(key2, "")
_, err = agent.UseKey(key2, "", 0)
if err == nil || !strings.Contains(err.Error(), "denied") {
t.Fatalf("expected denied error, got: %#v", err)
}

// Use with access works
_, err = agent.UseKey(key2, "root")
_, err = agent.UseKey(key2, "root", 0)
if err != nil {
t.Fatalf("err: %s", err)
}

// Remove without access fails
_, err = agent.RemoveKey(key1, "")
_, err = agent.RemoveKey(key1, "", 0)
if err == nil || !strings.Contains(err.Error(), "denied") {
t.Fatalf("expected denied error, got: %#v", err)
}

// Remove with access works
_, err = agent.RemoveKey(key1, "root")
_, err = agent.RemoveKey(key1, "root", 0)
if err != nil {
t.Fatalf("err: %s", err)
}
Expand Down
31 changes: 25 additions & 6 deletions command/agent/operator_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/hashicorp/consul/consul/structs"
multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/raft"
"strconv"
)

// OperatorRaftConfiguration is used to inspect the current Raft configuration.
Expand Down Expand Up @@ -59,8 +60,9 @@ func (s *HTTPServer) OperatorRaftPeer(resp http.ResponseWriter, req *http.Reques
}

type keyringArgs struct {
Key string
Token string
Key string
Token string
RelayFactor uint8
}

// OperatorKeyringEndpoint handles keyring operations (install, list, use, remove)
Expand All @@ -75,6 +77,23 @@ func (s *HTTPServer) OperatorKeyringEndpoint(resp http.ResponseWriter, req *http
}
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 req.Method {
case "GET":
Expand All @@ -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
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 {
return nil, err
}
Expand All @@ -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
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 {
return nil, err
}
Expand All @@ -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
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 {
return nil, err
}
Expand All @@ -123,7 +142,7 @@ func (s *HTTPServer) KeyringRemove(resp http.ResponseWriter, req *http.Request,

// KeyringUse is used to change the primary gossip encryption key
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 {
return nil, err
}
Expand Down
43 changes: 36 additions & 7 deletions command/agent/operator_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func TestOperator_KeyringInstall(t *testing.T) {
t.Fatalf("err: %s", err)
}

listResponse, err := srv.agent.ListKeys("")
listResponse, err := srv.agent.ListKeys("", 0)
if err != nil {
t.Fatalf("err: %s", err)
}
Expand Down Expand Up @@ -155,13 +155,13 @@ func TestOperator_KeyringRemove(t *testing.T) {
c.EncryptKey = key
}
httpTestWithConfig(t, func(srv *HTTPServer) {
_, err := srv.agent.InstallKey(tempKey, "")
_, err := srv.agent.InstallKey(tempKey, "", 0)
if err != nil {
t.Fatalf("err: %v", err)
}

// Make sure the temp key is installed
list, err := srv.agent.ListKeys("")
list, err := srv.agent.ListKeys("", 0)
if err != nil {
t.Fatalf("err: %v", err)
}
Expand Down Expand Up @@ -191,7 +191,7 @@ func TestOperator_KeyringRemove(t *testing.T) {
}

// Make sure the temp key has been removed
list, err = srv.agent.ListKeys("")
list, err = srv.agent.ListKeys("", 0)
if err != nil {
t.Fatalf("err: %v", err)
}
Expand All @@ -217,7 +217,7 @@ func TestOperator_KeyringUse(t *testing.T) {
c.EncryptKey = oldKey
}
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)
}

Expand All @@ -233,12 +233,12 @@ func TestOperator_KeyringUse(t *testing.T) {
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)
}

// Make sure only the new key remains
list, err := srv.agent.ListKeys("")
list, err := srv.agent.ListKeys("", 0)
if err != nil {
t.Fatalf("err: %v", err)
}
Expand All @@ -256,3 +256,32 @@ func TestOperator_KeyringUse(t *testing.T) {
}
}, 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)
}
19 changes: 10 additions & 9 deletions command/agent/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ type joinResponse struct {
}

type keyringRequest struct {
Key string
Key string
RelayFactor uint8
}

type KeyringEntry struct {
Expand Down Expand Up @@ -604,21 +605,21 @@ func (i *AgentRPC) handleKeyring(client *rpcClient, seq uint64, cmd, token strin
var r keyringResponse
var err error

if cmd != listKeysCommand {
if err = client.dec.Decode(&req); err != nil {
return fmt.Errorf("decode failed: %v", err)
}
if err = client.dec.Decode(&req); err != nil {
return fmt.Errorf("decode failed: %v", err)
}

i.agent.logger.Printf("[INFO] agent: Sending rpc command with relay factor %d", req.RelayFactor)

switch cmd {
case listKeysCommand:
queryResp, err = i.agent.ListKeys(token)
queryResp, err = i.agent.ListKeys(token, req.RelayFactor)
case installKeyCommand:
queryResp, err = i.agent.InstallKey(req.Key, token)
queryResp, err = i.agent.InstallKey(req.Key, token, req.RelayFactor)
case useKeyCommand:
queryResp, err = i.agent.UseKey(req.Key, token)
queryResp, err = i.agent.UseKey(req.Key, token, req.RelayFactor)
case removeKeyCommand:
queryResp, err = i.agent.RemoveKey(req.Key, token)
queryResp, err = i.agent.RemoveKey(req.Key, token, req.RelayFactor)
default:
respHeader := responseHeader{Seq: seq, Error: unsupportedCommand}
client.Send(&respHeader, nil)
Expand Down
17 changes: 9 additions & 8 deletions command/agent/rpc_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,48 +194,49 @@ func (c *RPCClient) WANMembers() ([]Member, error) {
return resp.Members, err
}

func (c *RPCClient) ListKeys(token string) (keyringResponse, error) {
func (c *RPCClient) ListKeys(token string, relayFactor uint8) (keyringResponse, error) {
header := requestHeader{
Command: listKeysCommand,
Seq: c.getSeq(),
Token: token,
}
req := keyringRequest{RelayFactor: relayFactor}
var resp keyringResponse
err := c.genericRPC(&header, nil, &resp)
err := c.genericRPC(&header, req, &resp)
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{
Command: installKeyCommand,
Seq: c.getSeq(),
Token: token,
}
req := keyringRequest{key}
req := keyringRequest{Key: key, RelayFactor: relayFactor}
var resp keyringResponse
err := c.genericRPC(&header, &req, &resp)
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{
Command: useKeyCommand,
Seq: c.getSeq(),
Token: token,
}
req := keyringRequest{key}
req := keyringRequest{Key: key, RelayFactor: relayFactor}
var resp keyringResponse
err := c.genericRPC(&header, &req, &resp)
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{
Command: removeKeyCommand,
Seq: c.getSeq(),
Token: token,
}
req := keyringRequest{key}
req := keyringRequest{Key: key, RelayFactor: relayFactor}
var resp keyringResponse
err := c.genericRPC(&header, &req, &resp)
return resp, err
Expand Down
Loading

0 comments on commit 5b80490

Please sign in to comment.