diff --git a/command/agent/agent_test.go b/command/agent/agent_test.go index 1e4aec000c..3f398016fa 100644 --- a/command/agent/agent_test.go +++ b/command/agent/agent_test.go @@ -81,11 +81,11 @@ func makeAgentKeyring(t *testing.T, conf *Config, key string) (string, *Agent) { conf.DataDir = dir fileLAN := filepath.Join(dir, serfLANKeyring) - if err := initKeyring(fileLAN, key); err != nil { + if _, err := initKeyring(fileLAN, key); err != nil { t.Fatalf("err: %s", err) } fileWAN := filepath.Join(dir, serfWANKeyring) - if err := initKeyring(fileWAN, key); err != nil { + if _, err := initKeyring(fileWAN, key); err != nil { t.Fatalf("err: %s", err) } diff --git a/command/agent/command.go b/command/agent/command.go index 7bb9496189..8c0455c433 100644 --- a/command/agent/command.go +++ b/command/agent/command.go @@ -156,17 +156,29 @@ func (c *Command) readConfig() *Config { } fileLAN := filepath.Join(config.DataDir, serfLANKeyring) - if err := initKeyring(fileLAN, config.EncryptKey); err != nil { + done, err := initKeyring(fileLAN, config.EncryptKey) + if err != nil { c.Ui.Error(fmt.Sprintf("Error initializing keyring: %s", err)) return nil } + if !done { + c.Ui.Error(fmt.Sprintf( + "WARNING: keyring file %s already exists, not overwriting", + fileLAN)) + } if config.Server { fileWAN := filepath.Join(config.DataDir, serfWANKeyring) - if err := initKeyring(fileWAN, config.EncryptKey); err != nil { + done, err := initKeyring(fileWAN, config.EncryptKey) + if err != nil { c.Ui.Error(fmt.Sprintf("Error initializing keyring: %s", err)) return nil } + if !done { + c.Ui.Error(fmt.Sprintf( + "WARNING: keyring file %s already exists, not overwriting", + fileWAN)) + } } } diff --git a/command/agent/keyring.go b/command/agent/keyring.go index 51a4666536..f0644982e8 100644 --- a/command/agent/keyring.go +++ b/command/agent/keyring.go @@ -18,51 +18,42 @@ const ( serfWANKeyring = "serf/remote.keyring" ) -// initKeyring will create a keyring file at a given path. -func initKeyring(path, key string) error { +// initKeyring will create a keyring file at a given path. Returns whether any +// action was taken and any applicable error. +func initKeyring(path, key string) (bool, error) { var keys []string if _, err := base64.StdEncoding.DecodeString(key); err != nil { - return fmt.Errorf("Invalid key: %s", err) + return false, fmt.Errorf("Invalid key: %s", err) } + // Just exit if the file already exists. if _, err := os.Stat(path); err == nil { - content, err := ioutil.ReadFile(path) - if err != nil { - return err - } - if err := json.Unmarshal(content, &keys); err != nil { - return err - } - for _, existing := range keys { - if key == existing { - return nil - } - } + return false, nil } keys = append(keys, key) keyringBytes, err := json.Marshal(keys) if err != nil { - return err + return false, err } if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil { - return err + return false, err } fh, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600) if err != nil { - return err + return false, err } defer fh.Close() if _, err := fh.Write(keyringBytes); err != nil { os.Remove(path) - return err + return false, err } - return nil + return true, nil } // loadKeyringFile will load a gossip encryption keyring out of a file. The file diff --git a/command/agent/keyring_test.go b/command/agent/keyring_test.go index 734a67dc14..50fb1e1b40 100644 --- a/command/agent/keyring_test.go +++ b/command/agent/keyring_test.go @@ -1,12 +1,10 @@ package agent import ( - "bytes" - "encoding/json" + "fmt" "io/ioutil" "os" "path/filepath" - "strings" "testing" ) @@ -78,6 +76,7 @@ func TestAgent_LoadKeyrings(t *testing.T) { func TestAgent_InitKeyring(t *testing.T) { key1 := "tbLJg26ZJyJ9pK3qhc9jig==" key2 := "4leC33rgtXKIVUr9Nr0snQ==" + expected := fmt.Sprintf(`["%s"]`, key1) dir, err := ioutil.TempDir("", "consul") if err != nil { @@ -88,56 +87,36 @@ func TestAgent_InitKeyring(t *testing.T) { file := filepath.Join(dir, "keyring") // First initialize the keyring - if err := initKeyring(file, key1); err != nil { - t.Fatalf("err: %s", err) - } - - content1, err := ioutil.ReadFile(file) + done, err := initKeyring(file, key1) if err != nil { t.Fatalf("err: %s", err) } - if !strings.Contains(string(content1), key1) { - t.Fatalf("bad: %s", content1) - } - if strings.Contains(string(content1), key2) { - t.Fatalf("bad: %s", content1) + if !done { + t.Fatalf("should have modified keyring") } - // Now initialize again with the same key - if err := initKeyring(file, key1); err != nil { - t.Fatalf("err: %s", err) - } - - content2, err := ioutil.ReadFile(file) + content, err := ioutil.ReadFile(file) if err != nil { t.Fatalf("err: %s", err) } - if !bytes.Equal(content1, content2) { - t.Fatalf("bad: %s", content2) + if string(content) != expected { + t.Fatalf("bad: %s", content) } - // Initialize an existing keyring with a new key - if err := initKeyring(file, key2); err != nil { - t.Fatalf("err: %s", err) - } - - content3, err := ioutil.ReadFile(file) + // Try initializing again with a different key + done, err = initKeyring(file, key2) if err != nil { t.Fatalf("err: %s", err) } - if !strings.Contains(string(content3), key1) { - t.Fatalf("bad: %s", content3) - } - if !strings.Contains(string(content3), key2) { - t.Fatalf("bad: %s", content3) + if done { + t.Fatalf("should not have modified keyring") } - // Unmarshal and make sure that key1 is still primary - var keys []string - if err := json.Unmarshal(content3, &keys); err != nil { + content, err = ioutil.ReadFile(file) + if err != nil { t.Fatalf("err: %s", err) } - if keys[0] != key1 { - t.Fatalf("bad: %#v", keys) + if string(content) != expected { + t.Fatalf("bad: %s", content) } } diff --git a/website/source/docs/agent/options.html.markdown b/website/source/docs/agent/options.html.markdown index 9046b03d1d..ece3157a3b 100644 --- a/website/source/docs/agent/options.html.markdown +++ b/website/source/docs/agent/options.html.markdown @@ -93,9 +93,8 @@ The options below are all specified on the command-line. automatically whenever the agent is restarted. This means that to encrypt Consul's gossip protocol, this option only needs to be provided once on each agent's initial startup sequence. If it is provided after Consul has been - initialized with an encryption key, then the provided key is simply added - as a secondary encryption key. More information on how keys can be changed - is available on the [keyring command](/docs/commands/keyring.html) page. + initialized with an encryption key, then the provided key is ignored and + a warning will be displayed. * `-join` - Address of another agent to join upon starting up. This can be specified multiple times to specify multiple agents to join. If Consul is