From 347e1cdb8a4d77c5676e98a3532700bb18949178 Mon Sep 17 00:00:00 2001 From: Tom Proctor Date: Wed, 2 Aug 2023 15:35:46 +0100 Subject: [PATCH] Support for setting Vault CA from VAULT_CACERT_BYTES env (#1782) --- config/consul_test.go | 15 ++-- config/nomad_test.go | 75 +++++++++--------- config/ssl.go | 27 +++++-- config/ssl_test.go | 145 ++++++++++++++++++++++------------ config/vault.go | 3 + config/vault_test.go | 166 +++++++++++++++++++++++++++++---------- dependency/client_set.go | 8 +- manager/runner.go | 5 ++ 8 files changed, 302 insertions(+), 142 deletions(-) diff --git a/config/consul_test.go b/config/consul_test.go index f95d435ea..97c5a0031 100644 --- a/config/consul_test.go +++ b/config/consul_test.go @@ -309,13 +309,14 @@ func TestConsulConfig_Finalize(t *testing.T) { Attempts: Int(DefaultRetryAttempts), }, SSL: &SSLConfig{ - CaCert: String(""), - CaPath: String(""), - Cert: String(""), - Enabled: Bool(false), - Key: String(""), - ServerName: String(""), - Verify: Bool(true), + CaCert: String(""), + CaCertBytes: String(""), + CaPath: String(""), + Cert: String(""), + Enabled: Bool(false), + Key: String(""), + ServerName: String(""), + Verify: Bool(true), }, Token: String(""), TokenFile: String(""), diff --git a/config/nomad_test.go b/config/nomad_test.go index cc3f4578c..4413150d8 100644 --- a/config/nomad_test.go +++ b/config/nomad_test.go @@ -228,13 +228,14 @@ func TestNomadConfig_Finalize(t *testing.T) { Enabled: Bool(false), Namespace: String(""), SSL: &SSLConfig{ - CaCert: String(""), - CaPath: String(""), - Cert: String(""), - Enabled: Bool(false), - Key: String(""), - ServerName: String(""), - Verify: Bool(true), + CaCert: String(""), + CaCertBytes: String(""), + CaPath: String(""), + Cert: String(""), + Enabled: Bool(false), + Key: String(""), + ServerName: String(""), + Verify: Bool(true), }, Token: String(""), AuthUsername: String(""), @@ -266,13 +267,14 @@ func TestNomadConfig_Finalize(t *testing.T) { Enabled: Bool(true), Namespace: String(""), SSL: &SSLConfig{ - CaCert: String(""), - CaPath: String(""), - Cert: String(""), - Enabled: Bool(false), - Key: String(""), - ServerName: String(""), - Verify: Bool(true), + CaCert: String(""), + CaCertBytes: String(""), + CaPath: String(""), + Cert: String(""), + Enabled: Bool(false), + Key: String(""), + ServerName: String(""), + Verify: Bool(true), }, Token: String(""), AuthUsername: String(""), @@ -311,13 +313,14 @@ func TestNomadConfig_Finalize(t *testing.T) { Enabled: Bool(true), Namespace: String(""), SSL: &SSLConfig{ - CaCert: String("ca.crt"), - CaPath: String(""), - Cert: String("foo.crt"), - Enabled: Bool(true), - Key: String("foo.key"), - ServerName: String("server.global.nomad"), - Verify: Bool(true), + CaCert: String("ca.crt"), + CaCertBytes: String(""), + CaPath: String(""), + Cert: String("foo.crt"), + Enabled: Bool(true), + Key: String("foo.key"), + ServerName: String("server.global.nomad"), + Verify: Bool(true), }, Token: String(""), AuthUsername: String(""), @@ -351,13 +354,14 @@ func TestNomadConfig_Finalize(t *testing.T) { Enabled: Bool(true), Namespace: String(""), SSL: &SSLConfig{ - CaCert: String(""), - CaPath: String(""), - Cert: String(""), - Enabled: Bool(false), - Key: String(""), - ServerName: String(""), - Verify: Bool(true), + CaCert: String(""), + CaCertBytes: String(""), + CaPath: String(""), + Cert: String(""), + Enabled: Bool(false), + Key: String(""), + ServerName: String(""), + Verify: Bool(true), }, Token: String(""), AuthUsername: String(""), @@ -395,13 +399,14 @@ func TestNomadConfig_Finalize(t *testing.T) { Enabled: Bool(false), Namespace: String(""), SSL: &SSLConfig{ - CaCert: String(""), - CaPath: String(""), - Cert: String(""), - Enabled: Bool(false), - Key: String(""), - ServerName: String(""), - Verify: Bool(true), + CaCert: String(""), + CaCertBytes: String(""), + CaPath: String(""), + Cert: String(""), + Enabled: Bool(false), + Key: String(""), + ServerName: String(""), + Verify: Bool(true), }, Token: String(""), AuthUsername: String(""), diff --git a/config/ssl.go b/config/ssl.go index dd49781fa..86fb5eb00 100644 --- a/config/ssl.go +++ b/config/ssl.go @@ -12,13 +12,14 @@ const ( // SSLConfig is the configuration for SSL. type SSLConfig struct { - CaCert *string `mapstructure:"ca_cert"` - CaPath *string `mapstructure:"ca_path"` - Cert *string `mapstructure:"cert"` - Enabled *bool `mapstructure:"enabled"` - Key *string `mapstructure:"key"` - ServerName *string `mapstructure:"server_name"` - Verify *bool `mapstructure:"verify"` + CaCert *string `mapstructure:"ca_cert"` + CaCertBytes *string `mapstructure:"ca_cert_bytes"` + CaPath *string `mapstructure:"ca_path"` + Cert *string `mapstructure:"cert"` + Enabled *bool `mapstructure:"enabled"` + Key *string `mapstructure:"key"` + ServerName *string `mapstructure:"server_name"` + Verify *bool `mapstructure:"verify"` } // DefaultSSLConfig returns a configuration that is populated with the @@ -35,6 +36,7 @@ func (c *SSLConfig) Copy() *SSLConfig { var o SSLConfig o.CaCert = c.CaCert + o.CaCertBytes = c.CaCertBytes o.CaPath = c.CaPath o.Cert = c.Cert o.Enabled = c.Enabled @@ -70,6 +72,10 @@ func (c *SSLConfig) Merge(o *SSLConfig) *SSLConfig { r.CaCert = o.CaCert } + if o.CaCertBytes != nil { + r.CaCertBytes = o.CaCertBytes + } + if o.CaPath != nil { r.CaPath = o.CaPath } @@ -99,6 +105,7 @@ func (c *SSLConfig) Finalize() { c.Enabled = Bool(false || StringPresent(c.Cert) || StringPresent(c.CaCert) || + StringPresent(c.CaCertBytes) || StringPresent(c.CaPath) || StringPresent(c.Key) || StringPresent(c.ServerName) || @@ -113,6 +120,10 @@ func (c *SSLConfig) Finalize() { c.CaCert = String("") } + if c.CaCertBytes == nil { + c.CaCertBytes = String("") + } + if c.CaPath == nil { c.CaPath = String("") } @@ -138,6 +149,7 @@ func (c *SSLConfig) GoString() string { return fmt.Sprintf("&SSLConfig{"+ "CaCert:%s, "+ + "CaCertBytes:%s, "+ "CaPath:%s, "+ "Cert:%s, "+ "Enabled:%s, "+ @@ -146,6 +158,7 @@ func (c *SSLConfig) GoString() string { "Verify:%s"+ "}", StringGoString(c.CaCert), + StringGoString(c.CaCertBytes), StringGoString(c.CaPath), StringGoString(c.Cert), BoolGoString(c.Enabled), diff --git a/config/ssl_test.go b/config/ssl_test.go index d436b047d..8fbec91df 100644 --- a/config/ssl_test.go +++ b/config/ssl_test.go @@ -25,13 +25,14 @@ func TestSSLConfig_Copy(t *testing.T) { { "same_enabled", &SSLConfig{ - Enabled: Bool(true), - Verify: Bool(true), - CaCert: String("ca_cert"), - CaPath: String("ca_path"), - Cert: String("cert"), - Key: String("key"), - ServerName: String("server_name"), + Enabled: Bool(true), + Verify: Bool(true), + CaCert: String("ca_cert"), + CaCertBytes: String("ca_cert_bytes"), + CaPath: String("ca_path"), + Cert: String("cert"), + Key: String("key"), + ServerName: String("server_name"), }, }, } @@ -197,6 +198,30 @@ func TestSSLConfig_Merge(t *testing.T) { &SSLConfig{CaCert: String("ca_cert")}, &SSLConfig{CaCert: String("ca_cert")}, }, + { + "ca_cert_bytes_overrides", + &SSLConfig{CaCertBytes: String("ca_cert_bytes")}, + &SSLConfig{CaCertBytes: String("")}, + &SSLConfig{CaCertBytes: String("")}, + }, + { + "ca_cert_bytes_empty_one", + &SSLConfig{CaCertBytes: String("ca_cert_bytes")}, + &SSLConfig{}, + &SSLConfig{CaCertBytes: String("ca_cert_bytes")}, + }, + { + "ca_cert_bytes_empty_two", + &SSLConfig{}, + &SSLConfig{CaCertBytes: String("ca_cert_bytes")}, + &SSLConfig{CaCertBytes: String("ca_cert_bytes")}, + }, + { + "ca_cert_bytes_same", + &SSLConfig{CaCertBytes: String("ca_cert_bytes")}, + &SSLConfig{CaCertBytes: String("ca_cert_bytes")}, + &SSLConfig{CaCertBytes: String("ca_cert_bytes")}, + }, { "ca_path_overrides", &SSLConfig{CaPath: String("ca_path")}, @@ -267,13 +292,14 @@ func TestSSLConfig_Finalize(t *testing.T) { "empty", &SSLConfig{}, &SSLConfig{ - Enabled: Bool(false), - Cert: String(""), - CaCert: String(""), - CaPath: String(""), - Key: String(""), - ServerName: String(""), - Verify: Bool(true), + Enabled: Bool(false), + Cert: String(""), + CaCert: String(""), + CaCertBytes: String(""), + CaPath: String(""), + Key: String(""), + ServerName: String(""), + Verify: Bool(true), }, }, { @@ -282,13 +308,14 @@ func TestSSLConfig_Finalize(t *testing.T) { Cert: String("cert"), }, &SSLConfig{ - Enabled: Bool(true), - Cert: String("cert"), - CaCert: String(""), - CaPath: String(""), - Key: String(""), - ServerName: String(""), - Verify: Bool(true), + Enabled: Bool(true), + Cert: String("cert"), + CaCert: String(""), + CaCertBytes: String(""), + CaPath: String(""), + Key: String(""), + ServerName: String(""), + Verify: Bool(true), }, }, { @@ -297,13 +324,30 @@ func TestSSLConfig_Finalize(t *testing.T) { CaCert: String("ca_cert"), }, &SSLConfig{ - Enabled: Bool(true), - Cert: String(""), - CaCert: String("ca_cert"), - CaPath: String(""), - Key: String(""), - ServerName: String(""), - Verify: Bool(true), + Enabled: Bool(true), + Cert: String(""), + CaCert: String("ca_cert"), + CaCertBytes: String(""), + CaPath: String(""), + Key: String(""), + ServerName: String(""), + Verify: Bool(true), + }, + }, + { + "with_ca_cert_bytes", + &SSLConfig{ + CaCertBytes: String("ca_cert_bytes"), + }, + &SSLConfig{ + Enabled: Bool(true), + Cert: String(""), + CaCert: String(""), + CaCertBytes: String("ca_cert_bytes"), + CaPath: String(""), + Key: String(""), + ServerName: String(""), + Verify: Bool(true), }, }, { @@ -312,13 +356,14 @@ func TestSSLConfig_Finalize(t *testing.T) { CaPath: String("ca_path"), }, &SSLConfig{ - Enabled: Bool(true), - Cert: String(""), - CaCert: String(""), - CaPath: String("ca_path"), - Key: String(""), - ServerName: String(""), - Verify: Bool(true), + Enabled: Bool(true), + Cert: String(""), + CaCert: String(""), + CaCertBytes: String(""), + CaPath: String("ca_path"), + Key: String(""), + ServerName: String(""), + Verify: Bool(true), }, }, { @@ -327,13 +372,14 @@ func TestSSLConfig_Finalize(t *testing.T) { Key: String("key"), }, &SSLConfig{ - Enabled: Bool(true), - Cert: String(""), - CaCert: String(""), - CaPath: String(""), - Key: String("key"), - ServerName: String(""), - Verify: Bool(true), + Enabled: Bool(true), + Cert: String(""), + CaCert: String(""), + CaCertBytes: String(""), + CaPath: String(""), + Key: String("key"), + ServerName: String(""), + Verify: Bool(true), }, }, { @@ -342,13 +388,14 @@ func TestSSLConfig_Finalize(t *testing.T) { ServerName: String("server_name"), }, &SSLConfig{ - Enabled: Bool(true), - Cert: String(""), - CaCert: String(""), - CaPath: String(""), - Key: String(""), - ServerName: String("server_name"), - Verify: Bool(true), + Enabled: Bool(true), + Cert: String(""), + CaCert: String(""), + CaCertBytes: String(""), + CaPath: String(""), + Key: String(""), + ServerName: String("server_name"), + Verify: Bool(true), }, }, } diff --git a/config/vault.go b/config/vault.go index ceeb9dc39..2808acd46 100644 --- a/config/vault.go +++ b/config/vault.go @@ -303,6 +303,9 @@ func (c *VaultConfig) Finalize() { if c.SSL.CaCert == nil { c.SSL.CaCert = stringFromEnv([]string{api.EnvVaultCACert}, "") } + if c.SSL.CaCertBytes == nil { + c.SSL.CaCertBytes = stringFromEnv([]string{api.EnvVaultCACertBytes}, "") + } if c.SSL.CaPath == nil { c.SSL.CaPath = stringFromEnv([]string{api.EnvVaultCAPath}, "") } diff --git a/config/vault_test.go b/config/vault_test.go index a2b7cff18..bdc2830ce 100644 --- a/config/vault_test.go +++ b/config/vault_test.go @@ -8,6 +8,8 @@ import ( "reflect" "testing" "time" + + "github.com/hashicorp/vault/api" ) func TestVaultConfig_Copy(t *testing.T) { @@ -471,11 +473,13 @@ func TestVaultConfig_Merge(t *testing.T) { func TestVaultConfig_Finalize(t *testing.T) { cases := []struct { name string + env map[string]string i *VaultConfig r *VaultConfig }{ { "empty", + nil, &VaultConfig{}, &VaultConfig{ Address: String(""), @@ -489,13 +493,14 @@ func TestVaultConfig_Finalize(t *testing.T) { Attempts: Int(DefaultRetryAttempts), }, SSL: &SSLConfig{ - CaCert: String(""), - CaPath: String(""), - Cert: String(""), - Enabled: Bool(true), - Key: String(""), - ServerName: String(""), - Verify: Bool(true), + CaCert: String(""), + CaCertBytes: String(""), + CaPath: String(""), + Cert: String(""), + Enabled: Bool(true), + Key: String(""), + ServerName: String(""), + Verify: Bool(true), }, Token: String(""), Transport: &TransportConfig{ @@ -518,6 +523,7 @@ func TestVaultConfig_Finalize(t *testing.T) { }, { "with_address", + nil, &VaultConfig{ Address: String("address"), }, @@ -533,13 +539,14 @@ func TestVaultConfig_Finalize(t *testing.T) { Attempts: Int(DefaultRetryAttempts), }, SSL: &SSLConfig{ - CaCert: String(""), - CaPath: String(""), - Cert: String(""), - Enabled: Bool(true), - Key: String(""), - ServerName: String(""), - Verify: Bool(true), + CaCert: String(""), + CaCertBytes: String(""), + CaPath: String(""), + Cert: String(""), + Enabled: Bool(true), + Key: String(""), + ServerName: String(""), + Verify: Bool(true), }, Token: String(""), Transport: &TransportConfig{ @@ -562,8 +569,19 @@ func TestVaultConfig_Finalize(t *testing.T) { }, { "with_ssl_config", + nil, &VaultConfig{ Address: String("address"), + SSL: &SSLConfig{ + CaCert: String("ca_cert"), + CaCertBytes: String("ca_cert_bytes"), + CaPath: String("ca_path"), + Cert: String("cert"), + Enabled: Bool(false), + Key: String("key"), + ServerName: String("server_name"), + Verify: Bool(false), + }, }, &VaultConfig{ Address: String("address"), @@ -577,13 +595,68 @@ func TestVaultConfig_Finalize(t *testing.T) { Attempts: Int(DefaultRetryAttempts), }, SSL: &SSLConfig{ - CaCert: String(""), - CaPath: String(""), - Cert: String(""), + CaCert: String("ca_cert"), + CaCertBytes: String("ca_cert_bytes"), + CaPath: String("ca_path"), + Cert: String("cert"), + Enabled: Bool(false), + Key: String("key"), + ServerName: String("server_name"), + Verify: Bool(false), + }, + Token: String(""), + Transport: &TransportConfig{ + DialKeepAlive: TimeDuration(DefaultDialKeepAlive), + DialTimeout: TimeDuration(DefaultDialTimeout), + DisableKeepAlives: Bool(false), + IdleConnTimeout: TimeDuration(DefaultIdleConnTimeout), + MaxIdleConns: Int(DefaultMaxIdleConns), + MaxIdleConnsPerHost: Int(DefaultMaxIdleConnsPerHost), + TLSHandshakeTimeout: TimeDuration(DefaultTLSHandshakeTimeout), + }, + UnwrapToken: Bool(DefaultVaultUnwrapToken), + DefaultLeaseDuration: TimeDuration(DefaultVaultLeaseDuration), + LeaseRenewalThreshold: Float64(DefaultLeaseRenewalThreshold), + K8SAuthRoleName: String(""), + K8SServiceAccountTokenPath: String(DefaultK8SServiceAccountTokenPath), + K8SServiceAccountToken: String(""), + K8SServiceMountPath: String(DefaultK8SServiceMountPath), + }, + }, + { + "with_ssl_config_env", + map[string]string{ + api.EnvVaultCACert: "ca_cert", + api.EnvVaultCACertBytes: "ca_cert_bytes", + api.EnvVaultCAPath: "ca_path", + api.EnvVaultClientCert: "cert", + api.EnvVaultClientKey: "key", + api.EnvVaultTLSServerName: "server_name", + api.EnvVaultSkipVerify: "true", + }, + &VaultConfig{ + Address: String("address"), + }, + &VaultConfig{ + Address: String("address"), + Enabled: Bool(true), + Namespace: String(""), + RenewToken: Bool(false), + Retry: &RetryConfig{ + Backoff: TimeDuration(DefaultRetryBackoff), + MaxBackoff: TimeDuration(DefaultRetryMaxBackoff), Enabled: Bool(true), - Key: String(""), - ServerName: String(""), - Verify: Bool(true), + Attempts: Int(DefaultRetryAttempts), + }, + SSL: &SSLConfig{ + CaCert: String("ca_cert"), + CaCertBytes: String("ca_cert_bytes"), + CaPath: String("ca_path"), + Cert: String("cert"), + Enabled: Bool(true), + Key: String("key"), + ServerName: String("server_name"), + Verify: Bool(false), }, Token: String(""), Transport: &TransportConfig{ @@ -606,6 +679,7 @@ func TestVaultConfig_Finalize(t *testing.T) { }, { "with_default_lease_duration", + nil, &VaultConfig{ Address: String("address"), DefaultLeaseDuration: TimeDuration(1 * time.Minute), @@ -622,13 +696,14 @@ func TestVaultConfig_Finalize(t *testing.T) { Attempts: Int(DefaultRetryAttempts), }, SSL: &SSLConfig{ - CaCert: String(""), - CaPath: String(""), - Cert: String(""), - Enabled: Bool(true), - Key: String(""), - ServerName: String(""), - Verify: Bool(true), + CaCert: String(""), + CaCertBytes: String(""), + CaPath: String(""), + Cert: String(""), + Enabled: Bool(true), + Key: String(""), + ServerName: String(""), + Verify: Bool(true), }, Token: String(""), Transport: &TransportConfig{ @@ -651,6 +726,7 @@ func TestVaultConfig_Finalize(t *testing.T) { }, { "with_lease_renewal_threshold", + nil, &VaultConfig{ Address: String("address"), LeaseRenewalThreshold: Float64(0.70), @@ -667,13 +743,14 @@ func TestVaultConfig_Finalize(t *testing.T) { Attempts: Int(DefaultRetryAttempts), }, SSL: &SSLConfig{ - CaCert: String(""), - CaPath: String(""), - Cert: String(""), - Enabled: Bool(true), - Key: String(""), - ServerName: String(""), - Verify: Bool(true), + CaCert: String(""), + CaCertBytes: String(""), + CaPath: String(""), + Cert: String(""), + Enabled: Bool(true), + Key: String(""), + ServerName: String(""), + Verify: Bool(true), }, Token: String(""), Transport: &TransportConfig{ @@ -696,6 +773,7 @@ func TestVaultConfig_Finalize(t *testing.T) { }, { "with_k8s_settings", + nil, &VaultConfig{ K8SAuthRoleName: String("K8SAuthRoleName"), K8SServiceAccountTokenPath: String("K8SServiceAccountTokenPath"), @@ -714,13 +792,14 @@ func TestVaultConfig_Finalize(t *testing.T) { Attempts: Int(DefaultRetryAttempts), }, SSL: &SSLConfig{ - CaCert: String(""), - CaPath: String(""), - Cert: String(""), - Enabled: Bool(true), - Key: String(""), - ServerName: String(""), - Verify: Bool(true), + CaCert: String(""), + CaCertBytes: String(""), + CaPath: String(""), + Cert: String(""), + Enabled: Bool(true), + Key: String(""), + ServerName: String(""), + Verify: Bool(true), }, Token: String(""), Transport: &TransportConfig{ @@ -745,6 +824,11 @@ func TestVaultConfig_Finalize(t *testing.T) { for i, tc := range cases { t.Run(fmt.Sprintf("%d_%s", i, tc.name), func(t *testing.T) { + if tc.env != nil { + for k, v := range tc.env { + t.Setenv(k, v) + } + } tc.i.Finalize() if !reflect.DeepEqual(tc.r, tc.i) { t.Errorf("\nexp: %#v\nact: %#v", tc.r, tc.i) diff --git a/dependency/client_set.go b/dependency/client_set.go index 909ba03f8..b19cccd99 100644 --- a/dependency/client_set.go +++ b/dependency/client_set.go @@ -95,6 +95,7 @@ type CreateVaultClientInput struct { SSLCert string SSLKey string SSLCACert string + SSLCACertBytes string SSLCAPath string ServerName string ClientUserAgent string @@ -302,10 +303,11 @@ func (c *ClientSet) CreateVaultClient(i *CreateVaultClientInput) error { } // Custom CA certificate - if i.SSLCACert != "" || i.SSLCAPath != "" { + if i.SSLCACert != "" || i.SSLCAPath != "" || i.SSLCACertBytes != "" { rootConfig := &rootcerts.Config{ - CAFile: i.SSLCACert, - CAPath: i.SSLCAPath, + CAFile: i.SSLCACert, + CACertificate: []byte(i.SSLCACertBytes), + CAPath: i.SSLCAPath, } if err := rootcerts.ConfigureTLS(&tlsConfig, rootConfig); err != nil { return fmt.Errorf("client set: vault configuring TLS failed: %s", err) diff --git a/manager/runner.go b/manager/runner.go index b3fdd14dd..9f330b349 100644 --- a/manager/runner.go +++ b/manager/runner.go @@ -1128,6 +1128,10 @@ func (r *Runner) childEnv() []string { m["VAULT_CACERT"] = config.StringVal(r.config.Vault.SSL.CaCert) } + if config.StringPresent(r.config.Vault.SSL.CaCertBytes) { + m["VAULT_CACERT_BYTES"] = config.StringVal(r.config.Vault.SSL.CaCertBytes) + } + if config.StringPresent(r.config.Vault.SSL.ServerName) { m["VAULT_TLS_SERVER_NAME"] = config.StringVal(r.config.Vault.SSL.ServerName) } @@ -1350,6 +1354,7 @@ func NewClientSet(c *config.Config) (*dep.ClientSet, error) { SSLCert: config.StringVal(c.Vault.SSL.Cert), SSLKey: config.StringVal(c.Vault.SSL.Key), SSLCACert: config.StringVal(c.Vault.SSL.CaCert), + SSLCACertBytes: config.StringVal(c.Vault.SSL.CaCertBytes), SSLCAPath: config.StringVal(c.Vault.SSL.CaPath), ServerName: config.StringVal(c.Vault.SSL.ServerName), ClientUserAgent: config.StringVal(c.Vault.ClientUserAgent),