diff --git a/command/agent/agent.go b/command/agent/agent.go index 169db3276b..f8b477147c 100644 --- a/command/agent/agent.go +++ b/command/agent/agent.go @@ -1,11 +1,8 @@ package agent import ( - "encoding/base64" - "encoding/json" "fmt" "io" - "io/ioutil" "log" "net" "os" @@ -15,15 +12,9 @@ import ( "github.com/hashicorp/consul/consul" "github.com/hashicorp/consul/consul/structs" - "github.com/hashicorp/memberlist" "github.com/hashicorp/serf/serf" ) -const ( - SerfLANKeyring = "serf/local.keyring" - SerfWANKeyring = "serf/remote.keyring" -) - /* The agent is the long running process that is run on every machine. It exposes an RPC interface that is used by the CLI to control the @@ -687,124 +678,3 @@ func (a *Agent) deletePid() error { } return nil } - -// loadKeyringFile will load a gossip encryption keyring out of a file. The file -// must be in JSON format and contain a list of encryption key strings. -func loadKeyringFile(c *serf.Config) error { - if c.KeyringFile == "" { - return nil - } - - if _, err := os.Stat(c.KeyringFile); err != nil { - return err - } - - // Read in the keyring file data - keyringData, err := ioutil.ReadFile(c.KeyringFile) - if err != nil { - return err - } - - // Decode keyring JSON - keys := make([]string, 0) - if err := json.Unmarshal(keyringData, &keys); err != nil { - return err - } - - // Decode base64 values - keysDecoded := make([][]byte, len(keys)) - for i, key := range keys { - keyBytes, err := base64.StdEncoding.DecodeString(key) - if err != nil { - return err - } - keysDecoded[i] = keyBytes - } - - // Create the keyring - keyring, err := memberlist.NewKeyring(keysDecoded, keysDecoded[0]) - if err != nil { - return err - } - - c.MemberlistConfig.Keyring = keyring - - // Success! - return nil -} - -// ListKeysLAN returns the keys installed on the LAN gossip pool -func (a *Agent) ListKeysLAN() (*serf.KeyResponse, error) { - if a.server != nil { - km := a.server.KeyManagerLAN() - return km.ListKeys() - } - km := a.client.KeyManagerLAN() - return km.ListKeys() -} - -// ListKeysWAN returns the keys installed on the WAN gossip pool -func (a *Agent) ListKeysWAN() (*serf.KeyResponse, error) { - if a.server != nil { - km := a.server.KeyManagerWAN() - return km.ListKeys() - } - return nil, fmt.Errorf("WAN keyring not available on client node") -} - -// InstallKeyWAN installs a new WAN gossip encryption key on server nodes -func (a *Agent) InstallKeyWAN(key string) (*serf.KeyResponse, error) { - if a.server != nil { - km := a.server.KeyManagerWAN() - return km.InstallKey(key) - } - return nil, fmt.Errorf("WAN keyring not available on client node") -} - -// InstallKeyLAN installs a new LAN gossip encryption key on all nodes -func (a *Agent) InstallKeyLAN(key string) (*serf.KeyResponse, error) { - if a.server != nil { - km := a.server.KeyManagerLAN() - return km.InstallKey(key) - } - km := a.client.KeyManagerLAN() - return km.InstallKey(key) -} - -// UseKeyWAN changes the primary WAN gossip encryption key on server nodes -func (a *Agent) UseKeyWAN(key string) (*serf.KeyResponse, error) { - if a.server != nil { - km := a.server.KeyManagerWAN() - return km.UseKey(key) - } - return nil, fmt.Errorf("WAN keyring not available on client node") -} - -// UseKeyLAN changes the primary LAN gossip encryption key on all nodes -func (a *Agent) UseKeyLAN(key string) (*serf.KeyResponse, error) { - if a.server != nil { - km := a.server.KeyManagerLAN() - return km.UseKey(key) - } - km := a.client.KeyManagerLAN() - return km.UseKey(key) -} - -// RemoveKeyWAN removes a WAN gossip encryption key on server nodes -func (a *Agent) RemoveKeyWAN(key string) (*serf.KeyResponse, error) { - if a.server != nil { - km := a.server.KeyManagerWAN() - return km.RemoveKey(key) - } - return nil, fmt.Errorf("WAN keyring not available on client node") -} - -// RemoveKeyLAN removes a LAN gossip encryption key on all nodes -func (a *Agent) RemoveKeyLAN(key string) (*serf.KeyResponse, error) { - if a.server != nil { - km := a.server.KeyManagerLAN() - return km.RemoveKey(key) - } - km := a.client.KeyManagerLAN() - return km.RemoveKey(key) -} diff --git a/command/agent/keyring.go b/command/agent/keyring.go new file mode 100644 index 0000000000..dc63b4f59a --- /dev/null +++ b/command/agent/keyring.go @@ -0,0 +1,138 @@ +package agent + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "io/ioutil" + "os" + + "github.com/hashicorp/memberlist" + "github.com/hashicorp/serf/serf" +) + +const ( + SerfLANKeyring = "serf/local.keyring" + SerfWANKeyring = "serf/remote.keyring" +) + +// loadKeyringFile will load a gossip encryption keyring out of a file. The file +// must be in JSON format and contain a list of encryption key strings. +func loadKeyringFile(c *serf.Config) error { + if c.KeyringFile == "" { + return nil + } + + if _, err := os.Stat(c.KeyringFile); err != nil { + return err + } + + // Read in the keyring file data + keyringData, err := ioutil.ReadFile(c.KeyringFile) + if err != nil { + return err + } + + // Decode keyring JSON + keys := make([]string, 0) + if err := json.Unmarshal(keyringData, &keys); err != nil { + return err + } + + // Decode base64 values + keysDecoded := make([][]byte, len(keys)) + for i, key := range keys { + keyBytes, err := base64.StdEncoding.DecodeString(key) + if err != nil { + return err + } + keysDecoded[i] = keyBytes + } + + // Create the keyring + keyring, err := memberlist.NewKeyring(keysDecoded, keysDecoded[0]) + if err != nil { + return err + } + + c.MemberlistConfig.Keyring = keyring + + // Success! + return nil +} + +// ListKeysLAN returns the keys installed on the LAN gossip pool +func (a *Agent) ListKeysLAN() (*serf.KeyResponse, error) { + if a.server != nil { + km := a.server.KeyManagerLAN() + return km.ListKeys() + } + km := a.client.KeyManagerLAN() + return km.ListKeys() +} + +// ListKeysWAN returns the keys installed on the WAN gossip pool +func (a *Agent) ListKeysWAN() (*serf.KeyResponse, error) { + if a.server != nil { + km := a.server.KeyManagerWAN() + return km.ListKeys() + } + return nil, fmt.Errorf("WAN keyring not available on client node") +} + +// InstallKeyWAN installs a new WAN gossip encryption key on server nodes +func (a *Agent) InstallKeyWAN(key string) (*serf.KeyResponse, error) { + if a.server != nil { + km := a.server.KeyManagerWAN() + return km.InstallKey(key) + } + return nil, fmt.Errorf("WAN keyring not available on client node") +} + +// InstallKeyLAN installs a new LAN gossip encryption key on all nodes +func (a *Agent) InstallKeyLAN(key string) (*serf.KeyResponse, error) { + if a.server != nil { + km := a.server.KeyManagerLAN() + return km.InstallKey(key) + } + km := a.client.KeyManagerLAN() + return km.InstallKey(key) +} + +// UseKeyWAN changes the primary WAN gossip encryption key on server nodes +func (a *Agent) UseKeyWAN(key string) (*serf.KeyResponse, error) { + if a.server != nil { + km := a.server.KeyManagerWAN() + return km.UseKey(key) + } + return nil, fmt.Errorf("WAN keyring not available on client node") +} + +// UseKeyLAN changes the primary LAN gossip encryption key on all nodes +func (a *Agent) UseKeyLAN(key string) (*serf.KeyResponse, error) { + if a.server != nil { + km := a.server.KeyManagerLAN() + return km.UseKey(key) + } + km := a.client.KeyManagerLAN() + return km.UseKey(key) +} + +// RemoveKeyWAN removes a WAN gossip encryption key on server nodes +func (a *Agent) RemoveKeyWAN(key string) (*serf.KeyResponse, error) { + if a.server != nil { + km := a.server.KeyManagerWAN() + return km.RemoveKey(key) + } + return nil, fmt.Errorf("WAN keyring not available on client node") +} + +// RemoveKeyLAN removes a LAN gossip encryption key on all nodes +func (a *Agent) RemoveKeyLAN(key string) (*serf.KeyResponse, error) { + if a.server != nil { + km := a.server.KeyManagerLAN() + return km.RemoveKey(key) + } + km := a.client.KeyManagerLAN() + return km.RemoveKey(key) +} diff --git a/command/agent/keyring_test.go b/command/agent/keyring_test.go new file mode 100644 index 0000000000..7807e53761 --- /dev/null +++ b/command/agent/keyring_test.go @@ -0,0 +1,71 @@ +package agent + +import ( + "os" + "testing" +) + +func TestAgent_LoadKeyrings(t *testing.T) { + key := "tbLJg26ZJyJ9pK3qhc9jig==" + + // Should be no configured keyring file by default + conf1 := nextConfig() + dir1, agent1 := makeAgent(t, conf1) + defer os.RemoveAll(dir1) + defer agent1.Shutdown() + + c := agent1.config.ConsulConfig + if c.SerfLANConfig.KeyringFile != "" { + t.Fatalf("bad: %#v", c.SerfLANConfig.KeyringFile) + } + if c.SerfLANConfig.MemberlistConfig.Keyring != nil { + t.Fatalf("keyring should not be loaded") + } + if c.SerfWANConfig.KeyringFile != "" { + t.Fatalf("bad: %#v", c.SerfLANConfig.KeyringFile) + } + if c.SerfWANConfig.MemberlistConfig.Keyring != nil { + t.Fatalf("keyring should not be loaded") + } + + // Server should auto-load LAN and WAN keyring files + conf2 := nextConfig() + dir2, agent2 := makeAgentKeyring(t, conf2, key) + defer os.RemoveAll(dir2) + defer agent2.Shutdown() + + c = agent2.config.ConsulConfig + if c.SerfLANConfig.KeyringFile == "" { + t.Fatalf("should have keyring file") + } + if c.SerfLANConfig.MemberlistConfig.Keyring == nil { + t.Fatalf("keyring should be loaded") + } + if c.SerfWANConfig.KeyringFile == "" { + t.Fatalf("should have keyring file") + } + if c.SerfWANConfig.MemberlistConfig.Keyring == nil { + t.Fatalf("keyring should be loaded") + } + + // Client should auto-load only the LAN keyring file + conf3 := nextConfig() + conf3.Server = false + dir3, agent3 := makeAgentKeyring(t, conf3, key) + defer os.RemoveAll(dir3) + defer agent3.Shutdown() + + c = agent3.config.ConsulConfig + if c.SerfLANConfig.KeyringFile == "" { + t.Fatalf("should have keyring file") + } + if c.SerfLANConfig.MemberlistConfig.Keyring == nil { + t.Fatalf("keyring should be loaded") + } + if c.SerfWANConfig.KeyringFile != "" { + t.Fatalf("bad: %#v", c.SerfLANConfig.KeyringFile) + } + if c.SerfWANConfig.MemberlistConfig.Keyring != nil { + t.Fatalf("keyring should not be loaded") + } +}