diff --git a/internal/util/aws_metadata.go b/internal/kms/aws_metadata.go similarity index 95% rename from internal/util/aws_metadata.go rename to internal/kms/aws_metadata.go index dd4ba1e6d..a281b219f 100644 --- a/internal/util/aws_metadata.go +++ b/internal/kms/aws_metadata.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package kms import ( "context" @@ -60,7 +60,7 @@ const ( awsCMK = "AWS_CMK_ARN" ) -var _ = RegisterKMSProvider(KMSProvider{ +var _ = RegisterProvider(Provider{ UniqueID: kmsTypeAWSMetadata, Initializer: initAWSMetadataKMS, }) @@ -78,7 +78,7 @@ type AWSMetadataKMS struct { cmk string } -func initAWSMetadataKMS(args KMSInitializerArgs) (EncryptionKMS, error) { +func initAWSMetadataKMS(args ProviderInitArgs) (EncryptionKMS, error) { kms := &AWSMetadataKMS{ namespace: args.Namespace, } @@ -152,10 +152,10 @@ func (kms *AWSMetadataKMS) Destroy() { // Nothing to do. } -// requiresDEKStore indicates that the DEKs should get stored in the metadata +// RequiresDEKStore indicates that the DEKs should get stored in the metadata // of the volumes. This Amazon KMS provider does not support storing DEKs in // AWS as that adds additional costs. -func (kms *AWSMetadataKMS) requiresDEKStore() DEKStoreType { +func (kms *AWSMetadataKMS) RequiresDEKStore() DEKStoreType { return DEKStoreMetadata } diff --git a/internal/util/aws_metadata_test.go b/internal/kms/aws_metadata_test.go similarity index 98% rename from internal/util/aws_metadata_test.go rename to internal/kms/aws_metadata_test.go index 5f82db763..4b7078817 100644 --- a/internal/util/aws_metadata_test.go +++ b/internal/kms/aws_metadata_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package kms import ( "testing" diff --git a/internal/util/kms.go b/internal/kms/kms.go similarity index 60% rename from internal/util/kms.go rename to internal/kms/kms.go index f2af1fe0b..5fd7d3dcb 100644 --- a/internal/util/kms.go +++ b/internal/kms/kms.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package kms import ( "context" @@ -46,6 +46,12 @@ const ( // defaultKMSConfigMapName default ConfigMap name to fetch kms // connection details. defaultKMSConfigMapName = "csi-kms-connection-details" + + // kmsConfigPath is the location of the vault config file. + kmsConfigPath = "/etc/ceph-csi-encryption-kms-config/config.json" + + // Default KMS type. + DefaultKMSType = "default" ) // GetKMS returns an instance of Key Management System. @@ -56,8 +62,8 @@ const ( // - secrets contain additional details, like TLS certificates to connect to // the KMS func GetKMS(tenant, kmsID string, secrets map[string]string) (EncryptionKMS, error) { - if kmsID == "" || kmsID == defaultKMSType { - return initSecretsKMS(secrets) + if kmsID == "" || kmsID == DefaultKMSType { + return GetDefaultKMS(secrets) } config, err := getKMSConfiguration() @@ -170,10 +176,10 @@ func getKMSConfigMap() (map[string]interface{}, error) { return kmsConfig, nil } -// getKMSProvider inspects the configuration and tries to identify what -// KMSProvider is expected to be used with it. This returns the -// KMSProvider.UniqueID. -func getKMSProvider(config map[string]interface{}) (string, error) { +// getProvider inspects the configuration and tries to identify what +// Provider is expected to be used with it. This returns the +// Provider.UniqueID. +func getProvider(config map[string]interface{}) (string, error) { var name string providerName, ok := config[kmsTypeKey] @@ -202,45 +208,45 @@ func getKMSProvider(config map[string]interface{}) (string, error) { "configuration option %q or %q", kmsTypeKey, kmsProviderKey) } -// KMSInitializerArgs get passed to KMSInitializerFunc when a new instance of a -// KMSProvider is initialized. -type KMSInitializerArgs struct { +// ProviderInitArgs get passed to ProviderInitFunc when a new instance of a +// Provider is initialized. +type ProviderInitArgs struct { Tenant string Config map[string]interface{} Secrets map[string]string // Namespace contains the Kubernetes Namespace where the Ceph-CSI Pods // are running. This is an optional option, and might be unset when the - // KMSProvider.Initializer is called. + // Provider.Initializer is called. Namespace string } -// KMSInitializerFunc gets called when the KMSProvider needs to be +// ProviderInitFunc gets called when the Provider needs to be // instantiated. -type KMSInitializerFunc func(args KMSInitializerArgs) (EncryptionKMS, error) +type ProviderInitFunc func(args ProviderInitArgs) (EncryptionKMS, error) -type KMSProvider struct { +type Provider struct { UniqueID string - Initializer KMSInitializerFunc + Initializer ProviderInitFunc } type kmsProviderList struct { - providers map[string]KMSProvider + providers map[string]Provider } // kmsManager is used to create instances for a KMS provider. -var kmsManager = kmsProviderList{providers: map[string]KMSProvider{}} +var kmsManager = kmsProviderList{providers: map[string]Provider{}} -// RegisterKMSProvider uses kmsManager to register the given KMSProvider. The -// KMSProvider.Initializer function will get called when a new instance of the +// RegisterProvider uses kmsManager to register the given Provider. The +// Provider.Initializer function will get called when a new instance of the // KMS is required. -func RegisterKMSProvider(provider KMSProvider) bool { +func RegisterProvider(provider Provider) bool { // validate uniqueness of the UniqueID if provider.UniqueID == "" { panic("a provider MUST set a UniqueID") } _, ok := kmsManager.providers[provider.UniqueID] if ok { - panic("duplicate registration of KMSProvider.UniqueID: " + provider.UniqueID) + panic("duplicate registration of Provider.UniqueID: " + provider.UniqueID) } // validate the Initializer @@ -253,14 +259,14 @@ func RegisterKMSProvider(provider KMSProvider) bool { return true } -// buildKMS creates a new KMSProvider instance, based on the configuration that -// was passed. This uses getKMSProvider() internally to identify the -// KMSProvider to instantiate. +// buildKMS creates a new Provider instance, based on the configuration that +// was passed. This uses getProvider() internally to identify the +// Provider to instantiate. func (kf *kmsProviderList) buildKMS( tenant string, config map[string]interface{}, secrets map[string]string) (EncryptionKMS, error) { - providerName, err := getKMSProvider(config) + providerName, err := getProvider(config) if err != nil { return nil, err } @@ -271,14 +277,14 @@ func (kf *kmsProviderList) buildKMS( providerName) } - kmsInitArgs := KMSInitializerArgs{ + kmsInitArgs := ProviderInitArgs{ Tenant: tenant, Config: config, Secrets: secrets, } // Namespace is an optional parameter, it may not be set and is not - // required for all KMSProviders + // required for all Providers ns, err := getPodNamespace() if err == nil { kmsInitArgs.Namespace = ns @@ -286,3 +292,96 @@ func (kf *kmsProviderList) buildKMS( return provider.Initializer(kmsInitArgs) } + +func GetDefaultKMS(secrets map[string]string) (EncryptionKMS, error) { + provider, ok := kmsManager.providers[DefaultKMSType] + if !ok { + return nil, fmt.Errorf("could not find KMS provider %q", DefaultKMSType) + } + + kmsInitArgs := ProviderInitArgs{ + Secrets: secrets, + } + + return provider.Initializer(kmsInitArgs) +} + +// EncryptionKMS provides external Key Management System for encryption +// passphrases storage. +type EncryptionKMS interface { + Destroy() + + // RequiresDEKStore returns the DEKStoreType that is needed to be + // configure for the KMS. Nothing needs to be done when this function + // returns DEKStoreIntegrated, otherwise you will need to configure an + // alternative storage for the DEKs. + RequiresDEKStore() DEKStoreType + + // EncryptDEK provides a way for a KMS to encrypt a DEK. In case the + // encryption is done transparently inside the KMS service, the + // function can return an unencrypted value. + EncryptDEK(volumeID, plainDEK string) (string, error) + + // DecryptDEK provides a way for a KMS to decrypt a DEK. In case the + // encryption is done transparently inside the KMS service, the + // function does not need to do anything except return the encyptedDEK + // as it was received. + DecryptDEK(volumeID, encyptedDEK string) (string, error) +} + +// DEKStoreType describes what DEKStore needs to be configured when using a +// particular KMS. A KMS might support different DEKStores depending on its +// configuration. +type DEKStoreType string + +const ( + // DEKStoreIntegrated indicates that the KMS itself supports storing + // DEKs. + DEKStoreIntegrated = DEKStoreType("") + // DEKStoreMetadata indicates that the KMS should be configured to + // store the DEK in the metadata of the volume. + DEKStoreMetadata = DEKStoreType("metadata") +) + +// DEKStore allows KMS instances to implement a modular backend for DEK +// storage. This can be used to store the DEK in a different location, in case +// the KMS can not store passphrases for volumes. +type DEKStore interface { + // StoreDEK saves the DEK in the configured store. + StoreDEK(volumeID string, dek string) error + // FetchDEK reads the DEK from the configured store and returns it. + FetchDEK(volumeID string) (string, error) + // RemoveDEK deletes the DEK from the configured store. + RemoveDEK(volumeID string) error +} + +// IntegratedDEK is a DEKStore that can not be configured. Either the KMS does +// not use a DEK, or the DEK is stored in the KMS without additional +// configuration options. +type IntegratedDEK struct{} + +func (i IntegratedDEK) RequiresDEKStore() DEKStoreType { + return DEKStoreIntegrated +} + +func (i IntegratedDEK) EncryptDEK(volumeID, plainDEK string) (string, error) { + return plainDEK, nil +} + +func (i IntegratedDEK) DecryptDEK(volumeID, encyptedDEK string) (string, error) { + return encyptedDEK, nil +} + +// getKeys takes a map that uses strings for keys and returns a slice with the +// keys. +func getKeys(m map[string]interface{}) []string { + keys := make([]string, len(m)) + + i := 0 + for k := range m { + keys[i] = k + i++ + } + + return keys +} diff --git a/internal/util/kms_test.go b/internal/kms/kms_test.go similarity index 76% rename from internal/util/kms_test.go rename to internal/kms/kms_test.go index 0d5f10b20..ec3452897 100644 --- a/internal/util/kms_test.go +++ b/internal/kms/kms_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package kms import ( "testing" @@ -22,22 +22,22 @@ import ( "github.com/stretchr/testify/assert" ) -func noinitKMS(args KMSInitializerArgs) (EncryptionKMS, error) { +func noinitKMS(args ProviderInitArgs) (EncryptionKMS, error) { return nil, nil } -func TestRegisterKMSProvider(t *testing.T) { +func TestRegisterProvider(t *testing.T) { t.Parallel() tests := []struct { - provider KMSProvider + provider Provider panics bool }{{ - KMSProvider{ + Provider{ UniqueID: "incomplete-provider", }, true, }, { - KMSProvider{ + Provider{ UniqueID: "initializer-only", Initializer: noinitKMS, }, @@ -47,9 +47,9 @@ func TestRegisterKMSProvider(t *testing.T) { for _, test := range tests { provider := test.provider if test.panics { - assert.Panics(t, func() { RegisterKMSProvider(provider) }) + assert.Panics(t, func() { RegisterProvider(provider) }) } else { - assert.True(t, RegisterKMSProvider(provider)) + assert.True(t, RegisterProvider(provider)) } } } diff --git a/internal/util/secretskms.go b/internal/kms/secretskms.go similarity index 94% rename from internal/util/secretskms.go rename to internal/kms/secretskms.go index 1436c9017..cac5838fb 100644 --- a/internal/util/secretskms.go +++ b/internal/kms/secretskms.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package kms import ( "context" @@ -36,9 +36,6 @@ const ( // Encryption passphrase location in K8s secrets. encryptionPassphraseKey = "encryptionPassphrase" - // Default KMS type. - defaultKMSType = "default" - // kmsTypeSecretsMetadata is the SecretsKMS with per-volume encryption, // where the DEK is stored in the metadata of the volume itself. kmsTypeSecretsMetadata = "metadata" @@ -53,16 +50,21 @@ const ( // SecretsKMS is default KMS implementation that means no KMS is in use. type SecretsKMS struct { - integratedDEK + IntegratedDEK passphrase string } -// initSecretsKMS initializes a SecretsKMS that uses the passphrase from the +var _ = RegisterProvider(Provider{ + UniqueID: DefaultKMSType, + Initializer: newSecretsKMS, +}) + +// newSecretsKMS initializes a SecretsKMS that uses the passphrase from the // secret that is configured for the StorageClass. This KMS provider uses a // single (LUKS) passhprase for all volumes. -func initSecretsKMS(secrets map[string]string) (EncryptionKMS, error) { - passphraseValue, ok := secrets[encryptionPassphraseKey] +func newSecretsKMS(args ProviderInitArgs) (EncryptionKMS, error) { + passphraseValue, ok := args.Secrets[encryptionPassphraseKey] if !ok { return nil, errors.New("missing encryption passphrase in secrets") } @@ -98,7 +100,7 @@ type SecretsMetadataKMS struct { SecretsKMS } -var _ = RegisterKMSProvider(KMSProvider{ +var _ = RegisterProvider(Provider{ UniqueID: kmsTypeSecretsMetadata, Initializer: initSecretsMetadataKMS, }) @@ -106,7 +108,7 @@ var _ = RegisterKMSProvider(KMSProvider{ // initSecretsMetadataKMS initializes a SecretsMetadataKMS that wraps a SecretsKMS, // so that the passphrase from the user provided or StorageClass secrets can be used // for encrypting/decrypting DEKs that are stored in a detached DEKStore. -func initSecretsMetadataKMS(args KMSInitializerArgs) (EncryptionKMS, error) { +func initSecretsMetadataKMS(args ProviderInitArgs) (EncryptionKMS, error) { var ( smKMS SecretsMetadataKMS encryptionPassphrase string @@ -179,7 +181,7 @@ func (kms SecretsMetadataKMS) Destroy() { kms.SecretsKMS.Destroy() } -func (kms SecretsMetadataKMS) requiresDEKStore() DEKStoreType { +func (kms SecretsMetadataKMS) RequiresDEKStore() DEKStoreType { return DEKStoreMetadata } diff --git a/internal/util/secretskms_test.go b/internal/kms/secretskms_test.go similarity index 81% rename from internal/util/secretskms_test.go rename to internal/kms/secretskms_test.go index f27dba7fd..e8a9c326f 100644 --- a/internal/util/secretskms_test.go +++ b/internal/kms/secretskms_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package kms import ( "testing" @@ -23,6 +23,26 @@ import ( "github.com/stretchr/testify/require" ) +func TestNewSecretsKMS(t *testing.T) { + t.Parallel() + secrets := map[string]string{} + + // no passphrase in the secrets, should fail + kms, err := newSecretsKMS(ProviderInitArgs{ + Secrets: secrets, + }) + assert.Error(t, err) + assert.Nil(t, kms) + + // set a passphrase and it should pass + secrets[encryptionPassphraseKey] = "plaintext encryption key" + kms, err = newSecretsKMS(ProviderInitArgs{ + Secrets: secrets, + }) + assert.NotNil(t, kms) + assert.NoError(t, err) +} + func TestGenerateNonce(t *testing.T) { t.Parallel() size := 64 @@ -44,7 +64,7 @@ func TestGenerateCipher(t *testing.T) { func TestInitSecretsMetadataKMS(t *testing.T) { t.Parallel() - args := KMSInitializerArgs{ + args := ProviderInitArgs{ Tenant: "tenant", Config: nil, Secrets: map[string]string{}, @@ -61,7 +81,7 @@ func TestInitSecretsMetadataKMS(t *testing.T) { kms, err = initSecretsMetadataKMS(args) assert.NoError(t, err) require.NotNil(t, kms) - assert.Equal(t, DEKStoreMetadata, kms.requiresDEKStore()) + assert.Equal(t, DEKStoreMetadata, kms.RequiresDEKStore()) } func TestWorkflowSecretsMetadataKMS(t *testing.T) { @@ -69,7 +89,7 @@ func TestWorkflowSecretsMetadataKMS(t *testing.T) { secrets := map[string]string{ encryptionPassphraseKey: "my-passphrase-from-kubernetes", } - args := KMSInitializerArgs{ + args := ProviderInitArgs{ Tenant: "tenant", Config: nil, Secrets: secrets, @@ -81,9 +101,7 @@ func TestWorkflowSecretsMetadataKMS(t *testing.T) { require.NotNil(t, kms) // plainDEK is the (LUKS) passphrase for the volume - plainDEK, err := generateNewEncryptionPassphrase() - assert.NoError(t, err) - assert.NotEqual(t, "", plainDEK) + plainDEK := "usually created with generateNewEncryptionPassphrase()" encryptedDEK, err := kms.EncryptDEK(volumeID, plainDEK) assert.NoError(t, err) diff --git a/internal/util/vault.go b/internal/kms/vault.go similarity index 99% rename from internal/util/vault.go rename to internal/kms/vault.go index bb095d362..6a7fc001c 100644 --- a/internal/util/vault.go +++ b/internal/kms/vault.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package kms import ( "errors" @@ -89,7 +89,7 @@ type vaultConnection struct { type VaultKMS struct { vaultConnection - integratedDEK + IntegratedDEK // vaultPassphrasePath (VPP) used to be added before the "key" of the // secret (like /v1/secret/data//key) @@ -317,13 +317,13 @@ func (vc *vaultConnection) getDeleteKeyContext() map[string]string { return keyContext } -var _ = RegisterKMSProvider(KMSProvider{ +var _ = RegisterProvider(Provider{ UniqueID: kmsTypeVault, Initializer: initVaultKMS, }) // InitVaultKMS returns an interface to HashiCorp Vault KMS. -func initVaultKMS(args KMSInitializerArgs) (EncryptionKMS, error) { +func initVaultKMS(args ProviderInitArgs) (EncryptionKMS, error) { kms := &VaultKMS{} err := kms.initConnection(args.Config) if err != nil { diff --git a/internal/util/vault_sa.go b/internal/kms/vault_sa.go similarity index 98% rename from internal/util/vault_sa.go rename to internal/kms/vault_sa.go index c8940ea03..11ec2a3b5 100644 --- a/internal/util/vault_sa.go +++ b/internal/kms/vault_sa.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package kms import ( "context" @@ -77,14 +77,14 @@ type VaultTenantSA struct { saTokenDir string } -var _ = RegisterKMSProvider(KMSProvider{ +var _ = RegisterProvider(Provider{ UniqueID: kmsTypeVaultTenantSA, Initializer: initVaultTenantSA, }) // initVaultTenantSA returns an interface to HashiCorp Vault KMS where Tenants // use their ServiceAccount to access the service. -func initVaultTenantSA(args KMSInitializerArgs) (EncryptionKMS, error) { +func initVaultTenantSA(args ProviderInitArgs) (EncryptionKMS, error) { var err error config := args.Config diff --git a/internal/util/vault_sa_test.go b/internal/kms/vault_sa_test.go similarity index 99% rename from internal/util/vault_sa_test.go rename to internal/kms/vault_sa_test.go index 789d42b3f..5778f2aba 100644 --- a/internal/util/vault_sa_test.go +++ b/internal/kms/vault_sa_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package kms import ( "errors" diff --git a/internal/util/vault_test.go b/internal/kms/vault_test.go similarity index 99% rename from internal/util/vault_test.go rename to internal/kms/vault_test.go index 93caf707b..70abbbb2c 100644 --- a/internal/util/vault_test.go +++ b/internal/kms/vault_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package kms import ( "errors" diff --git a/internal/util/vault_tokens.go b/internal/kms/vault_tokens.go similarity index 99% rename from internal/util/vault_tokens.go rename to internal/kms/vault_tokens.go index 57f8e286d..bae918aa9 100644 --- a/internal/util/vault_tokens.go +++ b/internal/kms/vault_tokens.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package kms import ( "context" @@ -181,7 +181,7 @@ Example JSON structure in the KMS config is, */ type vaultTenantConnection struct { vaultConnection - integratedDEK + IntegratedDEK client *kubernetes.Clientset @@ -204,13 +204,13 @@ type VaultTokensKMS struct { TokenName string } -var _ = RegisterKMSProvider(KMSProvider{ +var _ = RegisterProvider(Provider{ UniqueID: kmsTypeVaultTokens, Initializer: initVaultTokensKMS, }) // InitVaultTokensKMS returns an interface to HashiCorp Vault KMS. -func initVaultTokensKMS(args KMSInitializerArgs) (EncryptionKMS, error) { +func initVaultTokensKMS(args ProviderInitArgs) (EncryptionKMS, error) { var err error config := args.Config diff --git a/internal/util/vault_tokens_test.go b/internal/kms/vault_tokens_test.go similarity index 99% rename from internal/util/vault_tokens_test.go rename to internal/kms/vault_tokens_test.go index cddd8dd0a..df1e105f0 100644 --- a/internal/util/vault_tokens_test.go +++ b/internal/kms/vault_tokens_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package kms import ( "encoding/json" @@ -76,7 +76,7 @@ func TestInitVaultTokensKMS(t *testing.T) { return } - args := KMSInitializerArgs{ + args := ProviderInitArgs{ Tenant: "bob", Config: make(map[string]interface{}), Secrets: nil, diff --git a/internal/rbd/encryption.go b/internal/rbd/encryption.go index 99d35469f..c28aa532c 100644 --- a/internal/rbd/encryption.go +++ b/internal/rbd/encryption.go @@ -22,6 +22,7 @@ import ( "fmt" "strings" + kmsapi "github.com/ceph/ceph-csi/internal/kms" "github.com/ceph/ceph-csi/internal/util" "github.com/ceph/ceph-csi/internal/util/log" @@ -289,7 +290,7 @@ func (ri *rbdImage) ParseEncryptionOpts(ctx context.Context, volOptions map[stri // configureEncryption sets up the VolumeEncryption for this rbdImage. Once // configured, use isEncrypted() to see if the volume supports encryption. func (ri *rbdImage) configureEncryption(kmsID string, credentials map[string]string) error { - kms, err := util.GetKMS(ri.Owner, kmsID, credentials) + kms, err := kmsapi.GetKMS(ri.Owner, kmsID, credentials) if err != nil { return err } diff --git a/internal/util/crypto.go b/internal/util/crypto.go index 79821e328..d04ab2cf7 100644 --- a/internal/util/crypto.go +++ b/internal/util/crypto.go @@ -26,6 +26,7 @@ import ( "strconv" "strings" + "github.com/ceph/ceph-csi/internal/kms" "github.com/ceph/ceph-csi/internal/util/log" ) @@ -33,9 +34,6 @@ const ( mapperFilePrefix = "luks-rbd-" mapperFilePathPrefix = "/dev/mapper" - // kmsConfigPath is the location of the vault config file. - kmsConfigPath = "/etc/ceph-csi-encryption-kms-config/config.json" - // Passphrase size - 20 bytes is 160 bits to satisfy: // https://tools.ietf.org/html/rfc6749#section-10.10 encryptionPassphraseSize = 20 @@ -54,11 +52,11 @@ var ( ) type VolumeEncryption struct { - KMS EncryptionKMS + KMS kms.EncryptionKMS // dekStore that will be used, this can be the EncryptionKMS or a // different object implementing the DEKStore interface. - dekStore DEKStore + dekStore kms.DEKStore id string } @@ -76,7 +74,7 @@ func FetchEncryptionKMSID(encrypted, kmsID string) (string, error) { } if kmsID == "" { - kmsID = defaultKMSType + kmsID = kms.DefaultKMSType } return kmsID, nil @@ -88,24 +86,24 @@ func FetchEncryptionKMSID(encrypted, kmsID string) (string, error) { // Callers that receive a ErrDEKStoreNeeded error, should use // VolumeEncryption.SetDEKStore() to configure an alternative storage for the // DEKs. -func NewVolumeEncryption(id string, kms EncryptionKMS) (*VolumeEncryption, error) { +func NewVolumeEncryption(id string, ekms kms.EncryptionKMS) (*VolumeEncryption, error) { kmsID := id if kmsID == "" { // if kmsID is not set, encryption is enabled, and the type is // SecretsKMS - kmsID = defaultKMSType + kmsID = kms.DefaultKMSType } ve := &VolumeEncryption{ id: kmsID, - KMS: kms, + KMS: ekms, } - if kms.requiresDEKStore() == DEKStoreIntegrated { - dekStore, ok := kms.(DEKStore) + if ekms.RequiresDEKStore() == kms.DEKStoreIntegrated { + dekStore, ok := ekms.(kms.DEKStore) if !ok { return nil, fmt.Errorf("KMS %T does not implement the "+ - "DEKStore interface", kms) + "DEKStore interface", ekms) } ve.dekStore = dekStore @@ -118,7 +116,7 @@ func NewVolumeEncryption(id string, kms EncryptionKMS) (*VolumeEncryption, error // SetDEKStore sets the DEKStore for this VolumeEncryption instance. It will be // used when StoreNewCryptoPassphrase() or RemoveDEK() is called. -func (ve *VolumeEncryption) SetDEKStore(dekStore DEKStore) { +func (ve *VolumeEncryption) SetDEKStore(dekStore kms.DEKStore) { ve.dekStore = dekStore } @@ -141,72 +139,6 @@ func (ve *VolumeEncryption) GetID() string { return ve.id } -// EncryptionKMS provides external Key Management System for encryption -// passphrases storage. -type EncryptionKMS interface { - Destroy() - - // requiresDEKStore returns the DEKStoreType that is needed to be - // configure for the KMS. Nothing needs to be done when this function - // returns DEKStoreIntegrated, otherwise you will need to configure an - // alternative storage for the DEKs. - requiresDEKStore() DEKStoreType - - // EncryptDEK provides a way for a KMS to encrypt a DEK. In case the - // encryption is done transparently inside the KMS service, the - // function can return an unencrypted value. - EncryptDEK(volumeID, plainDEK string) (string, error) - - // DecryptDEK provides a way for a KMS to decrypt a DEK. In case the - // encryption is done transparently inside the KMS service, the - // function does not need to do anything except return the encyptedDEK - // as it was received. - DecryptDEK(volumeID, encyptedDEK string) (string, error) -} - -// DEKStoreType describes what DEKStore needs to be configured when using a -// particular KMS. A KMS might support different DEKStores depending on its -// configuration. -type DEKStoreType string - -const ( - // DEKStoreIntegrated indicates that the KMS itself supports storing - // DEKs. - DEKStoreIntegrated = DEKStoreType("") - // DEKStoreMetadata indicates that the KMS should be configured to - // store the DEK in the metadata of the volume. - DEKStoreMetadata = DEKStoreType("metadata") -) - -// DEKStore allows KMS instances to implement a modular backend for DEK -// storage. This can be used to store the DEK in a different location, in case -// the KMS can not store passphrases for volumes. -type DEKStore interface { - // StoreDEK saves the DEK in the configured store. - StoreDEK(volumeID string, dek string) error - // FetchDEK reads the DEK from the configured store and returns it. - FetchDEK(volumeID string) (string, error) - // RemoveDEK deletes the DEK from the configured store. - RemoveDEK(volumeID string) error -} - -// integratedDEK is a DEKStore that can not be configured. Either the KMS does -// not use a DEK, or the DEK is stored in the KMS without additional -// configuration options. -type integratedDEK struct{} - -func (i integratedDEK) requiresDEKStore() DEKStoreType { - return DEKStoreIntegrated -} - -func (i integratedDEK) EncryptDEK(volumeID, plainDEK string) (string, error) { - return plainDEK, nil -} - -func (i integratedDEK) DecryptDEK(volumeID, encyptedDEK string) (string, error) { - return encyptedDEK, nil -} - // StoreCryptoPassphrase takes an unencrypted passphrase, encrypts it and saves // it in the DEKStore. func (ve *VolumeEncryption) StoreCryptoPassphrase(volumeID, passphrase string) error { diff --git a/internal/util/crypto_test.go b/internal/util/crypto_test.go index d063c8d5a..28b8fefea 100644 --- a/internal/util/crypto_test.go +++ b/internal/util/crypto_test.go @@ -20,26 +20,12 @@ import ( "encoding/base64" "testing" + "github.com/ceph/ceph-csi/internal/kms" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestInitSecretsKMS(t *testing.T) { - t.Parallel() - secrets := map[string]string{} - - // no passphrase in the secrets, should fail - kms, err := initSecretsKMS(secrets) - assert.Error(t, err) - assert.Nil(t, kms) - - // set a passphrase and it should pass - secrets[encryptionPassphraseKey] = "plaintext encryption key" - kms, err = initSecretsKMS(secrets) - assert.NotNil(t, kms) - assert.NoError(t, err) -} - func TestGenerateNewEncryptionPassphrase(t *testing.T) { t.Parallel() b64Passphrase, err := generateNewEncryptionPassphrase() @@ -55,17 +41,18 @@ func TestGenerateNewEncryptionPassphrase(t *testing.T) { func TestKMSWorkflow(t *testing.T) { t.Parallel() secrets := map[string]string{ - encryptionPassphraseKey: "workflow test", + // FIXME: use encryptionPassphraseKey from SecretsKMS + "encryptionPassphrase": "workflow test", } - kms, err := GetKMS("tenant", defaultKMSType, secrets) + kmsProvider, err := kms.GetDefaultKMS(secrets) assert.NoError(t, err) - require.NotNil(t, kms) + require.NotNil(t, kmsProvider) - ve, err := NewVolumeEncryption("", kms) + ve, err := NewVolumeEncryption("", kmsProvider) assert.NoError(t, err) require.NotNil(t, ve) - assert.Equal(t, defaultKMSType, ve.GetID()) + assert.Equal(t, kms.DefaultKMSType, ve.GetID()) volumeID := "volume-id" @@ -74,5 +61,5 @@ func TestKMSWorkflow(t *testing.T) { passphrase, err := ve.GetCryptoPassphrase(volumeID) assert.NoError(t, err) - assert.Equal(t, secrets[encryptionPassphraseKey], passphrase) + assert.Equal(t, secrets["encryptionPassphrase"], passphrase) } diff --git a/internal/util/util.go b/internal/util/util.go index 62dcf2b2f..17a2836d1 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -344,20 +344,6 @@ func contains(s []string, key string) bool { return false } -// getKeys takes a map that uses strings for keys and returns a slice with the -// keys. -func getKeys(m map[string]interface{}) []string { - keys := make([]string, len(m)) - - i := 0 - for k := range m { - keys[i] = k - i++ - } - - return keys -} - // CallStack returns the stack of the calls in the current goroutine. Useful // for debugging or reporting errors. This is a friendly alternative to // assert() or panic().