util: allow tenants to (re)configure VaultTokens settings

A tenant can place a ConfigMap in their Kubernetes Namespace with
configuration options that differ from the global (by the Storage Admin
set) values.

The ConfigMap needs to be located in the Tenants namespace, as described
in the documentation

See-also: docs/design/proposals/encryption-with-vault-tokens.md
Signed-off-by: Niels de Vos <ndevos@redhat.com>
This commit is contained in:
Niels de Vos 2020-12-15 09:11:53 +01:00 committed by mergify[bot]
parent 81061e9f68
commit e4b16a5c72
2 changed files with 76 additions and 0 deletions

View File

@ -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"

View File

@ -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
}