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 <madhupr007@gmail.com>
This commit is contained in:
Madhu Rajanna 2020-12-15 15:37:28 +05:30 committed by mergify[bot]
parent e4b16a5c72
commit b3fbcb9c95
4 changed files with 110 additions and 5 deletions

View File

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

View File

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

View File

@ -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",
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{})

View File

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