From cddc1b0127e78f483fcdb34c8b61e720bb11f267 Mon Sep 17 00:00:00 2001 From: James Rasell Date: Tue, 2 Sep 2025 11:07:49 +0100 Subject: [PATCH] config: Validate keyring config to catch invalid provider types. (#26673) --- .changelog/26673.txt | 3 ++ command/agent/command.go | 9 ++++- command/agent/command_test.go | 17 +++++++++ nomad/structs/keyring.go | 16 ++++++++ nomad/structs/keyring_test.go | 69 +++++++++++++++++++++++++++++++++++ 5 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 .changelog/26673.txt diff --git a/.changelog/26673.txt b/.changelog/26673.txt new file mode 100644 index 000000000..db249cf1c --- /dev/null +++ b/.changelog/26673.txt @@ -0,0 +1,3 @@ +```release-note:improvement +config: Validate the `keyring` configuration block label against supported values on agent startup +``` diff --git a/command/agent/command.go b/command/agent/command.go index 51c29823a..10a5601ee 100644 --- a/command/agent/command.go +++ b/command/agent/command.go @@ -546,6 +546,13 @@ func (c *Command) IsValidConfig(config, cmdConfig *Config) bool { c.Ui.Warn("Please remove deprecated protocol_version field from config.") } + for _, keyring := range config.KEKProviders { + if err := keyring.Validate(); err != nil { + c.Ui.Error(fmt.Sprintf("keyring %q invalid: %v", keyring.Name, err)) + return false + } + } + return true } @@ -1580,7 +1587,7 @@ Client Options: -network-interface Forces the network fingerprinter to use the specified network interface. - + -preferred-address-family Specify which IP family to prefer when selecting an IP address of the network interface. Valid values are "ipv4" and "ipv6". When not specified, diff --git a/command/agent/command_test.go b/command/agent/command_test.go index 56a9830d0..5cac87f7f 100644 --- a/command/agent/command_test.go +++ b/command/agent/command_test.go @@ -485,6 +485,23 @@ func TestIsValidConfig(t *testing.T) { }, err: "missing protocol scheme", }, + { + name: "invalidate keyring provider", + conf: Config{ + DataDir: "/tmp", + Server: &ServerConfig{ + BootstrapExpect: 1, + Enabled: true, + }, + KEKProviders: []*structs.KEKProviderConfig{ + { + Name: "invalid", + Provider: "foo", + }, + }, + }, + err: "unknown keyring provider", + }, } for _, tc := range cases { diff --git a/nomad/structs/keyring.go b/nomad/structs/keyring.go index 363f9bd70..304bd421f 100644 --- a/nomad/structs/keyring.go +++ b/nomad/structs/keyring.go @@ -297,6 +297,22 @@ type KEKProviderConfig struct { ExtraKeysHCL []string `hcl:",unusedKeys" json:"-"` } +// Validate checks that the KEKProviderConfig is valid. +func (c *KEKProviderConfig) Validate() error { + + if c == nil { + return nil + } + + switch KEKProviderName(c.Provider) { + case KEKProviderAEAD, KEKProviderAWSKMS, KEKProviderAzureKeyVault, + KEKProviderGCPCloudKMS, KEKProviderVaultTransit: + return nil + default: + return fmt.Errorf("unknown keyring provider: %q", c.Provider) + } +} + func (c *KEKProviderConfig) Copy() *KEKProviderConfig { return &KEKProviderConfig{ Provider: c.Provider, diff --git a/nomad/structs/keyring_test.go b/nomad/structs/keyring_test.go index 3b1b2f4c4..691ec6af6 100644 --- a/nomad/structs/keyring_test.go +++ b/nomad/structs/keyring_test.go @@ -10,6 +10,75 @@ import ( "github.com/shoenig/test/must" ) +func TestKEKProviderConfig_Validate(t *testing.T) { + ci.Parallel(t) + + testCases := []struct { + name string + inputKeyringConfig *KEKProviderConfig + expectedErrorContains string + }{ + { + name: "nil", + inputKeyringConfig: nil, + expectedErrorContains: "", + }, + { + name: "aead", + inputKeyringConfig: &KEKProviderConfig{ + Provider: "aead", + }, + expectedErrorContains: "", + }, + { + name: "awskms", + inputKeyringConfig: &KEKProviderConfig{ + Provider: "awskms", + }, + expectedErrorContains: "", + }, + { + name: "azurekeyvault", + inputKeyringConfig: &KEKProviderConfig{ + Provider: "azurekeyvault", + }, + expectedErrorContains: "", + }, + { + name: "gcpckms", + inputKeyringConfig: &KEKProviderConfig{ + Provider: "gcpckms", + }, + expectedErrorContains: "", + }, + { + name: "transit", + inputKeyringConfig: &KEKProviderConfig{ + Provider: "transit", + }, + expectedErrorContains: "", + }, + { + name: "unknown", + inputKeyringConfig: &KEKProviderConfig{ + Provider: "unknown", + }, + expectedErrorContains: "unknown keyring provider", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actualError := tc.inputKeyringConfig.Validate() + if tc.expectedErrorContains == "" { + must.NoError(t, actualError) + } else { + must.ErrorContains(t, actualError, tc.expectedErrorContains) + } + }) + } +} + func TestKeyring_OIDCDiscoveryConfig(t *testing.T) { ci.Parallel(t)