From 9317e2afb43a1a1d92ad59687c9828781b4eedf9 Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Thu, 18 Mar 2021 13:50:38 +0100 Subject: [PATCH] util: rewrite GetKMS() to use KMS provider plugin API GetKMS() is the public API that initilizes the KMS providers on demand. Each provider identifies itself with a KMS-Type, and adds its own initialization function to a switch/case construct. This is not well maintainable. The new GetKMS() can be used the same way, but uses the new kmsManager interface to create and configure the KMS provider instances. All existing KMS providers are converted to use the new kmsManager plugins API. Signed-off-by: Niels de Vos --- internal/util/crypto.go | 75 ----------------- internal/util/kms.go | 130 ++++++++++++++++++++++++++--- internal/util/kms_test.go | 2 +- internal/util/secretskms.go | 11 ++- internal/util/secretskms_test.go | 26 ++++-- internal/util/vault.go | 19 +++-- internal/util/vault_test.go | 7 ++ internal/util/vault_tokens.go | 97 +++++++++++++-------- internal/util/vault_tokens_test.go | 58 ++++++++++--- 9 files changed, 278 insertions(+), 147 deletions(-) diff --git a/internal/util/crypto.go b/internal/util/crypto.go index e0634ec71..6b87f60b5 100644 --- a/internal/util/crypto.go +++ b/internal/util/crypto.go @@ -19,11 +19,8 @@ package util import ( "context" "encoding/base64" - "encoding/json" "errors" "fmt" - "io/ioutil" - "os" "path" "strings" @@ -34,22 +31,12 @@ const ( mapperFilePrefix = "luks-rbd-" mapperFilePathPrefix = "/dev/mapper" - kmsTypeKey = "encryptionKMSType" - // kmsConfigPath is the location of the vault config file kmsConfigPath = "/etc/ceph-csi-encryption-kms-config/config.json" // Passphrase size - 20 bytes is 160 bits to satisfy: // https://tools.ietf.org/html/rfc6749#section-10.10 encryptionPassphraseSize = 20 - // podNamespace ENV should be set in the cephcsi container - podNamespace = "POD_NAMESPACE" - - // kmsConfigMapName env to read a ConfigMap by name - kmsConfigMapName = "KMS_CONFIGMAP_NAME" - - // defaultConfigMapToRead default ConfigMap name to fetch kms connection details - defaultConfigMapToRead = "csi-kms-connection-details" ) var ( @@ -183,68 +170,6 @@ func (i integratedDEK) DecryptDEK(volumeID, encyptedDEK string) (string, error) return encyptedDEK, nil } -// GetKMS returns an instance of Key Management System. -// -// - tenant is the owner of the Volume, used to fetch the Vault Token from the -// Kubernetes Namespace where the PVC lives -// - kmsID is the service name of the KMS configuration -// - secrets contain additional details, like TLS certificates to connect to -// the KMS -func GetKMS(tenant, kmsID string, secrets map[string]string) (EncryptionKMS, error) { - if kmsID == "" || kmsID == defaultKMSType { - return initSecretsKMS(secrets) - } - var config map[string]interface{} - // #nosec - content, err := ioutil.ReadFile(kmsConfigPath) - if err != nil { - if !os.IsNotExist(err) { - return nil, fmt.Errorf("failed to read kms configuration from %s: %w", - kmsConfigPath, err) - } - // If the configmap is not mounted to the CSI pods read the configmap - // the kubernetes. - namespace := os.Getenv(podNamespace) - if namespace == "" { - return nil, fmt.Errorf("%q is not set", podNamespace) - } - name := os.Getenv(kmsConfigMapName) - if name == "" { - name = defaultConfigMapToRead - } - config, err = getVaultConfiguration(namespace, name) - if err != nil { - return nil, fmt.Errorf("failed to read kms configuration from configmap %s in namespace %s: %w", - namespace, name, err) - } - } else { - err = json.Unmarshal(content, &config) - if err != nil { - return nil, fmt.Errorf("failed to parse kms configuration: %w", err) - } - } - - kmsConfig, ok := config[kmsID].(map[string]interface{}) - if !ok { - return nil, fmt.Errorf("missing encryption KMS configuration with %s", kmsID) - } - - kmsType, ok := kmsConfig[kmsTypeKey] - if !ok { - return nil, fmt.Errorf("encryption KMS configuration for %s is missing KMS type", kmsID) - } - - switch kmsType { - case kmsTypeSecretsMetadata: - return initSecretsMetadataKMS(kmsID, secrets) - case kmsTypeVault: - return InitVaultKMS(kmsID, kmsConfig, secrets) - case kmsTypeVaultTokens: - return InitVaultTokensKMS(tenant, kmsID, kmsConfig) - } - return nil, fmt.Errorf("unknown encryption KMS type %s", kmsType) -} - // StoreNewCryptoPassphrase generates a new passphrase and saves it in the KMS. func (ve *VolumeEncryption) StoreNewCryptoPassphrase(volumeID string) error { passphrase, err := generateNewEncryptionPassphrase() diff --git a/internal/util/kms.go b/internal/util/kms.go index 76e0b578a..5e227702e 100644 --- a/internal/util/kms.go +++ b/internal/util/kms.go @@ -18,7 +18,10 @@ package util import ( "context" + "encoding/json" "fmt" + "io/ioutil" + "os" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -27,20 +30,122 @@ const ( // kmsProviderKey is the name of the KMS provider that is registered at // the kmsManager. This is used in the ConfigMap configuration options. kmsProviderKey = "KMS_PROVIDER" + // kmsTypeKey is the name of the KMS provider that is registered at + // the kmsManager. This is used in the configfile configuration + // options. + kmsTypeKey = "encryptionKMSType" + + // podNamespace ENV should be set in the cephcsi container + podNamespace = "POD_NAMESPACE" + + // kmsConfigMapName env to read a ConfigMap by name + kmsConfigMapName = "KMS_CONFIGMAP_NAME" + + // defaultConfigMapToRead default ConfigMap name to fetch kms connection details + defaultConfigMapToRead = "csi-kms-connection-details" ) -// getKMSConfig returns the (.Data) contents of the ConfigMap. +// GetKMS returns an instance of Key Management System. // -// FIXME: Ceph-CSI should not talk to Kubernetes directly. -func getKMSConfig(ns, configmap string) (map[string]string, error) { - c := NewK8sClient() - cm, err := c.CoreV1().ConfigMaps(ns).Get(context.Background(), - configmap, metav1.GetOptions{}) +// - tenant is the owner of the Volume, used to fetch the Vault Token from the +// Kubernetes Namespace where the PVC lives +// - kmsID is the service name of the KMS configuration +// - secrets contain additional details, like TLS certificates to connect to +// the KMS +func GetKMS(tenant, kmsID string, secrets map[string]string) (EncryptionKMS, error) { + if kmsID == "" || kmsID == defaultKMSType { + return initSecretsKMS(secrets) + } + + config, err := getKMSConfiguration() if err != nil { return nil, err } - return cm.Data, nil + // config contains a list of KMS connections, indexed by kmsID + section, ok := config[kmsID] + if !ok { + return nil, fmt.Errorf("could not get KMS configuration "+ + "for %q", kmsID) + } + + // kmsConfig can have additional sub-sections + kmsConfig, ok := section.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("failed to convert KMS configuration "+ + "section: %s", kmsID) + } + + return kmsManager.buildKMS(kmsID, tenant, kmsConfig, secrets) +} + +// getKMSConfiguration reads the configuration file from the filesystem, or if +// that fails the ConfigMap directly. The returned map contains all the KMS +// configuration sections, each keyed by its own kmsID. +func getKMSConfiguration() (map[string]interface{}, error) { + var config map[string]interface{} + // #nosec + content, err := ioutil.ReadFile(kmsConfigPath) + if err == nil { + // kmsConfigPath exists and was successfully read + err = json.Unmarshal(content, &config) + if err != nil { + return nil, fmt.Errorf("failed to parse KMS "+ + "configuration: %w", err) + } + } else { + // an error occurred while reading kmsConfigPath + if !os.IsNotExist(err) { + return nil, fmt.Errorf("failed to read KMS "+ + "configuration from %s: %w", kmsConfigPath, + err) + } + + // If the configmap is not mounted to the CSI pods read the + // configmap the kubernetes. + config, err = getKMSConfigMap() + if err != nil { + return nil, err + } + } + + return config, nil +} + +// getKMSConfigMap returns the contents of the ConfigMap. +// +// FIXME: Ceph-CSI should not talk to Kubernetes directly. +func getKMSConfigMap() (map[string]interface{}, error) { + ns := os.Getenv(podNamespace) + if ns == "" { + return nil, fmt.Errorf("%q is not set in the environment", + podNamespace) + } + cmName := os.Getenv(kmsConfigMapName) + if cmName == "" { + cmName = defaultConfigMapToRead + } + + c := NewK8sClient() + cm, err := c.CoreV1().ConfigMaps(ns).Get(context.Background(), + cmName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + + // convert cm.Data from map[string]interface{} + kmsConfig := make(map[string]interface{}) + for kmsID, data := range cm.BinaryData { + section := make(map[string]interface{}) + err = json.Unmarshal(data, §ion) + if err != nil { + return nil, fmt.Errorf("could not convert contents "+ + "of %q to s config section", kmsID) + } + kmsConfig[kmsID] = section + } + + return kmsConfig, nil } // getKMSProvider inspects the configuration and tries to identify what @@ -76,7 +181,7 @@ func getKMSProvider(config map[string]interface{}) (string, error) { // KMSInitializerArgs get passed to KMSInitializerFunc when a new instance of a // KMSProvider is initialized. type KMSInitializerArgs struct { - Id, Tenant string + ID, Tenant string Config map[string]interface{} Secrets map[string]string } @@ -120,7 +225,12 @@ func RegisterKMSProvider(provider KMSProvider) bool { return true } -func (kf *kmsProviderList) buildKMS(providerName, kmsID, tenant string, config map[string]interface{}, secrets map[string]string) (EncryptionKMS, error) { +func (kf *kmsProviderList) buildKMS(kmsID, tenant string, config map[string]interface{}, secrets map[string]string) (EncryptionKMS, error) { + providerName, err := getKMSProvider(config) + if err != nil { + return nil, err + } + provider, ok := kf.providers[providerName] if !ok { return nil, fmt.Errorf("could not find KMS provider %q", @@ -128,7 +238,7 @@ func (kf *kmsProviderList) buildKMS(providerName, kmsID, tenant string, config m } return provider.Initializer(KMSInitializerArgs{ - Id: kmsID, + ID: kmsID, Tenant: tenant, Config: config, Secrets: secrets, diff --git a/internal/util/kms_test.go b/internal/util/kms_test.go index d52748130..c956fd19b 100644 --- a/internal/util/kms_test.go +++ b/internal/util/kms_test.go @@ -22,7 +22,7 @@ import ( "github.com/stretchr/testify/assert" ) -func noinitKMS(id, tenant string, config map[string]interface{}, secrets map[string]string) (EncryptionKMS, error) { +func noinitKMS(args KMSInitializerArgs) (EncryptionKMS, error) { return nil, nil } diff --git a/internal/util/secretskms.go b/internal/util/secretskms.go index ec1544f78..94ac4c375 100644 --- a/internal/util/secretskms.go +++ b/internal/util/secretskms.go @@ -93,11 +93,16 @@ type SecretsMetadataKMS struct { encryptionKMSID string } +var _ = RegisterKMSProvider(KMSProvider{ + UniqueID: kmsTypeSecretsMetadata, + Initializer: initSecretsMetadataKMS, +}) + // initSecretsMetadataKMS initializes a SecretsMetadataKMS that wraps a // SecretsKMS, so that the passphrase from the StorageClass secrets can be used // for encrypting/decrypting DEKs that are stored in a detached DEKStore. -func initSecretsMetadataKMS(encryptionKMSID string, secrets map[string]string) (EncryptionKMS, error) { - eKMS, err := initSecretsKMS(secrets) +func initSecretsMetadataKMS(args KMSInitializerArgs) (EncryptionKMS, error) { + eKMS, err := initSecretsKMS(args.Secrets) if err != nil { return nil, err } @@ -109,7 +114,7 @@ func initSecretsMetadataKMS(encryptionKMSID string, secrets map[string]string) ( smKMS := SecretsMetadataKMS{} smKMS.SecretsKMS = sKMS - smKMS.encryptionKMSID = encryptionKMSID + smKMS.encryptionKMSID = args.ID return smKMS, nil } diff --git a/internal/util/secretskms_test.go b/internal/util/secretskms_test.go index 75ff71b90..9da0c0d7b 100644 --- a/internal/util/secretskms_test.go +++ b/internal/util/secretskms_test.go @@ -41,17 +41,22 @@ func TestGenerateCipher(t *testing.T) { } func TestInitSecretsMetadataKMS(t *testing.T) { - secrets := map[string]string{} + args := KMSInitializerArgs{ + ID: "secrets-metadata-unit-test", + Tenant: "tenant", + Config: nil, + Secrets: map[string]string{}, + } // passphrase it not set, init should fail - kms, err := initSecretsMetadataKMS("secrets-metadata-unit-test", secrets) + kms, err := initSecretsMetadataKMS(args) assert.Error(t, err) assert.Nil(t, kms) // set a passphrase to get a working KMS - secrets[encryptionPassphraseKey] = "my-passphrase-from-kubernetes" + args.Secrets[encryptionPassphraseKey] = "my-passphrase-from-kubernetes" - kms, err = initSecretsMetadataKMS("secrets-metadata-unit-test", secrets) + kms, err = initSecretsMetadataKMS(args) assert.NoError(t, err) require.NotNil(t, kms) assert.Equal(t, "secrets-metadata-unit-test", kms.GetID()) @@ -62,9 +67,15 @@ func TestWorkflowSecretsMetadataKMS(t *testing.T) { secrets := map[string]string{ encryptionPassphraseKey: "my-passphrase-from-kubernetes", } + args := KMSInitializerArgs{ + ID: "secrets-metadata-unit-test", + Tenant: "tenant", + Config: nil, + Secrets: secrets, + } volumeID := "csi-vol-1b00f5f8-b1c1-11e9-8421-9243c1f659f0" - kms, err := initSecretsMetadataKMS("secrets-metadata-unit-test", secrets) + kms, err := initSecretsMetadataKMS(args) assert.NoError(t, err) require.NotNil(t, kms) @@ -90,3 +101,8 @@ func TestWorkflowSecretsMetadataKMS(t *testing.T) { assert.NotEqual(t, "", decryptedDEK) assert.Equal(t, plainDEK, decryptedDEK) } + +func TestSecretsMetadataKMSRegistered(t *testing.T) { + _, ok := kmsManager.providers[kmsTypeSecretsMetadata] + assert.True(t, ok) +} diff --git a/internal/util/vault.go b/internal/util/vault.go index 97766a19e..5977fb07a 100644 --- a/internal/util/vault.go +++ b/internal/util/vault.go @@ -260,21 +260,26 @@ func (vc *vaultConnection) Destroy() { } } +var _ = RegisterKMSProvider(KMSProvider{ + UniqueID: kmsTypeVault, + Initializer: initVaultKMS, +}) + // InitVaultKMS returns an interface to HashiCorp Vault KMS. -func InitVaultKMS(kmsID string, config map[string]interface{}, secrets map[string]string) (EncryptionKMS, error) { +func initVaultKMS(args KMSInitializerArgs) (EncryptionKMS, error) { kms := &VaultKMS{} - err := kms.initConnection(kmsID, config) + err := kms.initConnection(args.ID, args.Config) if err != nil { return nil, fmt.Errorf("failed to initialize Vault connection: %w", err) } - err = kms.initCertificates(config, secrets) + err = kms.initCertificates(args.Config, args.Secrets) if err != nil { return nil, fmt.Errorf("failed to initialize Vault certificates: %w", err) } vaultAuthPath := vaultDefaultAuthPath - err = setConfigString(&vaultAuthPath, config, "vaultAuthPath") + err = setConfigString(&vaultAuthPath, args.Config, "vaultAuthPath") if err != nil { return nil, err } @@ -285,7 +290,7 @@ func InitVaultKMS(kmsID string, config map[string]interface{}, secrets map[strin } vaultRole := vaultDefaultRole - err = setConfigString(&vaultRole, config, "vaultRole") + err = setConfigString(&vaultRole, args.Config, "vaultRole") if err != nil { return nil, err } @@ -293,7 +298,7 @@ func InitVaultKMS(kmsID string, config map[string]interface{}, secrets map[strin // vault.VaultBackendPathKey is "secret/" by default, use vaultPassphraseRoot if configured vaultPassphraseRoot := "" - err = setConfigString(&vaultPassphraseRoot, config, "vaultPassphraseRoot") + err = setConfigString(&vaultPassphraseRoot, args.Config, "vaultPassphraseRoot") if err == nil { // the old example did have "/v1/secret/", convert that format if strings.HasPrefix(vaultPassphraseRoot, "/v1/") { @@ -306,7 +311,7 @@ func InitVaultKMS(kmsID string, config map[string]interface{}, secrets map[strin } kms.vaultPassphrasePath = vaultDefaultPassphrasePath - err = setConfigString(&kms.vaultPassphrasePath, config, "vaultPassphrasePath") + err = setConfigString(&kms.vaultPassphrasePath, args.Config, "vaultPassphrasePath") if err != nil { return nil, err } diff --git a/internal/util/vault_test.go b/internal/util/vault_test.go index ac1e47962..946ecacc9 100644 --- a/internal/util/vault_test.go +++ b/internal/util/vault_test.go @@ -20,6 +20,8 @@ import ( "errors" "os" "testing" + + "github.com/stretchr/testify/assert" ) func TestDetectAuthMountPath(t *testing.T) { @@ -95,3 +97,8 @@ func TestSetConfigString(t *testing.T) { t.Error("optionDefaultOverload should have been updated") } } + +func TestVaultKMSRegistered(t *testing.T) { + _, ok := kmsManager.providers[kmsTypeVault] + assert.True(t, ok) +} diff --git a/internal/util/vault_tokens.go b/internal/util/vault_tokens.go index 36ae5ea2e..f9c93d6a4 100644 --- a/internal/util/vault_tokens.go +++ b/internal/util/vault_tokens.go @@ -96,38 +96,44 @@ func (v *vaultTokenConf) convertStdVaultToCSIConfig(s *standardVault) { } } -// getVaultConfiguration fetches the vault configuration from the kubernetes -// configmap and converts the standard vault configuration (see json tag of -// standardVault structure) to the CSI vault configuration. -func getVaultConfiguration(namespace, name string) (map[string]interface{}, error) { - c := NewK8sClient() - cm, err := c.CoreV1().ConfigMaps(namespace).Get(context.Background(), name, metav1.GetOptions{}) +// convertConfig takes the keys/values in standard Vault environment variable +// format, and converts them to the format that is used in the configuration +// file. +// This uses JSON marshaling and unmarshaling to map the Vault environment +// configuration into bytes, then in the standardVault struct, which is passed +// through convertStdVaultToCSIConfig before converting back to a +// map[string]interface{} configuration. +// +// FIXME: this can surely be simplified?! +func transformConfig(svMap map[string]interface{}) (map[string]interface{}, error) { + // convert the map to JSON + data, err := json.Marshal(svMap) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to convert config %T to JSON: %w", svMap, err) } - config := make(map[string]interface{}) - // convert the standard vault configuration to CSI vault configuration and - // store it in the map that CSI expects - for k, v := range cm.Data { - sv := &standardVault{} - err = json.Unmarshal([]byte(v), sv) - if err != nil { - return nil, fmt.Errorf("failed to Unmarshal the vault configuration for %q: %w", k, err) - } - vc := vaultTokenConf{} - vc.convertStdVaultToCSIConfig(sv) - data, err := json.Marshal(vc) - if err != nil { - return nil, fmt.Errorf("failed to Marshal the CSI vault configuration for %q: %w", k, err) - } - jsonMap := make(map[string]interface{}) - err = json.Unmarshal(data, &jsonMap) - if err != nil { - return nil, fmt.Errorf("failed to Unmarshal the CSI vault configuration for %q: %w", k, err) - } - config[k] = jsonMap + + // convert the JSON back to a standardVault struct + sv := &standardVault{} + err = json.Unmarshal(data, sv) + if err != nil { + return nil, fmt.Errorf("failed to Unmarshal the vault configuration: %w", err) } - return config, nil + + // convert the standardVault struct to a vaultTokenConf struct + vc := vaultTokenConf{} + vc.convertStdVaultToCSIConfig(sv) + data, err = json.Marshal(vc) + if err != nil { + return nil, fmt.Errorf("failed to Marshal the CSI vault configuration: %w", err) + } + + // convert the vaultTokenConf struct to a map[string]interface{} + jsonMap := make(map[string]interface{}) + err = json.Unmarshal(data, &jsonMap) + if err != nil { + return nil, fmt.Errorf("failed to Unmarshal the CSI vault configuration: %w", err) + } + return jsonMap, nil } /* @@ -171,11 +177,29 @@ type VaultTokensKMS struct { TokenName string } +var _ = RegisterKMSProvider(KMSProvider{ + UniqueID: kmsTypeVaultTokens, + Initializer: initVaultTokensKMS, +}) + // InitVaultTokensKMS returns an interface to HashiCorp Vault KMS. -func InitVaultTokensKMS(tenant, kmsID string, config map[string]interface{}) (EncryptionKMS, error) { +// InitVaultTokensKMS returns an interface to HashiCorp Vault KMS. +func initVaultTokensKMS(args KMSInitializerArgs) (EncryptionKMS, error) { + var err error + + config := args.Config + _, ok := config[kmsProviderKey] + if ok { + // configuration comes from the ConfigMap, needs to be + // converted to vaultTokenConf type + config, err = transformConfig(config) + if err != nil { + return nil, fmt.Errorf("failed to convert configuration: %w", err) + } + } + kms := &VaultTokensKMS{} - kms.Tenant = tenant - err := kms.initConnection(kmsID, config) + err = kms.initConnection(args.ID, args.Config) if err != nil { return nil, fmt.Errorf("failed to initialize Vault connection: %w", err) } @@ -190,14 +214,15 @@ func InitVaultTokensKMS(tenant, kmsID string, config map[string]interface{}) (En } // fetch the configuration for the tenant - if tenant != "" { + if args.Tenant != "" { + kms.Tenant = args.Tenant tenantsMap, ok := config["tenants"] if ok { // tenants is a map per tenant, containing key/values tenants, ok := tenantsMap.(map[string]map[string]interface{}) if ok { // get the map for the tenant of the current operation - tenantConfig, ok := tenants[tenant] + tenantConfig, ok := tenants[args.Tenant] if ok { // override connection details from the tenant err = kms.parseConfig(tenantConfig) @@ -216,9 +241,9 @@ func InitVaultTokensKMS(tenant, kmsID string, config map[string]interface{}) (En // fetch the Vault Token from the Secret (TokenName) in the Kubernetes // Namespace (tenant) - kms.vaultConfig[api.EnvVaultToken], err = getToken(tenant, kms.TokenName) + kms.vaultConfig[api.EnvVaultToken], err = getToken(args.Tenant, kms.TokenName) if err != nil { - return nil, fmt.Errorf("failed fetching token from %s/%s: %w", tenant, kms.TokenName, err) + return nil, fmt.Errorf("failed fetching token from %s/%s: %w", args.Tenant, kms.TokenName, err) } err = kms.initCertificates(config) diff --git a/internal/util/vault_tokens_test.go b/internal/util/vault_tokens_test.go index 71c0eefc6..af2d425b3 100644 --- a/internal/util/vault_tokens_test.go +++ b/internal/util/vault_tokens_test.go @@ -21,6 +21,9 @@ import ( "errors" "strings" "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestParseConfig(t *testing.T) { @@ -34,7 +37,7 @@ func TestParseConfig(t *testing.T) { t.Errorf("unexpected error (%T): %s", err, err) } - // fill default options (normally done in InitVaultTokensKMS) + // fill default options (normally done in initVaultTokensKMS) config["vaultAddress"] = "https://vault.default.cluster.svc" config["tenantConfigName"] = vaultTokensDefaultConfigName config["tenantTokenName"] = vaultTokensDefaultTokenName @@ -65,7 +68,7 @@ func TestParseConfig(t *testing.T) { // TestInitVaultTokensKMS verifies that passing partial and complex // configurations get applied correctly. // -// When vault.New() is called at the end of InitVaultTokensKMS(), errors will +// When vault.New() is called at the end of initVaultTokensKMS(), errors will // mention the missing VAULT_TOKEN, and that is expected. func TestInitVaultTokensKMS(t *testing.T) { if true { @@ -74,39 +77,44 @@ func TestInitVaultTokensKMS(t *testing.T) { return } - config := make(map[string]interface{}) + args := KMSInitializerArgs{ + ID: "vault-tokens-config", + Tenant: "bob", + Config: make(map[string]interface{}), + Secrets: nil, + } // empty config map - _, err := InitVaultTokensKMS("bob", "vault-tokens-config", config) + _, err := initVaultTokensKMS(args) if !errors.Is(err, errConfigOptionMissing) { t.Errorf("unexpected error (%T): %s", err, err) } // fill required options - config["vaultAddress"] = "https://vault.default.cluster.svc" + args.Config["vaultAddress"] = "https://vault.default.cluster.svc" // parsing with all required options - _, err = InitVaultTokensKMS("bob", "vault-tokens-config", config) + _, err = initVaultTokensKMS(args) if err != nil && !strings.Contains(err.Error(), "VAULT_TOKEN") { t.Errorf("unexpected error: %s", err) } // fill tenants tenants := make(map[string]interface{}) - config["tenants"] = tenants + args.Config["tenants"] = tenants // empty tenants list - _, err = InitVaultTokensKMS("bob", "vault-tokens-config", config) + _, err = initVaultTokensKMS(args) if err != nil && !strings.Contains(err.Error(), "VAULT_TOKEN") { t.Errorf("unexpected error: %s", err) } // add tenant "bob" bob := make(map[string]interface{}) - config["tenants"].(map[string]interface{})["bob"] = bob bob["vaultAddress"] = "https://vault.bob.example.org" + args.Config["tenants"].(map[string]interface{})["bob"] = bob - _, err = InitVaultTokensKMS("bob", "vault-tokens-config", config) + _, err = initVaultTokensKMS(args) if err != nil && !strings.Contains(err.Error(), "VAULT_TOKEN") { t.Errorf("unexpected error: %s", err) } @@ -158,3 +166,33 @@ func TestStdVaultToCSIConfig(t *testing.T) { t.Errorf("unexpected value for VaultCAVerify: %s", v.VaultCAVerify) } } + +func TestTransformConfig(t *testing.T) { + cm := make(map[string]interface{}) + cm["KMS_PROVIDER"] = "vaulttokens" + cm["VAULT_ADDR"] = "https://vault.example.com" + cm["VAULT_BACKEND_PATH"] = "/secret" + cm["VAULT_CACERT"] = "" + cm["VAULT_TLS_SERVER_NAME"] = "vault.example.com" + cm["VAULT_CLIENT_CERT"] = "" + cm["VAULT_CLIENT_KEY"] = "" + cm["VAULT_NAMESPACE"] = "a-department" + cm["VAULT_SKIP_VERIFY"] = "true" // inverse of "vaultCAVerify" + + config, err := transformConfig(cm) + require.NoError(t, err) + assert.Equal(t, config["encryptionKMSType"], cm["KMS_PROVIDER"]) + assert.Equal(t, config["vaultAddress"], cm["VAULT_ADDR"]) + assert.Equal(t, config["vaultBackendPath"], cm["VAULT_BACKEND_PATH"]) + assert.Equal(t, config["vaultCAFromSecret"], cm["VAULT_CACERT"]) + assert.Equal(t, config["vaultTLSServerName"], cm["VAULT_TLS_SERVER_NAME"]) + assert.Equal(t, config["vaultClientCertFromSecret"], cm["VAULT_CLIENT_CERT"]) + assert.Equal(t, config["vaultClientCertKeyFromSecret"], cm["VAULT_CLIENT_KEY"]) + assert.Equal(t, config["vaultNamespace"], cm["VAULT_NAMESPACE"]) + assert.Equal(t, config["vaultCAVerify"], "false") +} + +func TestVaultTokensKMSRegistered(t *testing.T) { + _, ok := kmsManager.providers[kmsTypeVaultTokens] + assert.True(t, ok) +}