From b3fbcb9c95e8499d2f2e5b7ec40876f66c0f825e Mon Sep 17 00:00:00 2001 From: Madhu Rajanna Date: Tue, 15 Dec 2020 15:37:28 +0530 Subject: [PATCH] rbd: read configuration from the configmap if the kms encryption configmap is not mounted as a volume to the CSI pods, add the code to read the configuration from the kubernetes. Later the code to fetch the configmap will be moved to the new sidecar which is will talk to respective CO to fetch the encryption configurations. The k8s configmap uses the standard vault spefic names to add the configurations. this will be converted back to the CSI configurations. Signed-off-by: Madhu Rajanna --- .../kubernetes/csi-rbdplugin-provisioner.yaml | 6 ++ deploy/rbd/kubernetes/csi-rbdplugin.yaml | 6 ++ internal/util/crypto.go | 35 ++++++++-- internal/util/vault_tokens.go | 68 +++++++++++++++++++ 4 files changed, 110 insertions(+), 5 deletions(-) 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.