From 486b6b7541aa72729d45f9e533b75c873df910ee Mon Sep 17 00:00:00 2001 From: Ryan Cragun Date: Fri, 7 Mar 2025 15:20:58 -0700 Subject: [PATCH] VAULT-34581: retry_join: handle escapes in `auto_join` config (#29874) `go-discover` supports being configured with some configuration strings that include double-quotes, backslashes and escapes. As such, we now use its own parser when normalizing `auto_join` config that may have addresses. Signed-off-by: Ryan Cragun --- command/server/config.go | 20 +++++++++---------- command/server/config_test_helpers.go | 13 +++++++----- .../test-fixtures/raft_retry_join_attr.hcl | 3 +++ .../test-fixtures/raft_retry_join_block.hcl | 3 +++ .../test-fixtures/raft_retry_join_mixed.hcl | 3 +++ 5 files changed, 26 insertions(+), 16 deletions(-) diff --git a/command/server/config.go b/command/server/config.go index 623e098c38..053070692c 100644 --- a/command/server/config.go +++ b/command/server/config.go @@ -16,6 +16,7 @@ import ( "strings" "time" + "github.com/hashicorp/go-discover" "github.com/hashicorp/go-multierror" "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/hcl" @@ -1101,20 +1102,17 @@ func normalizeRaftRetryJoin(val any) ([]byte, error) { for k, v := range stanza { switch k { case "auto_join": - pairs := strings.Split(v.(string), " ") - for i, pair := range pairs { - pairParts := strings.Split(pair, "=") - if len(pairParts) != 2 { - return nil, fmt.Errorf("malformed auto_join pair %s, expected key=value", pair) - } + cfg, err := discover.Parse(v.(string)) + if err != nil { + return nil, err + } + for k, v := range cfg { // These are auto_join keys that are valid for the provider in go-discover - if slices.Contains([]string{"domain", "auth_url", "url", "host"}, pairParts[0]) { - pairParts[1] = configutil.NormalizeAddr(pairParts[1]) - pair = strings.Join(pairParts, "=") - pairs[i] = pair + if slices.Contains([]string{"domain", "auth_url", "url", "host"}, k) { + cfg[k] = configutil.NormalizeAddr(v) } } - normalizedStanza[k] = strings.Join(pairs, " ") + normalizedStanza[k] = cfg.String() case "leader_api_addr": normalizedStanza[k] = configutil.NormalizeAddr(v.(string)) default: diff --git a/command/server/config_test_helpers.go b/command/server/config_test_helpers.go index 9d609d2219..c71d610ee4 100644 --- a/command/server/config_test_helpers.go +++ b/command/server/config_test_helpers.go @@ -35,13 +35,16 @@ func testConfigRaftRetryJoin(t *testing.T) { t.Parallel() retryJoinExpected := []map[string]string{ + // NOTE: Normalization handles IPv6 addresses and returns auto_join with + // sorted stable keys. {"leader_api_addr": "http://127.0.0.1:8200"}, {"leader_api_addr": "http://[2001:db8::2:1]:8200"}, - {"auto_join": "provider=mdns service=consul domain=2001:db8::2:1"}, - {"auto_join": "provider=os tag_key=consul tag_value=server username=foo password=bar auth_url=https://[2001:db8::2:1]/auth"}, - {"auto_join": "provider=triton account=testaccount url=https://[2001:db8::2:1] key_id=1234 tag_key=consul-role tag_value=server"}, - {"auto_join": "provider=packet auth_token=token project=uuid url=https://[2001:db8::2:1] address_type=public_v6"}, - {"auto_join": "provider=vsphere category_name=consul-role tag_name=consul-server host=https://[2001:db8::2:1] user=foo password=bar insecure_ssl=false"}, + {"auto_join": "provider=mdns domain=2001:db8::2:1 service=consul"}, + {"auto_join": "provider=os auth_url=https://[2001:db8::2:1]/auth password=bar tag_key=consul tag_value=server username=foo"}, + {"auto_join": "provider=triton account=testaccount key_id=1234 tag_key=consul-role tag_value=server url=https://[2001:db8::2:1]"}, + {"auto_join": "provider=packet address_type=public_v6 auth_token=token project=uuid url=https://[2001:db8::2:1]"}, + {"auto_join": "provider=vsphere category_name=consul-role host=https://[2001:db8::2:1] insecure_ssl=false password=bar tag_name=consul-server user=foo"}, + {"auto_join": "provider=k8s label_selector=\"app.kubernetes.io/name=vault, component=server\" namespace=vault"}, } for _, cfg := range []string{ "attr", diff --git a/command/server/test-fixtures/raft_retry_join_attr.hcl b/command/server/test-fixtures/raft_retry_join_attr.hcl index 883ba96b67..abe69c3ed4 100644 --- a/command/server/test-fixtures/raft_retry_join_attr.hcl +++ b/command/server/test-fixtures/raft_retry_join_attr.hcl @@ -23,6 +23,9 @@ storage "raft" { retry_join = [ { "auto_join" = "provider=vsphere category_name=consul-role tag_name=consul-server host=https://[2001:db8:0:0:0:0:2:1] user=foo password=bar insecure_ssl=false" } ] + retry_join = [ + { "auto_join" = "provider=k8s namespace=vault label_selector=\"app.kubernetes.io/name=vault, component=server\"" } + ] } listener "tcp" { diff --git a/command/server/test-fixtures/raft_retry_join_block.hcl b/command/server/test-fixtures/raft_retry_join_block.hcl index 762bd5fd90..6202b2a161 100644 --- a/command/server/test-fixtures/raft_retry_join_block.hcl +++ b/command/server/test-fixtures/raft_retry_join_block.hcl @@ -26,6 +26,9 @@ storage "raft" { retry_join { "auto_join" = "provider=vsphere category_name=consul-role tag_name=consul-server host=https://[2001:db8:0:0:0:0:2:1] user=foo password=bar insecure_ssl=false" } + retry_join { + "auto_join" = "provider=k8s namespace=vault label_selector=\"app.kubernetes.io/name=vault, component=server\"" + } } listener "tcp" { diff --git a/command/server/test-fixtures/raft_retry_join_mixed.hcl b/command/server/test-fixtures/raft_retry_join_mixed.hcl index 9a5905d498..efbbc68a9b 100644 --- a/command/server/test-fixtures/raft_retry_join_mixed.hcl +++ b/command/server/test-fixtures/raft_retry_join_mixed.hcl @@ -23,6 +23,9 @@ storage "raft" { retry_join { "auto_join" = "provider=vsphere category_name=consul-role tag_name=consul-server host=https://[2001:db8:0:0:0:0:2:1] user=foo password=bar insecure_ssl=false" } + retry_join = [ + { "auto_join" = "provider=k8s namespace=vault label_selector=\"app.kubernetes.io/name=vault, component=server\"" } + ] } listener "tcp" {