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. limitations under the License.
*/ */
package util package kms
import ( import (
"context" "context"
@ -60,7 +60,7 @@ const (
awsCMK = "AWS_CMK_ARN" awsCMK = "AWS_CMK_ARN"
) )
var _ = RegisterKMSProvider(KMSProvider{ var _ = RegisterProvider(Provider{
UniqueID: kmsTypeAWSMetadata, UniqueID: kmsTypeAWSMetadata,
Initializer: initAWSMetadataKMS, Initializer: initAWSMetadataKMS,
}) })
@ -78,7 +78,7 @@ type AWSMetadataKMS struct {
cmk string cmk string
} }
func initAWSMetadataKMS(args KMSInitializerArgs) (EncryptionKMS, error) { func initAWSMetadataKMS(args ProviderInitArgs) (EncryptionKMS, error) {
kms := &AWSMetadataKMS{ kms := &AWSMetadataKMS{
namespace: args.Namespace, namespace: args.Namespace,
} }
@ -152,10 +152,10 @@ func (kms *AWSMetadataKMS) Destroy() {
// Nothing to do. // 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 // of the volumes. This Amazon KMS provider does not support storing DEKs in
// AWS as that adds additional costs. // AWS as that adds additional costs.
func (kms *AWSMetadataKMS) requiresDEKStore() DEKStoreType { func (kms *AWSMetadataKMS) RequiresDEKStore() DEKStoreType {
return DEKStoreMetadata return DEKStoreMetadata
} }

View File

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

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package util package kms
import ( import (
"context" "context"
@ -46,6 +46,12 @@ const (
// defaultKMSConfigMapName default ConfigMap name to fetch kms // defaultKMSConfigMapName default ConfigMap name to fetch kms
// connection details. // connection details.
defaultKMSConfigMapName = "csi-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. // GetKMS returns an instance of Key Management System.
@ -56,8 +62,8 @@ const (
// - secrets contain additional details, like TLS certificates to connect to // - secrets contain additional details, like TLS certificates to connect to
// the KMS // the KMS
func GetKMS(tenant, kmsID string, secrets map[string]string) (EncryptionKMS, error) { func GetKMS(tenant, kmsID string, secrets map[string]string) (EncryptionKMS, error) {
if kmsID == "" || kmsID == defaultKMSType { if kmsID == "" || kmsID == DefaultKMSType {
return initSecretsKMS(secrets) return GetDefaultKMS(secrets)
} }
config, err := getKMSConfiguration() config, err := getKMSConfiguration()
@ -170,10 +176,10 @@ func getKMSConfigMap() (map[string]interface{}, error) {
return kmsConfig, nil return kmsConfig, nil
} }
// getKMSProvider inspects the configuration and tries to identify what // getProvider inspects the configuration and tries to identify what
// KMSProvider is expected to be used with it. This returns the // Provider is expected to be used with it. This returns the
// KMSProvider.UniqueID. // Provider.UniqueID.
func getKMSProvider(config map[string]interface{}) (string, error) { func getProvider(config map[string]interface{}) (string, error) {
var name string var name string
providerName, ok := config[kmsTypeKey] providerName, ok := config[kmsTypeKey]
@ -202,45 +208,45 @@ func getKMSProvider(config map[string]interface{}) (string, error) {
"configuration option %q or %q", kmsTypeKey, kmsProviderKey) "configuration option %q or %q", kmsTypeKey, kmsProviderKey)
} }
// KMSInitializerArgs get passed to KMSInitializerFunc when a new instance of a // ProviderInitArgs get passed to ProviderInitFunc when a new instance of a
// KMSProvider is initialized. // Provider is initialized.
type KMSInitializerArgs struct { type ProviderInitArgs struct {
Tenant string Tenant string
Config map[string]interface{} Config map[string]interface{}
Secrets map[string]string Secrets map[string]string
// Namespace contains the Kubernetes Namespace where the Ceph-CSI Pods // Namespace contains the Kubernetes Namespace where the Ceph-CSI Pods
// are running. This is an optional option, and might be unset when the // are running. This is an optional option, and might be unset when the
// KMSProvider.Initializer is called. // Provider.Initializer is called.
Namespace string Namespace string
} }
// KMSInitializerFunc gets called when the KMSProvider needs to be // ProviderInitFunc gets called when the Provider needs to be
// instantiated. // instantiated.
type KMSInitializerFunc func(args KMSInitializerArgs) (EncryptionKMS, error) type ProviderInitFunc func(args ProviderInitArgs) (EncryptionKMS, error)
type KMSProvider struct { type Provider struct {
UniqueID string UniqueID string
Initializer KMSInitializerFunc Initializer ProviderInitFunc
} }
type kmsProviderList struct { type kmsProviderList struct {
providers map[string]KMSProvider providers map[string]Provider
} }
// kmsManager is used to create instances for a KMS 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 // RegisterProvider uses kmsManager to register the given Provider. The
// KMSProvider.Initializer function will get called when a new instance of the // Provider.Initializer function will get called when a new instance of the
// KMS is required. // KMS is required.
func RegisterKMSProvider(provider KMSProvider) bool { func RegisterProvider(provider Provider) bool {
// validate uniqueness of the UniqueID // validate uniqueness of the UniqueID
if provider.UniqueID == "" { if provider.UniqueID == "" {
panic("a provider MUST set a UniqueID") panic("a provider MUST set a UniqueID")
} }
_, ok := kmsManager.providers[provider.UniqueID] _, ok := kmsManager.providers[provider.UniqueID]
if ok { if ok {
panic("duplicate registration of KMSProvider.UniqueID: " + provider.UniqueID) panic("duplicate registration of Provider.UniqueID: " + provider.UniqueID)
} }
// validate the Initializer // validate the Initializer
@ -253,14 +259,14 @@ func RegisterKMSProvider(provider KMSProvider) bool {
return true return true
} }
// buildKMS creates a new KMSProvider instance, based on the configuration that // buildKMS creates a new Provider instance, based on the configuration that
// was passed. This uses getKMSProvider() internally to identify the // was passed. This uses getProvider() internally to identify the
// KMSProvider to instantiate. // Provider to instantiate.
func (kf *kmsProviderList) buildKMS( func (kf *kmsProviderList) buildKMS(
tenant string, tenant string,
config map[string]interface{}, config map[string]interface{},
secrets map[string]string) (EncryptionKMS, error) { secrets map[string]string) (EncryptionKMS, error) {
providerName, err := getKMSProvider(config) providerName, err := getProvider(config)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -271,14 +277,14 @@ func (kf *kmsProviderList) buildKMS(
providerName) providerName)
} }
kmsInitArgs := KMSInitializerArgs{ kmsInitArgs := ProviderInitArgs{
Tenant: tenant, Tenant: tenant,
Config: config, Config: config,
Secrets: secrets, Secrets: secrets,
} }
// Namespace is an optional parameter, it may not be set and is not // Namespace is an optional parameter, it may not be set and is not
// required for all KMSProviders // required for all Providers
ns, err := getPodNamespace() ns, err := getPodNamespace()
if err == nil { if err == nil {
kmsInitArgs.Namespace = ns kmsInitArgs.Namespace = ns
@ -286,3 +292,96 @@ func (kf *kmsProviderList) buildKMS(
return provider.Initializer(kmsInitArgs) 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. limitations under the License.
*/ */
package util package kms
import ( import (
"testing" "testing"
@ -22,22 +22,22 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func noinitKMS(args KMSInitializerArgs) (EncryptionKMS, error) { func noinitKMS(args ProviderInitArgs) (EncryptionKMS, error) {
return nil, nil return nil, nil
} }
func TestRegisterKMSProvider(t *testing.T) { func TestRegisterProvider(t *testing.T) {
t.Parallel() t.Parallel()
tests := []struct { tests := []struct {
provider KMSProvider provider Provider
panics bool panics bool
}{{ }{{
KMSProvider{ Provider{
UniqueID: "incomplete-provider", UniqueID: "incomplete-provider",
}, },
true, true,
}, { }, {
KMSProvider{ Provider{
UniqueID: "initializer-only", UniqueID: "initializer-only",
Initializer: noinitKMS, Initializer: noinitKMS,
}, },
@ -47,9 +47,9 @@ func TestRegisterKMSProvider(t *testing.T) {
for _, test := range tests { for _, test := range tests {
provider := test.provider provider := test.provider
if test.panics { if test.panics {
assert.Panics(t, func() { RegisterKMSProvider(provider) }) assert.Panics(t, func() { RegisterProvider(provider) })
} else { } 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. limitations under the License.
*/ */
package util package kms
import ( import (
"context" "context"
@ -36,9 +36,6 @@ const (
// Encryption passphrase location in K8s secrets. // Encryption passphrase location in K8s secrets.
encryptionPassphraseKey = "encryptionPassphrase" encryptionPassphraseKey = "encryptionPassphrase"
// Default KMS type.
defaultKMSType = "default"
// kmsTypeSecretsMetadata is the SecretsKMS with per-volume encryption, // kmsTypeSecretsMetadata is the SecretsKMS with per-volume encryption,
// where the DEK is stored in the metadata of the volume itself. // where the DEK is stored in the metadata of the volume itself.
kmsTypeSecretsMetadata = "metadata" kmsTypeSecretsMetadata = "metadata"
@ -53,16 +50,21 @@ const (
// SecretsKMS is default KMS implementation that means no KMS is in use. // SecretsKMS is default KMS implementation that means no KMS is in use.
type SecretsKMS struct { type SecretsKMS struct {
integratedDEK IntegratedDEK
passphrase string 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 // secret that is configured for the StorageClass. This KMS provider uses a
// single (LUKS) passhprase for all volumes. // single (LUKS) passhprase for all volumes.
func initSecretsKMS(secrets map[string]string) (EncryptionKMS, error) { func newSecretsKMS(args ProviderInitArgs) (EncryptionKMS, error) {
passphraseValue, ok := secrets[encryptionPassphraseKey] passphraseValue, ok := args.Secrets[encryptionPassphraseKey]
if !ok { if !ok {
return nil, errors.New("missing encryption passphrase in secrets") return nil, errors.New("missing encryption passphrase in secrets")
} }
@ -98,7 +100,7 @@ type SecretsMetadataKMS struct {
SecretsKMS SecretsKMS
} }
var _ = RegisterKMSProvider(KMSProvider{ var _ = RegisterProvider(Provider{
UniqueID: kmsTypeSecretsMetadata, UniqueID: kmsTypeSecretsMetadata,
Initializer: initSecretsMetadataKMS, Initializer: initSecretsMetadataKMS,
}) })
@ -106,7 +108,7 @@ var _ = RegisterKMSProvider(KMSProvider{
// initSecretsMetadataKMS initializes a SecretsMetadataKMS that wraps a SecretsKMS, // initSecretsMetadataKMS initializes a SecretsMetadataKMS that wraps a SecretsKMS,
// so that the passphrase from the user provided or StorageClass secrets can be used // 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. // for encrypting/decrypting DEKs that are stored in a detached DEKStore.
func initSecretsMetadataKMS(args KMSInitializerArgs) (EncryptionKMS, error) { func initSecretsMetadataKMS(args ProviderInitArgs) (EncryptionKMS, error) {
var ( var (
smKMS SecretsMetadataKMS smKMS SecretsMetadataKMS
encryptionPassphrase string encryptionPassphrase string
@ -179,7 +181,7 @@ func (kms SecretsMetadataKMS) Destroy() {
kms.SecretsKMS.Destroy() kms.SecretsKMS.Destroy()
} }
func (kms SecretsMetadataKMS) requiresDEKStore() DEKStoreType { func (kms SecretsMetadataKMS) RequiresDEKStore() DEKStoreType {
return DEKStoreMetadata return DEKStoreMetadata
} }

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package util package kms
import ( import (
"testing" "testing"
@ -23,6 +23,26 @@ import (
"github.com/stretchr/testify/require" "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) { func TestGenerateNonce(t *testing.T) {
t.Parallel() t.Parallel()
size := 64 size := 64
@ -44,7 +64,7 @@ func TestGenerateCipher(t *testing.T) {
func TestInitSecretsMetadataKMS(t *testing.T) { func TestInitSecretsMetadataKMS(t *testing.T) {
t.Parallel() t.Parallel()
args := KMSInitializerArgs{ args := ProviderInitArgs{
Tenant: "tenant", Tenant: "tenant",
Config: nil, Config: nil,
Secrets: map[string]string{}, Secrets: map[string]string{},
@ -61,7 +81,7 @@ func TestInitSecretsMetadataKMS(t *testing.T) {
kms, err = initSecretsMetadataKMS(args) kms, err = initSecretsMetadataKMS(args)
assert.NoError(t, err) assert.NoError(t, err)
require.NotNil(t, kms) require.NotNil(t, kms)
assert.Equal(t, DEKStoreMetadata, kms.requiresDEKStore()) assert.Equal(t, DEKStoreMetadata, kms.RequiresDEKStore())
} }
func TestWorkflowSecretsMetadataKMS(t *testing.T) { func TestWorkflowSecretsMetadataKMS(t *testing.T) {
@ -69,7 +89,7 @@ func TestWorkflowSecretsMetadataKMS(t *testing.T) {
secrets := map[string]string{ secrets := map[string]string{
encryptionPassphraseKey: "my-passphrase-from-kubernetes", encryptionPassphraseKey: "my-passphrase-from-kubernetes",
} }
args := KMSInitializerArgs{ args := ProviderInitArgs{
Tenant: "tenant", Tenant: "tenant",
Config: nil, Config: nil,
Secrets: secrets, Secrets: secrets,
@ -81,9 +101,7 @@ func TestWorkflowSecretsMetadataKMS(t *testing.T) {
require.NotNil(t, kms) require.NotNil(t, kms)
// plainDEK is the (LUKS) passphrase for the volume // plainDEK is the (LUKS) passphrase for the volume
plainDEK, err := generateNewEncryptionPassphrase() plainDEK := "usually created with generateNewEncryptionPassphrase()"
assert.NoError(t, err)
assert.NotEqual(t, "", plainDEK)
encryptedDEK, err := kms.EncryptDEK(volumeID, plainDEK) encryptedDEK, err := kms.EncryptDEK(volumeID, plainDEK)
assert.NoError(t, err) assert.NoError(t, err)

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package util package kms
import ( import (
"errors" "errors"
@ -89,7 +89,7 @@ type vaultConnection struct {
type VaultKMS struct { type VaultKMS struct {
vaultConnection vaultConnection
integratedDEK IntegratedDEK
// vaultPassphrasePath (VPP) used to be added before the "key" of the // vaultPassphrasePath (VPP) used to be added before the "key" of the
// secret (like /v1/secret/data/<VPP>/key) // secret (like /v1/secret/data/<VPP>/key)
@ -317,13 +317,13 @@ func (vc *vaultConnection) getDeleteKeyContext() map[string]string {
return keyContext return keyContext
} }
var _ = RegisterKMSProvider(KMSProvider{ var _ = RegisterProvider(Provider{
UniqueID: kmsTypeVault, UniqueID: kmsTypeVault,
Initializer: initVaultKMS, Initializer: initVaultKMS,
}) })
// InitVaultKMS returns an interface to HashiCorp Vault KMS. // InitVaultKMS returns an interface to HashiCorp Vault KMS.
func initVaultKMS(args KMSInitializerArgs) (EncryptionKMS, error) { func initVaultKMS(args ProviderInitArgs) (EncryptionKMS, error) {
kms := &VaultKMS{} kms := &VaultKMS{}
err := kms.initConnection(args.Config) err := kms.initConnection(args.Config)
if err != nil { if err != nil {

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package util package kms
import ( import (
"context" "context"
@ -77,14 +77,14 @@ type VaultTenantSA struct {
saTokenDir string saTokenDir string
} }
var _ = RegisterKMSProvider(KMSProvider{ var _ = RegisterProvider(Provider{
UniqueID: kmsTypeVaultTenantSA, UniqueID: kmsTypeVaultTenantSA,
Initializer: initVaultTenantSA, Initializer: initVaultTenantSA,
}) })
// initVaultTenantSA returns an interface to HashiCorp Vault KMS where Tenants // initVaultTenantSA returns an interface to HashiCorp Vault KMS where Tenants
// use their ServiceAccount to access the service. // use their ServiceAccount to access the service.
func initVaultTenantSA(args KMSInitializerArgs) (EncryptionKMS, error) { func initVaultTenantSA(args ProviderInitArgs) (EncryptionKMS, error) {
var err error var err error
config := args.Config config := args.Config

View File

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

View File

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

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package util package kms
import ( import (
"context" "context"
@ -181,7 +181,7 @@ Example JSON structure in the KMS config is,
*/ */
type vaultTenantConnection struct { type vaultTenantConnection struct {
vaultConnection vaultConnection
integratedDEK IntegratedDEK
client *kubernetes.Clientset client *kubernetes.Clientset
@ -204,13 +204,13 @@ type VaultTokensKMS struct {
TokenName string TokenName string
} }
var _ = RegisterKMSProvider(KMSProvider{ var _ = RegisterProvider(Provider{
UniqueID: kmsTypeVaultTokens, UniqueID: kmsTypeVaultTokens,
Initializer: initVaultTokensKMS, Initializer: initVaultTokensKMS,
}) })
// InitVaultTokensKMS returns an interface to HashiCorp Vault KMS. // InitVaultTokensKMS returns an interface to HashiCorp Vault KMS.
func initVaultTokensKMS(args KMSInitializerArgs) (EncryptionKMS, error) { func initVaultTokensKMS(args ProviderInitArgs) (EncryptionKMS, error) {
var err error var err error
config := args.Config config := args.Config

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package util package kms
import ( import (
"encoding/json" "encoding/json"
@ -76,7 +76,7 @@ func TestInitVaultTokensKMS(t *testing.T) {
return return
} }
args := KMSInitializerArgs{ args := ProviderInitArgs{
Tenant: "bob", Tenant: "bob",
Config: make(map[string]interface{}), Config: make(map[string]interface{}),
Secrets: nil, Secrets: nil,

View File

@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"strings" "strings"
kmsapi "github.com/ceph/ceph-csi/internal/kms"
"github.com/ceph/ceph-csi/internal/util" "github.com/ceph/ceph-csi/internal/util"
"github.com/ceph/ceph-csi/internal/util/log" "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 // configureEncryption sets up the VolumeEncryption for this rbdImage. Once
// configured, use isEncrypted() to see if the volume supports encryption. // configured, use isEncrypted() to see if the volume supports encryption.
func (ri *rbdImage) configureEncryption(kmsID string, credentials map[string]string) error { 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 { if err != nil {
return err return err
} }

View File

@ -26,6 +26,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/ceph/ceph-csi/internal/kms"
"github.com/ceph/ceph-csi/internal/util/log" "github.com/ceph/ceph-csi/internal/util/log"
) )
@ -33,9 +34,6 @@ const (
mapperFilePrefix = "luks-rbd-" mapperFilePrefix = "luks-rbd-"
mapperFilePathPrefix = "/dev/mapper" 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: // Passphrase size - 20 bytes is 160 bits to satisfy:
// https://tools.ietf.org/html/rfc6749#section-10.10 // https://tools.ietf.org/html/rfc6749#section-10.10
encryptionPassphraseSize = 20 encryptionPassphraseSize = 20
@ -54,11 +52,11 @@ var (
) )
type VolumeEncryption struct { type VolumeEncryption struct {
KMS EncryptionKMS KMS kms.EncryptionKMS
// dekStore that will be used, this can be the EncryptionKMS or a // dekStore that will be used, this can be the EncryptionKMS or a
// different object implementing the DEKStore interface. // different object implementing the DEKStore interface.
dekStore DEKStore dekStore kms.DEKStore
id string id string
} }
@ -76,7 +74,7 @@ func FetchEncryptionKMSID(encrypted, kmsID string) (string, error) {
} }
if kmsID == "" { if kmsID == "" {
kmsID = defaultKMSType kmsID = kms.DefaultKMSType
} }
return kmsID, nil return kmsID, nil
@ -88,24 +86,24 @@ func FetchEncryptionKMSID(encrypted, kmsID string) (string, error) {
// Callers that receive a ErrDEKStoreNeeded error, should use // Callers that receive a ErrDEKStoreNeeded error, should use
// VolumeEncryption.SetDEKStore() to configure an alternative storage for the // VolumeEncryption.SetDEKStore() to configure an alternative storage for the
// DEKs. // DEKs.
func NewVolumeEncryption(id string, kms EncryptionKMS) (*VolumeEncryption, error) { func NewVolumeEncryption(id string, ekms kms.EncryptionKMS) (*VolumeEncryption, error) {
kmsID := id kmsID := id
if kmsID == "" { if kmsID == "" {
// if kmsID is not set, encryption is enabled, and the type is // if kmsID is not set, encryption is enabled, and the type is
// SecretsKMS // SecretsKMS
kmsID = defaultKMSType kmsID = kms.DefaultKMSType
} }
ve := &VolumeEncryption{ ve := &VolumeEncryption{
id: kmsID, id: kmsID,
KMS: kms, KMS: ekms,
} }
if kms.requiresDEKStore() == DEKStoreIntegrated { if ekms.RequiresDEKStore() == kms.DEKStoreIntegrated {
dekStore, ok := kms.(DEKStore) dekStore, ok := ekms.(kms.DEKStore)
if !ok { if !ok {
return nil, fmt.Errorf("KMS %T does not implement the "+ return nil, fmt.Errorf("KMS %T does not implement the "+
"DEKStore interface", kms) "DEKStore interface", ekms)
} }
ve.dekStore = dekStore 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 // SetDEKStore sets the DEKStore for this VolumeEncryption instance. It will be
// used when StoreNewCryptoPassphrase() or RemoveDEK() is called. // used when StoreNewCryptoPassphrase() or RemoveDEK() is called.
func (ve *VolumeEncryption) SetDEKStore(dekStore DEKStore) { func (ve *VolumeEncryption) SetDEKStore(dekStore kms.DEKStore) {
ve.dekStore = dekStore ve.dekStore = dekStore
} }
@ -141,72 +139,6 @@ func (ve *VolumeEncryption) GetID() string {
return ve.id 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 // StoreCryptoPassphrase takes an unencrypted passphrase, encrypts it and saves
// it in the DEKStore. // it in the DEKStore.
func (ve *VolumeEncryption) StoreCryptoPassphrase(volumeID, passphrase string) error { func (ve *VolumeEncryption) StoreCryptoPassphrase(volumeID, passphrase string) error {

View File

@ -20,26 +20,12 @@ import (
"encoding/base64" "encoding/base64"
"testing" "testing"
"github.com/ceph/ceph-csi/internal/kms"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "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) { func TestGenerateNewEncryptionPassphrase(t *testing.T) {
t.Parallel() t.Parallel()
b64Passphrase, err := generateNewEncryptionPassphrase() b64Passphrase, err := generateNewEncryptionPassphrase()
@ -55,17 +41,18 @@ func TestGenerateNewEncryptionPassphrase(t *testing.T) {
func TestKMSWorkflow(t *testing.T) { func TestKMSWorkflow(t *testing.T) {
t.Parallel() t.Parallel()
secrets := map[string]string{ 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) assert.NoError(t, err)
require.NotNil(t, kms) require.NotNil(t, kmsProvider)
ve, err := NewVolumeEncryption("", kms) ve, err := NewVolumeEncryption("", kmsProvider)
assert.NoError(t, err) assert.NoError(t, err)
require.NotNil(t, ve) require.NotNil(t, ve)
assert.Equal(t, defaultKMSType, ve.GetID()) assert.Equal(t, kms.DefaultKMSType, ve.GetID())
volumeID := "volume-id" volumeID := "volume-id"
@ -74,5 +61,5 @@ func TestKMSWorkflow(t *testing.T) {
passphrase, err := ve.GetCryptoPassphrase(volumeID) passphrase, err := ve.GetCryptoPassphrase(volumeID)
assert.NoError(t, err) 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 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 // CallStack returns the stack of the calls in the current goroutine. Useful
// for debugging or reporting errors. This is a friendly alternative to // for debugging or reporting errors. This is a friendly alternative to
// assert() or panic(). // assert() or panic().