From 3dd425c884727c2bbcc6259110f22ba78b5d0e96 Mon Sep 17 00:00:00 2001 From: Kent 'picat' Gruber Date: Wed, 30 Sep 2020 17:02:37 -0400 Subject: [PATCH 1/7] Generate 32-byte gossip key for nomad operator keygen command The key generated from this command is used for gossip encrpytion, which utilizes AES GCM encryption. Using a key size of 16-bytes enables AES-128 while a key size of 32 bytes enables AES-256. The underlying memberlist library supports the larger key size, and is ultimatley preferable from a security standpoint. Consul also uses 32 bytes by default: https://github.com/hashicorp/consul/commit/1a14b94441dbd715fcb83d78c99fe87de9ad01ea --- command/operator_keygen.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/command/operator_keygen.go b/command/operator_keygen.go index 62dab3bae..f08a60a06 100644 --- a/command/operator_keygen.go +++ b/command/operator_keygen.go @@ -21,7 +21,7 @@ func (c *OperatorKeygenCommand) Help() string { helpText := ` Usage: nomad operator keygen - Generates a new encryption key that can be used to configure the + Generates a new 32-byte encryption key that can be used to configure the agent to encrypt traffic. The output of this command is already in the proper format that the agent expects. ` @@ -31,13 +31,13 @@ Usage: nomad operator keygen func (c *OperatorKeygenCommand) Name() string { return "operator keygen" } func (c *OperatorKeygenCommand) Run(_ []string) int { - key := make([]byte, 16) + key := make([]byte, 32) n, err := rand.Reader.Read(key) if err != nil { c.Ui.Error(fmt.Sprintf("Error reading random data: %s", err)) return 1 } - if n != 16 { + if n != 32 { c.Ui.Error(fmt.Sprintf("Couldn't read enough entropy. Generate more entropy!")) return 1 } From 37155dc881f23b2134407dd13694980c09e6c03a Mon Sep 17 00:00:00 2001 From: Kent 'picat' Gruber Date: Wed, 30 Sep 2020 17:03:12 -0400 Subject: [PATCH 2/7] Update server configuration docs to use 32 bytes --- website/pages/docs/configuration/server.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/pages/docs/configuration/server.mdx b/website/pages/docs/configuration/server.mdx index 77b2d3aba..5c7cbd05b 100644 --- a/website/pages/docs/configuration/server.mdx +++ b/website/pages/docs/configuration/server.mdx @@ -55,7 +55,7 @@ server { worker threads will dequeue for processing. - `encrypt` `(string: "")` - Specifies the secret key to use for encryption of - Nomad server's gossip network traffic. This key must be 16 bytes that are + Nomad server's gossip network traffic. This key must be 32 bytes that are base64-encoded. The provided key is automatically persisted to the data directory and loaded automatically whenever the agent is restarted. This means that to encrypt Nomad server's gossip protocol, this option only needs to be From 435802e3dbd3ee9ac9fee49a07fd9443352aeb44 Mon Sep 17 00:00:00 2001 From: Kent 'picat' Gruber Date: Wed, 30 Sep 2020 17:04:33 -0400 Subject: [PATCH 3/7] Fix operator keygen test to check for 32 bytes --- command/operator_keygen_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/operator_keygen_test.go b/command/operator_keygen_test.go index aef615692..22c57bff6 100644 --- a/command/operator_keygen_test.go +++ b/command/operator_keygen_test.go @@ -22,7 +22,7 @@ func TestKeygenCommand(t *testing.T) { t.Fatalf("err: %s", err) } - if len(result) != 16 { + if len(result) != 32 { t.Fatalf("bad: %#v", result) } } From e461ba01a9482e5813d253bee8aadefb9e6b181d Mon Sep 17 00:00:00 2001 From: Kent 'picat' Gruber Date: Wed, 30 Sep 2020 17:07:31 -0400 Subject: [PATCH 4/7] Update nomad operator keygen example command in docs --- website/pages/docs/commands/operator/keygen.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/pages/docs/commands/operator/keygen.mdx b/website/pages/docs/commands/operator/keygen.mdx index 3baf7bfd6..da8e9b649 100644 --- a/website/pages/docs/commands/operator/keygen.mdx +++ b/website/pages/docs/commands/operator/keygen.mdx @@ -24,5 +24,5 @@ nomad operator keygen ```shell-session $ nomad operator keygen -YgZOXLMhC7TtZqeghMT8+w== +6RhfKFZ5uYEaU6RgWzx69ssLcpiIkvnEZs5KBOQxvxA= ``` From 2ae72441b50b341c2fa5ad07640eb3c2818b8043 Mon Sep 17 00:00:00 2001 From: Kent 'picat' Gruber Date: Thu, 1 Oct 2020 11:12:14 -0400 Subject: [PATCH 5/7] Log AES-128 and AES-192 key sizes during keyring initialization --- command/agent/agent.go | 2 +- command/agent/keyring.go | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/command/agent/agent.go b/command/agent/agent.go index 909b7c9f7..4507bd5f6 100644 --- a/command/agent/agent.go +++ b/command/agent/agent.go @@ -807,7 +807,7 @@ func (a *Agent) setupKeyrings(config *nomad.Config) error { goto LOAD } if _, err := os.Stat(file); err != nil { - if err := initKeyring(file, a.config.Server.EncryptKey); err != nil { + if err := initKeyring(file, a.config.Server.EncryptKey, a.logger); err != nil { return err } } diff --git a/command/agent/keyring.go b/command/agent/keyring.go index ca509506b..a8537b9ca 100644 --- a/command/agent/keyring.go +++ b/command/agent/keyring.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" + log "github.com/hashicorp/go-hclog" "github.com/hashicorp/memberlist" "github.com/hashicorp/serf/serf" ) @@ -17,7 +18,7 @@ const ( ) // initKeyring will create a keyring file at a given path. -func initKeyring(path, key string) error { +func initKeyring(path, key string, l log.Logger) error { var keys []string if keyBytes, err := base64.StdEncoding.DecodeString(key); err != nil { @@ -26,6 +27,19 @@ func initKeyring(path, key string) error { return fmt.Errorf("Invalid key: %s", err) } + // Check for AES-256 key size (32-bytes) + if len(key) < 32 { + var encMethod string + switch len(key) { + case 16: + encMethod = "AES-128" + case 24: + encMethod = "AES-192" + } + msg := fmt.Sprintf("given %d-byte gossip key enables %s encryption, generate a 32-byte key to enable AES-256", len(key), encMethod) + l.Info(msg) + } + // Just exit if the file already exists. if _, err := os.Stat(path); err == nil { return nil From 614729bd4cd85f328be3f63b3ee744c9c6d7f6fc Mon Sep 17 00:00:00 2001 From: Kent 'picat' Gruber Date: Thu, 1 Oct 2020 11:13:06 -0400 Subject: [PATCH 6/7] Fix other usages of initKeyring func to use logger as third argument --- command/agent/keyring_test.go | 8 ++++++-- command/agent/testagent.go | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/command/agent/keyring_test.go b/command/agent/keyring_test.go index fc22bc8a0..3408d8df0 100644 --- a/command/agent/keyring_test.go +++ b/command/agent/keyring_test.go @@ -6,6 +6,8 @@ import ( "os" "path/filepath" "testing" + + "github.com/hashicorp/go-hclog" ) func TestAgent_LoadKeyrings(t *testing.T) { @@ -56,8 +58,10 @@ func TestAgent_InitKeyring(t *testing.T) { file := filepath.Join(dir, "keyring") + logger := hclog.NewNullLogger() + // First initialize the keyring - if err := initKeyring(file, key1); err != nil { + if err := initKeyring(file, key1, logger); err != nil { t.Fatalf("err: %s", err) } @@ -70,7 +74,7 @@ func TestAgent_InitKeyring(t *testing.T) { } // Try initializing again with a different key - if err := initKeyring(file, key2); err != nil { + if err := initKeyring(file, key2, logger); err != nil { t.Fatalf("err: %s", err) } diff --git a/command/agent/testagent.go b/command/agent/testagent.go index 1fa14879a..8ae07c3c9 100644 --- a/command/agent/testagent.go +++ b/command/agent/testagent.go @@ -147,7 +147,7 @@ RETRY: if a.Key != "" { writeKey := func(key, filename string) { path := filepath.Join(a.Config.DataDir, filename) - if err := initKeyring(path, key); err != nil { + if err := initKeyring(path, key, a.logger); err != nil { a.T.Fatalf("Error creating keyring %s: %s", path, err) } } From a38e33a9c83c71119a69ad0ec4e5111aee911773 Mon Sep 17 00:00:00 2001 From: Kent 'picat' Gruber Date: Fri, 2 Oct 2020 11:10:27 -0400 Subject: [PATCH 7/7] Fix panic in test due to the agent's logger not being initialized yet So a null logger is used to avoid the problem. --- command/agent/testagent.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/command/agent/testagent.go b/command/agent/testagent.go index 8ae07c3c9..507c76f1c 100644 --- a/command/agent/testagent.go +++ b/command/agent/testagent.go @@ -143,11 +143,15 @@ RETRY: a.Config.NodeName = fmt.Sprintf("Node %d", a.Config.Ports.RPC) } + // Create a null logger before initializing the keyring. This is typically + // done using the agent's logger. However, it hasn't been created yet. + logger := hclog.NewNullLogger() + // write the keyring if a.Key != "" { writeKey := func(key, filename string) { path := filepath.Join(a.Config.DataDir, filename) - if err := initKeyring(path, key, a.logger); err != nil { + if err := initKeyring(path, key, logger); err != nil { a.T.Fatalf("Error creating keyring %s: %s", path, err) } }