mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
Reduce encryption KMS configuration SC parameters
* moves KMS type from StorageClass into KMS configuration itself * updates omapval used to identify KMS to only it's ID without the type why? 1. when using multiple KMS configurations (not currently supported) automated parsing of kms configuration will be failing because some entries in configs won't comply with the requested type 2. less options are needed in the StorageClass and less data used to identify the KMS Signed-off-by: Vasyl Purchel vasyl.purchel@workday.com Signed-off-by: Andrea Baglioni andrea.baglioni@workday.com
This commit is contained in:
committed by
mergify[bot]
parent
1695c6965d
commit
669dc4536f
@ -19,7 +19,9 @@ package util
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
@ -39,6 +41,10 @@ const (
|
||||
|
||||
// Encryption passphrase location in K8s secrets
|
||||
encryptionPassphraseKey = "encryptionPassphrase"
|
||||
kmsTypeKey = "encryptionKMSType"
|
||||
|
||||
// Default KMS type
|
||||
defaultKMSType = "default"
|
||||
|
||||
// kmsConfigPath is the location of the vault config file
|
||||
kmsConfigPath = "/etc/ceph-csi-encryption-kms-config/config.json"
|
||||
@ -54,7 +60,7 @@ type EncryptionKMS interface {
|
||||
GetPassphrase(key string) (string, error)
|
||||
SavePassphrase(key, value string) error
|
||||
DeletePassphrase(key string) error
|
||||
KmsConfig() string
|
||||
GetID() string
|
||||
}
|
||||
|
||||
// MissingPassphrase is an error instructing to generate new passphrase
|
||||
@ -75,11 +81,6 @@ func initSecretsKMS(secrets map[string]string) (EncryptionKMS, error) {
|
||||
return SecretsKMS{passphrase: passphraseValue}, nil
|
||||
}
|
||||
|
||||
// KmsConfig returns KMS configuration: "<kms-type>|<kms-id>"
|
||||
func (kms SecretsKMS) KmsConfig() string {
|
||||
return "secrets|kubernetes"
|
||||
}
|
||||
|
||||
// GetPassphrase returns passphrase from Kubernetes secrets
|
||||
func (kms SecretsKMS) GetPassphrase(key string) (string, error) {
|
||||
return kms.passphrase, nil
|
||||
@ -96,31 +97,52 @@ func (kms SecretsKMS) DeletePassphrase(key string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetKMS returns an instance of Key Management System
|
||||
func GetKMS(opts, secrets map[string]string) (EncryptionKMS, error) {
|
||||
kmsType, ok := opts["encryptionKMS"]
|
||||
if !ok || kmsType == "" || kmsType == "secrets" {
|
||||
return initSecretsKMS(secrets)
|
||||
}
|
||||
if kmsType == "vault" {
|
||||
return InitVaultKMS(opts, secrets)
|
||||
}
|
||||
return nil, fmt.Errorf("unknown encryption KMS type %s", kmsType)
|
||||
// GetID is returning ID representing default KMS `default`
|
||||
func (kms SecretsKMS) GetID() string {
|
||||
return defaultKMSType
|
||||
}
|
||||
|
||||
// GetKMSConfig returns required keys for KMS to instantiate from it's config
|
||||
// - map with kms type and ID keys
|
||||
// - error if format is invalid
|
||||
func GetKMSConfig(config string) (map[string]string, error) {
|
||||
kmsConfigParts := strings.Split(config, "|")
|
||||
if len(kmsConfigParts) != 2 {
|
||||
return make(map[string]string), fmt.Errorf("failed to parse encryption KMS "+
|
||||
"configuration from config string, expected <type>|<id>, got: %s", config)
|
||||
// GetKMS returns an instance of Key Management System
|
||||
func GetKMS(kmsID string, secrets map[string]string) (EncryptionKMS, error) {
|
||||
if kmsID == "" || kmsID == defaultKMSType {
|
||||
return initSecretsKMS(secrets)
|
||||
}
|
||||
return map[string]string{
|
||||
"encryptionKMS": kmsConfigParts[0],
|
||||
"encryptionKMSID": kmsConfigParts[1],
|
||||
}, nil
|
||||
|
||||
// #nosec
|
||||
content, err := ioutil.ReadFile(kmsConfigPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read kms configuration from %s: %s",
|
||||
kmsConfigPath, 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)
|
||||
}
|
||||
|
||||
kmsConfigData, ok := config[kmsID].(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing encryption KMS configuration with %s", kmsID)
|
||||
}
|
||||
kmsConfig := make(map[string]string)
|
||||
for key, value := range kmsConfigData {
|
||||
kmsConfig[key], ok = value.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("broken KMS config: '%s' for '%s' is not a string",
|
||||
value, key)
|
||||
}
|
||||
}
|
||||
|
||||
kmsType, ok := kmsConfig[kmsTypeKey]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("encryption KMS configuration for %s is missing KMS type", kmsID)
|
||||
}
|
||||
|
||||
if kmsType == "vault" {
|
||||
return InitVaultKMS(kmsID, kmsConfig, secrets)
|
||||
}
|
||||
return nil, fmt.Errorf("unknown encryption KMS type %s", kmsType)
|
||||
}
|
||||
|
||||
// GetCryptoPassphrase Retrieves passphrase to encrypt volume
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -48,9 +49,9 @@ const (
|
||||
kmsKMS represents a Hashicorp Vault KMS configuration
|
||||
|
||||
Example JSON structure in the KMS config is,
|
||||
[
|
||||
{
|
||||
"encryptionKMSID": "local_vault_unique_identifier",
|
||||
{
|
||||
"local_vault_unique_identifier": {
|
||||
"encryptionKMSType": "vault",
|
||||
"vaultAddress": "https://127.0.0.1:8500",
|
||||
"vaultAuthPath": "/v1/auth/kubernetes/login",
|
||||
"vaultRole": "csi-kubernetes",
|
||||
@ -61,88 +62,83 @@ Example JSON structure in the KMS config is,
|
||||
"vaultCAFromSecret": "vault-ca"
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
*/
|
||||
type VaultKMS struct {
|
||||
EncryptionKMSID string `json:"encryptionKMSID"`
|
||||
VaultAddress string `json:"vaultAddress"`
|
||||
VaultAuthPath string `json:"vaultAuthPath"`
|
||||
VaultRole string `json:"vaultRole"`
|
||||
VaultNamespace string `json:"vaultNamespace"`
|
||||
VaultPassphraseRoot string `json:"vaultPassphraseRoot"`
|
||||
VaultPassphrasePath string `json:"vaultPassphrasePath"`
|
||||
VaultCAVerify bool `json:"vaultCAVerify"`
|
||||
VaultCAFromSecret string `json:"vaultCAFromSecret"`
|
||||
EncryptionKMSID string
|
||||
VaultAddress string
|
||||
VaultAuthPath string
|
||||
VaultRole string
|
||||
VaultNamespace string
|
||||
VaultPassphraseRoot string
|
||||
VaultPassphrasePath string
|
||||
VaultCAVerify bool
|
||||
vaultCA *x509.CertPool
|
||||
}
|
||||
|
||||
// InitVaultKMS returns an interface to HashiCorp Vault KMS
|
||||
func InitVaultKMS(opts, secrets map[string]string) (EncryptionKMS, error) {
|
||||
var config []VaultKMS
|
||||
func InitVaultKMS(kmsID string, config, secrets map[string]string) (EncryptionKMS, error) {
|
||||
var (
|
||||
ok bool
|
||||
err error
|
||||
)
|
||||
kms := &VaultKMS{}
|
||||
kms.EncryptionKMSID = kmsID
|
||||
|
||||
vaultID, ok := opts["encryptionKMSID"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing encryptionKMSID for vault as encryption KMS")
|
||||
kms.VaultAddress, ok = config["vaultAddress"]
|
||||
if !ok || kms.VaultAddress == "" {
|
||||
return nil, fmt.Errorf("missing 'vaultAddress' for vault KMS %s", kmsID)
|
||||
}
|
||||
kms.VaultAuthPath, ok = config["vaultAuthPath"]
|
||||
if !ok || kms.VaultAuthPath == "" {
|
||||
kms.VaultAuthPath = vaultDefaultAuthPath
|
||||
}
|
||||
kms.VaultRole, ok = config["vaultRole"]
|
||||
if !ok || kms.VaultRole == "" {
|
||||
kms.VaultRole = vaultDefaultRole
|
||||
}
|
||||
kms.VaultNamespace, ok = config["vaultNamespace"]
|
||||
if !ok || kms.VaultNamespace == "" {
|
||||
kms.VaultNamespace = vaultDefaultNamespace
|
||||
}
|
||||
kms.VaultPassphraseRoot, ok = config["vaultPassphraseRoot"]
|
||||
if !ok || kms.VaultPassphraseRoot == "" {
|
||||
kms.VaultPassphraseRoot = vaultDefaultPassphraseRoot
|
||||
}
|
||||
kms.VaultPassphrasePath, ok = config["vaultPassphrasePath"]
|
||||
if !ok || kms.VaultPassphrasePath == "" {
|
||||
kms.VaultPassphrasePath = vaultDefaultPassphrasePath
|
||||
}
|
||||
kms.VaultCAVerify = true
|
||||
verifyCA, ok := config["vaultCAVerify"]
|
||||
if ok {
|
||||
kms.VaultCAVerify, err = strconv.ParseBool(verifyCA)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse 'vaultCAVerify' for vault <%s> kms config: %s",
|
||||
kmsID, err)
|
||||
}
|
||||
}
|
||||
vaultCAFromSecret, ok := config["vaultCAFromSecret"]
|
||||
if ok && vaultCAFromSecret != "" {
|
||||
caPEM, ok := secrets[vaultCAFromSecret]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing vault CA in secret %s", vaultCAFromSecret)
|
||||
}
|
||||
roots := x509.NewCertPool()
|
||||
ok = roots.AppendCertsFromPEM([]byte(caPEM))
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed loading CA bundle for vault from secret %s",
|
||||
vaultCAFromSecret)
|
||||
}
|
||||
kms.vaultCA = roots
|
||||
}
|
||||
|
||||
// #nosec
|
||||
content, err := ioutil.ReadFile(kmsConfigPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching vault configuration for vault ID (%s): (%s)",
|
||||
vaultID, err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(content, &config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unmarshal failed: %v. raw buffer response: %s",
|
||||
err, string(content))
|
||||
}
|
||||
|
||||
for i := range config {
|
||||
vault := &config[i]
|
||||
if vault.EncryptionKMSID != vaultID {
|
||||
continue
|
||||
}
|
||||
if vault.VaultAddress == "" {
|
||||
return nil, fmt.Errorf("missing vaultAddress for vault as encryption KMS")
|
||||
}
|
||||
if vault.VaultAuthPath == "" {
|
||||
vault.VaultAuthPath = vaultDefaultAuthPath
|
||||
}
|
||||
if vault.VaultRole == "" {
|
||||
vault.VaultRole = vaultDefaultRole
|
||||
}
|
||||
if vault.VaultNamespace == "" {
|
||||
vault.VaultNamespace = vaultDefaultNamespace
|
||||
}
|
||||
if vault.VaultPassphraseRoot == "" {
|
||||
vault.VaultPassphraseRoot = vaultDefaultPassphraseRoot
|
||||
}
|
||||
if vault.VaultPassphrasePath == "" {
|
||||
vault.VaultPassphrasePath = vaultDefaultPassphrasePath
|
||||
}
|
||||
if vault.VaultCAFromSecret != "" {
|
||||
caPEM, ok := secrets[vault.VaultCAFromSecret]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing vault CA in secret %s", vault.VaultCAFromSecret)
|
||||
}
|
||||
roots := x509.NewCertPool()
|
||||
ok = roots.AppendCertsFromPEM([]byte(caPEM))
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed loading CA bundle for vault from secret %s",
|
||||
vault.VaultCAFromSecret)
|
||||
}
|
||||
vault.vaultCA = roots
|
||||
}
|
||||
return vault, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("missing configuration for vault ID (%s)", vaultID)
|
||||
return kms, nil
|
||||
}
|
||||
|
||||
// KmsConfig returns KMS configuration: "<kms-type>|<kms-id>"
|
||||
func (kms *VaultKMS) KmsConfig() string {
|
||||
return fmt.Sprintf("vault|%s", kms.EncryptionKMSID)
|
||||
// GetID is returning correlation ID to KMS configuration
|
||||
func (kms *VaultKMS) GetID() string {
|
||||
return kms.EncryptionKMSID
|
||||
}
|
||||
|
||||
// GetPassphrase returns passphrase from Vault
|
||||
|
@ -416,8 +416,8 @@ func (cj *CSIJournal) GetObjectUUIDData(ctx context.Context, monitors string, cr
|
||||
cj.cephUUIDDirectoryPrefix+objectUUID, cj.encryptKMSKey)
|
||||
if err != nil {
|
||||
if _, ok := err.(ErrKeyNotFound); !ok {
|
||||
klog.Errorf(Log(ctx, "=> GetObjectUUIDData encryptedKMS failed: %s (%s)"), cj.cephUUIDDirectoryPrefix+objectUUID, err)
|
||||
return "", "", "", err
|
||||
return "", "", "", fmt.Errorf("OMapVal for %s/%s failed to get encryption KMS value: %s",
|
||||
pool, cj.cephUUIDDirectoryPrefix+objectUUID, err)
|
||||
}
|
||||
// ErrKeyNotFound means no encryption KMS was used
|
||||
}
|
||||
|
Reference in New Issue
Block a user