mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-11-10 00:10:20 +00:00
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:
parent
778b5e86de
commit
4a3b1181ce
@ -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
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
package kms
|
||||
|
||||
import (
|
||||
"testing"
|
@ -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
|
||||
}
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
@ -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 {
|
@ -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
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
package kms
|
||||
|
||||
import (
|
||||
"errors"
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
package kms
|
||||
|
||||
import (
|
||||
"errors"
|
@ -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
|
@ -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,
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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().
|
||||
|
Loading…
Reference in New Issue
Block a user