diff --git a/deploy/rbd/kubernetes/csi-rbdplugin-provisioner.yaml b/deploy/rbd/kubernetes/csi-rbdplugin-provisioner.yaml index 09c288e45..4e819acad 100644 --- a/deploy/rbd/kubernetes/csi-rbdplugin-provisioner.yaml +++ b/deploy/rbd/kubernetes/csi-rbdplugin-provisioner.yaml @@ -133,6 +133,12 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # - name: POD_NAMESPACE + # valueFrom: + # fieldRef: + # fieldPath: spec.namespace + # - name: KMS_CONFIGMAP_NAME + # value: encryptionConfig - name: CSI_ENDPOINT value: unix:///csi/csi-provisioner.sock imagePullPolicy: "IfNotPresent" diff --git a/deploy/rbd/kubernetes/csi-rbdplugin.yaml b/deploy/rbd/kubernetes/csi-rbdplugin.yaml index 4be1e6721..a16beca50 100644 --- a/deploy/rbd/kubernetes/csi-rbdplugin.yaml +++ b/deploy/rbd/kubernetes/csi-rbdplugin.yaml @@ -69,6 +69,12 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + # - name: POD_NAMESPACE + # valueFrom: + # fieldRef: + # fieldPath: spec.namespace + # - name: KMS_CONFIGMAP_NAME + # value: encryptionConfig - name: CSI_ENDPOINT value: unix:///csi/csi.sock imagePullPolicy: "IfNotPresent" diff --git a/internal/util/crypto.go b/internal/util/crypto.go index 13fe216a7..787e79f78 100644 --- a/internal/util/crypto.go +++ b/internal/util/crypto.go @@ -23,6 +23,7 @@ import ( "errors" "fmt" "io/ioutil" + "os" "path" "strings" @@ -46,6 +47,14 @@ const ( // 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" ) // EncryptionKMS provides external Key Management System for encryption @@ -113,18 +122,34 @@ func GetKMS(tenant, kmsID string, secrets map[string]string) (EncryptionKMS, err if kmsID == "" || kmsID == defaultKMSType { return initSecretsKMS(secrets) } - + var config map[string]interface{} // #nosec content, err := ioutil.ReadFile(kmsConfigPath) if err != nil { - return nil, fmt.Errorf("failed to read kms configuration from %s: %s", - kmsConfigPath, err) + 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) + } } - var config map[string]interface{} err = json.Unmarshal(content, &config) if err != nil { - return nil, fmt.Errorf("failed to parse kms configuration: %s", err) + return nil, fmt.Errorf("failed to parse kms configuration: %w", err) } kmsConfig, ok := config[kmsID].(map[string]interface{}) diff --git a/internal/util/vault_tokens.go b/internal/util/vault_tokens.go index 2ce9d83d8..e01130b19 100644 --- a/internal/util/vault_tokens.go +++ b/internal/util/vault_tokens.go @@ -18,6 +18,7 @@ package util import ( "context" + "encoding/json" "errors" "fmt" "os" @@ -52,6 +53,73 @@ const ( vaultTokenSecretKey = "token" ) +type standardVault struct { + KmsPROVIDER string `json:"KMS_PROVIDER"` + VaultADDR string `json:"VAULT_ADDR"` + VaultBackendPath string `json:"VAULT_BACKEND_PATH"` + VaultCACert string `json:"VAULT_CACERT"` + VaultTLSServerName string `json:"VAULT_TLS_SERVER_NAME"` + VaultClientCert string `json:"VAULT_CLIENT_CERT"` + VaultClientKey string `json:"VAULT_CLIENT_KEY"` + VaultNamespace string `json:"VAULT_NAMESPACE"` +} + +type vaultTokenConf struct { + EncryptionKMSType string `json:"encryptionKMSType"` + VaultAddress string `json:"vaultAddress"` + VaultBackendPath string `json:"vaultBackendPath"` + VaultCAFromSecret string `json:"vaultCAFromSecret"` + VaultTLSServerName string `json:"vaultTLSServerName"` + VaultClientCertFromSecret string `json:"vaultClientCertFromSecret"` + VaultClientCertKeyFromSecret string `json:"vaultClientCertKeyFromSecret"` + VaultNamespace string `json:"vaultNamespace"` +} + +func (v *vaultTokenConf) convertStdVaultToCSIConfig(s *standardVault) { + v.EncryptionKMSType = s.KmsPROVIDER + v.VaultAddress = s.VaultADDR + v.VaultBackendPath = s.VaultBackendPath + v.VaultCAFromSecret = s.VaultCACert + v.VaultClientCertFromSecret = s.VaultClientCert + v.VaultClientCertKeyFromSecret = s.VaultClientKey + v.VaultNamespace = s.VaultNamespace + v.VaultTLSServerName = s.VaultTLSServerName +} + +// 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{}) + if err != nil { + return nil, 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 + } + return config, nil +} + /* VaultTokens represents a Hashicorp Vault KMS configuration that provides a Token per tenant.