util: introduce VolumeEncryption type

Prepare for grouping encryption related functions together. The main
rbdVolume object should not be cluttered with KMS or DEK procedures.

Signed-off-by: Niels de Vos <ndevos@redhat.com>
This commit is contained in:
Niels de Vos 2021-02-22 16:25:35 +01:00 committed by mergify[bot]
parent aa52afff09
commit d4076d6216
8 changed files with 61 additions and 39 deletions

View File

@ -153,7 +153,7 @@ func (cs *ControllerServer) parseVolCreateRequest(ctx context.Context, req *csi.
}
func buildCreateVolumeResponse(ctx context.Context, req *csi.CreateVolumeRequest, rbdVol *rbdVolume) (*csi.CreateVolumeResponse, error) {
if rbdVol.Encrypted {
if rbdVol.isEncrypted() {
err := rbdVol.setupEncryption(ctx)
if err != nil {
util.ErrorLog(ctx, err.Error())
@ -516,7 +516,7 @@ func (cs *ControllerServer) createBackingImage(ctx context.Context, cr *util.Cre
return err
}
}
if rbdVol.Encrypted {
if rbdVol.isEncrypted() {
err = rbdVol.setupEncryption(ctx)
if err != nil {
util.ErrorLog(ctx, "failed to setup encroption for image %s: %v", rbdVol, err)
@ -690,8 +690,8 @@ func (cs *ControllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol
return nil, status.Error(codes.Internal, err.Error())
}
if rbdVol.Encrypted {
if err = rbdVol.KMS.DeletePassphrase(rbdVol.VolID); err != nil {
if rbdVol.isEncrypted() {
if err = rbdVol.encryption.KMS.DeletePassphrase(rbdVol.VolID); err != nil {
util.WarningLog(ctx, "failed to clean the passphrase for volume %s: %s", rbdVol.VolID, err)
}
}
@ -755,7 +755,7 @@ func (cs *ControllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateS
}
// TODO: re-encrypt snapshot with a new passphrase
if rbdVol.Encrypted {
if rbdVol.isEncrypted() {
return nil, status.Errorf(codes.Unimplemented, "source Volume %s is encrypted, "+
"snapshotting is not supported currently", rbdVol.VolID)
}
@ -1124,7 +1124,7 @@ func (cs *ControllerServer) ControllerExpandVolume(ctx context.Context, req *csi
return nil, err
}
if rbdVol.Encrypted {
if rbdVol.isEncrypted() {
return nil, status.Errorf(codes.InvalidArgument, "encrypted volumes do not support resize (%s)",
rbdVol)
}

View File

@ -81,11 +81,16 @@ func (rv *rbdVolume) ensureEncryptionMetadataSet(status rbdEncryptionState) erro
return nil
}
// isEncrypted returns `true` if the rbdVolume is (or needs to be) encrypted.
func (rv *rbdVolume) isEncrypted() bool {
return rv.encryption != nil
}
// setupEncryption configures the metadata of the RBD image for encryption:
// - the Data-Encryption-Key (DEK) will be generated stored for use by the KMS;
// - the RBD image will be marked to support encryption in its metadata.
func (rv *rbdVolume) setupEncryption(ctx context.Context) error {
err := util.StoreNewCryptoPassphrase(rv.VolID, rv.KMS)
err := util.StoreNewCryptoPassphrase(rv.VolID, rv.encryption.KMS)
if err != nil {
util.ErrorLog(ctx, "failed to save encryption passphrase for "+
"image %s: %s", rv.String(), err)
@ -103,7 +108,7 @@ func (rv *rbdVolume) setupEncryption(ctx context.Context) error {
}
func (rv *rbdVolume) encryptDevice(ctx context.Context, devicePath string) error {
passphrase, err := util.GetCryptoPassphrase(rv.VolID, rv.KMS)
passphrase, err := util.GetCryptoPassphrase(rv.VolID, rv.encryption.KMS)
if err != nil {
util.ErrorLog(ctx, "failed to get crypto passphrase for %s: %v",
rv.String(), err)
@ -126,7 +131,7 @@ func (rv *rbdVolume) encryptDevice(ctx context.Context, devicePath string) error
}
func (rv *rbdVolume) openEncryptedDevice(ctx context.Context, devicePath string) (string, error) {
passphrase, err := util.GetCryptoPassphrase(rv.VolID, rv.KMS)
passphrase, err := util.GetCryptoPassphrase(rv.VolID, rv.encryption.KMS)
if err != nil {
util.ErrorLog(ctx, "failed to get passphrase for encrypted device %s: %v",
rv.String(), err)
@ -175,21 +180,29 @@ func (rv *rbdVolume) initKMS(ctx context.Context, volOptions, credentials map[st
return nil
}
rv.Encrypted, err = strconv.ParseBool(encrypted)
isEncrypted, err := strconv.ParseBool(encrypted)
if err != nil {
return fmt.Errorf(
"invalid value set in 'encrypted': %s (should be \"true\" or \"false\")", encrypted)
} else if !rv.Encrypted {
} else if !isEncrypted {
return nil
}
// deliberately ignore if parsing failed as GetKMS will return default
// implementation of kmsID is empty
kmsID := volOptions["encryptionKMSID"]
rv.KMS, err = util.GetKMS(rv.Owner, kmsID, credentials)
err = rv.setKMS(volOptions["encryptionKMSID"], credentials)
if err != nil {
return fmt.Errorf("invalid encryption kms configuration: %w", err)
}
return nil
}
func (rv *rbdVolume) setKMS(kmsID string, credentials map[string]string) error {
kms, err := util.GetKMS(rv.Owner, kmsID, credentials)
if err != nil {
return err
}
rv.encryption = &util.VolumeEncryption{KMS: kms}
return nil
}

View File

@ -291,7 +291,7 @@ func (ns *NodeServer) stageTransaction(ctx context.Context, req *csi.NodeStageVo
util.DebugLog(ctx, "rbd image: %s/%s was successfully mapped at %s\n",
req.GetVolumeId(), volOptions.Pool, devicePath)
if volOptions.Encrypted {
if volOptions.isEncrypted() {
devicePath, err = ns.processEncryptedDevice(ctx, volOptions, devicePath)
if err != nil {
return transaction, err

View File

@ -270,7 +270,7 @@ func createPath(ctx context.Context, volOpt *rbdVolume, cr *util.Credentials) (s
util.WarningLog(ctx, "rbd: map error %v, rbd output: %s", err, stderr)
// unmap rbd image if connection timeout
if strings.Contains(err.Error(), rbdMapConnectionTimeout) {
detErr := detachRBDImageOrDeviceSpec(ctx, imagePath, true, isNbd, volOpt.Encrypted, volOpt.VolID, volOpt.UnmapOptions)
detErr := detachRBDImageOrDeviceSpec(ctx, imagePath, true, isNbd, volOpt.isEncrypted(), volOpt.VolID, volOpt.UnmapOptions)
if detErr != nil {
util.WarningLog(ctx, "rbd: %s unmap error %v", imagePath, detErr)
}

View File

@ -233,8 +233,8 @@ func (rv *rbdVolume) Exists(ctx context.Context, parentVol *rbdVolume) (bool, er
}
kmsID := ""
if rv.Encrypted {
kmsID = rv.KMS.GetID()
if rv.isEncrypted() {
kmsID = rv.encryption.KMS.GetID()
}
j, err := volJournal.Connect(rv.Monitors, rv.RadosNamespace, rv.conn.Creds)
@ -410,8 +410,8 @@ func reserveVol(ctx context.Context, rbdVol *rbdVolume, rbdSnap *rbdSnapshot, cr
}
kmsID := ""
if rbdVol.Encrypted {
kmsID = rbdVol.KMS.GetID()
if rbdVol.isEncrypted() {
kmsID = rbdVol.encryption.KMS.GetID()
}
j, err := volJournal.Connect(rbdVol.Monitors, rbdVol.RadosNamespace, cr)

View File

@ -98,11 +98,10 @@ type rbdVolume struct {
MonValueFromSecret string `json:"monValueFromSecret"`
VolSize int64 `json:"volSize"`
DisableInUseChecks bool `json:"disableInUseChecks"`
Encrypted bool
readOnly bool
Primary bool
ThickProvision bool
KMS util.EncryptionKMS
encryption *util.VolumeEncryption
// Owner is the creator (tenant, Kubernetes Namespace) of the volume.
Owner string
CreatedAt *timestamp.Timestamp
@ -167,8 +166,8 @@ func (rv *rbdVolume) Destroy() {
if rv.conn != nil {
rv.conn.Destroy()
}
if rv.KMS != nil {
rv.KMS.Destroy()
if rv.isEncrypted() {
rv.encryption.Destroy()
}
}
@ -834,8 +833,7 @@ func genVolFromVolID(ctx context.Context, volumeID string, cr *util.Credentials,
rbdVol.Owner = imageAttributes.Owner
if imageAttributes.KmsID != "" {
rbdVol.Encrypted = true
rbdVol.KMS, err = util.GetKMS(rbdVol.Owner, imageAttributes.KmsID, secrets)
err = rbdVol.setKMS(imageAttributes.KmsID, secrets)
if err != nil {
return rbdVol, err
}
@ -1168,7 +1166,7 @@ func stashRBDImageMetadata(volOptions *rbdVolume, path string) error {
Pool: volOptions.Pool,
RadosNamespace: volOptions.RadosNamespace,
ImageName: volOptions.RbdImageName,
Encrypted: volOptions.Encrypted,
Encrypted: volOptions.isEncrypted(),
UnmapOptions: volOptions.UnmapOptions,
}

View File

@ -51,6 +51,22 @@ const (
defaultConfigMapToRead = "csi-kms-connection-details"
)
type VolumeEncryption struct {
KMS EncryptionKMS
}
// NewVolumeEncryption creates a new instance of VolumeEncryption.
func NewVolumeEncryption(kms EncryptionKMS) (*VolumeEncryption, error) {
ve := &VolumeEncryption{KMS: kms}
return ve, nil
}
// Destroy frees any resources that the VolumeEncryption instance allocated.
func (ve *VolumeEncryption) Destroy() {
ve.KMS.Destroy()
}
// EncryptionKMS provides external Key Management System for encryption
// passphrases storage.
type EncryptionKMS interface {

View File

@ -18,7 +18,6 @@ package util
import (
"errors"
"fmt"
)
const (
@ -27,10 +26,6 @@ const (
// 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"
)
// SecretsKMS is default KMS implementation that means no KMS is in use.
@ -59,19 +54,19 @@ func (kms SecretsKMS) Destroy() {
// nothing to do
}
// FetchDEK returns passphrase from Kubernetes secrets.
func (kms SecretsKMS) FetchDEK(key string) (string, error) {
// GetPassphrase returns passphrase from Kubernetes secrets.
func (kms SecretsKMS) GetPassphrase(key string) (string, error) {
return kms.passphrase, nil
}
// StoreDEK does nothing, as there is no passphrase per key (volume), so
// SavePassphrase does nothing, as there is no passphrase per key (volume), so
// no need to store is anywhere.
func (kms SecretsKMS) StoreDEK(key, value string) error {
func (kms SecretsKMS) SavePassphrase(key, value string) error {
return nil
}
// RemoveDEK is doing nothing as no new passphrases are saved with
// DeletePassphrase is doing nothing as no new passphrases are saved with
// SecretsKMS.
func (kms SecretsKMS) RemoveDEK(key string) error {
func (kms SecretsKMS) DeletePassphrase(key string) error {
return nil
}