02de4c8b76
During gossip encryption key rotation it would be nice to be able to see if all nodes are using the same key. This PR adds another field to the json response from `GET v1/operator/keyring` which lists the primary keys in use per dc. That way an operator can tell when a key was successfully setup as primary key. Based on https://github.com/hashicorp/serf/pull/611 to add primary key to list keyring output: ```json [ { "WAN": true, "Datacenter": "dc2", "Segment": "", "Keys": { "0OuM4oC3Os18OblWiBbZUaHA7Hk+tNs/6nhNYtaNduM=": 6, "SINm887hKTzmMWeBNKTJReaTLX3mBEJKriDyt88Ad+g=": 6 }, "PrimaryKeys": { "SINm887hKTzmMWeBNKTJReaTLX3mBEJKriDyt88Ad+g=": 6 }, "NumNodes": 6 }, { "WAN": false, "Datacenter": "dc2", "Segment": "", "Keys": { "0OuM4oC3Os18OblWiBbZUaHA7Hk+tNs/6nhNYtaNduM=": 8, "SINm887hKTzmMWeBNKTJReaTLX3mBEJKriDyt88Ad+g=": 8 }, "PrimaryKeys": { "SINm887hKTzmMWeBNKTJReaTLX3mBEJKriDyt88Ad+g=": 8 }, "NumNodes": 8 }, { "WAN": false, "Datacenter": "dc1", "Segment": "", "Keys": { "0OuM4oC3Os18OblWiBbZUaHA7Hk+tNs/6nhNYtaNduM=": 3, "SINm887hKTzmMWeBNKTJReaTLX3mBEJKriDyt88Ad+g=": 8 }, "PrimaryKeys": { "SINm887hKTzmMWeBNKTJReaTLX3mBEJKriDyt88Ad+g=": 8 }, "NumNodes": 8 } ] ``` I intentionally did not change the CLI output because I didn't find a good way of displaying this information. There are a couple of options that we could implement later: * add a flag to show the primary keys * add a flag to show json output Fixes #3393.
93 lines
2.3 KiB
Go
93 lines
2.3 KiB
Go
package api
|
|
|
|
// keyringRequest is used for performing Keyring operations
|
|
type keyringRequest struct {
|
|
Key string
|
|
}
|
|
|
|
// KeyringResponse is returned when listing the gossip encryption keys
|
|
type KeyringResponse struct {
|
|
// Whether this response is for a WAN ring
|
|
WAN bool
|
|
|
|
// The datacenter name this request corresponds to
|
|
Datacenter string
|
|
|
|
// Segment has the network segment this request corresponds to.
|
|
Segment string
|
|
|
|
// Messages has information or errors from serf
|
|
Messages map[string]string `json:",omitempty"`
|
|
|
|
// A map of the encryption keys to the number of nodes they're installed on
|
|
Keys map[string]int
|
|
|
|
// A map of the encryption primary keys to the number of nodes they're installed on
|
|
PrimaryKeys map[string]int
|
|
|
|
// The total number of nodes in this ring
|
|
NumNodes int
|
|
}
|
|
|
|
// KeyringInstall is used to install a new gossip encryption key into the cluster
|
|
func (op *Operator) KeyringInstall(key string, q *WriteOptions) error {
|
|
r := op.c.newRequest("POST", "/v1/operator/keyring")
|
|
r.setWriteOptions(q)
|
|
r.obj = keyringRequest{
|
|
Key: key,
|
|
}
|
|
_, resp, err := requireOK(op.c.doRequest(r))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
resp.Body.Close()
|
|
return nil
|
|
}
|
|
|
|
// KeyringList is used to list the gossip keys installed in the cluster
|
|
func (op *Operator) KeyringList(q *QueryOptions) ([]*KeyringResponse, error) {
|
|
r := op.c.newRequest("GET", "/v1/operator/keyring")
|
|
r.setQueryOptions(q)
|
|
_, resp, err := requireOK(op.c.doRequest(r))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
var out []*KeyringResponse
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// KeyringRemove is used to remove a gossip encryption key from the cluster
|
|
func (op *Operator) KeyringRemove(key string, q *WriteOptions) error {
|
|
r := op.c.newRequest("DELETE", "/v1/operator/keyring")
|
|
r.setWriteOptions(q)
|
|
r.obj = keyringRequest{
|
|
Key: key,
|
|
}
|
|
_, resp, err := requireOK(op.c.doRequest(r))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
resp.Body.Close()
|
|
return nil
|
|
}
|
|
|
|
// KeyringUse is used to change the active gossip encryption key
|
|
func (op *Operator) KeyringUse(key string, q *WriteOptions) error {
|
|
r := op.c.newRequest("PUT", "/v1/operator/keyring")
|
|
r.setWriteOptions(q)
|
|
r.obj = keyringRequest{
|
|
Key: key,
|
|
}
|
|
_, resp, err := requireOK(op.c.doRequest(r))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
resp.Body.Close()
|
|
return nil
|
|
}
|