cleanup: move KMS functionality into its own package

A new "internal/kms" package is introduced, it holds the API that can be
consumed by the RBD components.

The KMS providers are currently in the same package as the API. With
later follow-up changes the providers will be placed in their own
sub-package.

Because of the name of the package "kms", the types, functions and
structs inside the package should not be prefixed with KMS anymore:

    internal/kms/kms.go:213:6: type name will be used as kms.KMSInitializerArgs by other packages, and that stutters; consider calling this InitializerArgs (golint)

Updates: #852
Signed-off-by: Niels de Vos <ndevos@redhat.com>
This commit is contained in:
Niels de Vos 2021-08-26 13:43:20 +02:00 committed by mergify[bot]
parent 778b5e86de
commit 4a3b1181ce
16 changed files with 215 additions and 190 deletions

View File

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

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package util
package kms
import (
"testing"

View File

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

View File

@ -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))
}
}
}

View File

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

View File

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

View File

@ -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/<VPP>/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 {

View File

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

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package util
package kms
import (
"errors"

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package util
package kms
import (
"errors"

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
}

View File

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