diff --git a/examples/kms/vault/tenant-config.yaml b/examples/kms/vault/tenant-config.yaml new file mode 100644 index 000000000..202c71909 --- /dev/null +++ b/examples/kms/vault/tenant-config.yaml @@ -0,0 +1,12 @@ +--- +# This is an optional (re)configuration of the connection to the Vault +# Service that can be created in a Kubernetes Namespace for a Tenant. +apiVersion: v1 +kind: ConfigMap +metadata: + name: ceph-csi-kms-config +data: + vaultAddress: "http://vault.default.svc.cluster.local:8200" + vaultBackendPath: "secret/" + vaultTLSServerName: "vault.default.svc.cluster.local" + vaultCAVerify: "false" diff --git a/internal/util/vault_tokens.go b/internal/util/vault_tokens.go index 3d41691f1..2ce9d83d8 100644 --- a/internal/util/vault_tokens.go +++ b/internal/util/vault_tokens.go @@ -127,6 +127,11 @@ func InitVaultTokensKMS(tenant, kmsID string, config map[string]interface{}) (En } } } + + err = kms.parseTenantConfig() + if err != nil { + return nil, fmt.Errorf("failed to parse config for tenant: %w", err) + } } // fetch the Vault Token from the Secret (TokenName) in the Kubernetes @@ -339,3 +344,62 @@ func getCertificate(tenant, secretName, key string) (string, error) { return string(cert), nil } + +// isTenantConfigOption return true if a tenant may (re)configure the option in +// their own ConfigMap, false otherwise. +func isTenantConfigOption(opt string) bool { + switch opt { + case "vaultAddress": + case "vaultBackendPath": + case "vaultTLSServerName": + case "vaultCAFromSecret": + case "vaultCAVerify": + default: + return false + } + + return true +} + +// parseTenantConfig gets the optional ConfigMap from the Tenants namespace, +// and applies the allowable options (see isTenantConfigOption) to the KMS +// configuration. +func (kms *VaultTokensKMS) parseTenantConfig() error { + if kms.Tenant == "" || kms.ConfigName == "" { + return nil + } + + // fetch the ConfigMap from the tanants namespace + c := NewK8sClient() + cm, err := c.CoreV1().ConfigMaps(kms.Tenant).Get(context.TODO(), + kms.ConfigName, metav1.GetOptions{}) + if apierrs.IsNotFound(err) { + // the tenant did not (re)configure any options + return nil + } else if err != nil { + return fmt.Errorf("failed to get config (%s) for tenant (%s): %w", + kms.ConfigName, kms.Tenant, err) + } + + // create a new map with config options, but only include the options + // that a tenant may (re)configure + config := make(map[string]interface{}) + for k, v := range cm.Data { + if isTenantConfigOption(k) { + config[k] = v + } // else: silently ignore the option + } + if len(config) == 0 { + // no options configured by the tenant + return nil + } + + // apply the configuration options from the tenant + err = kms.parseConfig(config) + if err != nil { + return fmt.Errorf("failed to parse config (%s) for tenant (%s): %w", + kms.ConfigName, kms.Tenant, err) + } + + return nil +}