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) { func buildCreateVolumeResponse(ctx context.Context, req *csi.CreateVolumeRequest, rbdVol *rbdVolume) (*csi.CreateVolumeResponse, error) {
if rbdVol.Encrypted { if rbdVol.isEncrypted() {
err := rbdVol.setupEncryption(ctx) err := rbdVol.setupEncryption(ctx)
if err != nil { if err != nil {
util.ErrorLog(ctx, err.Error()) util.ErrorLog(ctx, err.Error())
@ -516,7 +516,7 @@ func (cs *ControllerServer) createBackingImage(ctx context.Context, cr *util.Cre
return err return err
} }
} }
if rbdVol.Encrypted { if rbdVol.isEncrypted() {
err = rbdVol.setupEncryption(ctx) err = rbdVol.setupEncryption(ctx)
if err != nil { if err != nil {
util.ErrorLog(ctx, "failed to setup encroption for image %s: %v", rbdVol, err) 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()) return nil, status.Error(codes.Internal, err.Error())
} }
if rbdVol.Encrypted { if rbdVol.isEncrypted() {
if err = rbdVol.KMS.DeletePassphrase(rbdVol.VolID); err != nil { 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) 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 // 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, "+ return nil, status.Errorf(codes.Unimplemented, "source Volume %s is encrypted, "+
"snapshotting is not supported currently", rbdVol.VolID) "snapshotting is not supported currently", rbdVol.VolID)
} }
@ -1124,7 +1124,7 @@ func (cs *ControllerServer) ControllerExpandVolume(ctx context.Context, req *csi
return nil, err return nil, err
} }
if rbdVol.Encrypted { if rbdVol.isEncrypted() {
return nil, status.Errorf(codes.InvalidArgument, "encrypted volumes do not support resize (%s)", return nil, status.Errorf(codes.InvalidArgument, "encrypted volumes do not support resize (%s)",
rbdVol) rbdVol)
} }

View File

@ -81,11 +81,16 @@ func (rv *rbdVolume) ensureEncryptionMetadataSet(status rbdEncryptionState) erro
return nil 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: // 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 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. // - the RBD image will be marked to support encryption in its metadata.
func (rv *rbdVolume) setupEncryption(ctx context.Context) error { 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 { if err != nil {
util.ErrorLog(ctx, "failed to save encryption passphrase for "+ util.ErrorLog(ctx, "failed to save encryption passphrase for "+
"image %s: %s", rv.String(), err) "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 { 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 { if err != nil {
util.ErrorLog(ctx, "failed to get crypto passphrase for %s: %v", util.ErrorLog(ctx, "failed to get crypto passphrase for %s: %v",
rv.String(), err) 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) { 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 { if err != nil {
util.ErrorLog(ctx, "failed to get passphrase for encrypted device %s: %v", util.ErrorLog(ctx, "failed to get passphrase for encrypted device %s: %v",
rv.String(), err) rv.String(), err)
@ -175,21 +180,29 @@ func (rv *rbdVolume) initKMS(ctx context.Context, volOptions, credentials map[st
return nil return nil
} }
rv.Encrypted, err = strconv.ParseBool(encrypted) isEncrypted, err := strconv.ParseBool(encrypted)
if err != nil { if err != nil {
return fmt.Errorf( return fmt.Errorf(
"invalid value set in 'encrypted': %s (should be \"true\" or \"false\")", encrypted) "invalid value set in 'encrypted': %s (should be \"true\" or \"false\")", encrypted)
} else if !rv.Encrypted { } else if !isEncrypted {
return nil return nil
} }
// deliberately ignore if parsing failed as GetKMS will return default err = rv.setKMS(volOptions["encryptionKMSID"], credentials)
// implementation of kmsID is empty
kmsID := volOptions["encryptionKMSID"]
rv.KMS, err = util.GetKMS(rv.Owner, kmsID, credentials)
if err != nil { if err != nil {
return fmt.Errorf("invalid encryption kms configuration: %w", err) return fmt.Errorf("invalid encryption kms configuration: %w", err)
} }
return nil 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", util.DebugLog(ctx, "rbd image: %s/%s was successfully mapped at %s\n",
req.GetVolumeId(), volOptions.Pool, devicePath) req.GetVolumeId(), volOptions.Pool, devicePath)
if volOptions.Encrypted { if volOptions.isEncrypted() {
devicePath, err = ns.processEncryptedDevice(ctx, volOptions, devicePath) devicePath, err = ns.processEncryptedDevice(ctx, volOptions, devicePath)
if err != nil { if err != nil {
return transaction, err 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) util.WarningLog(ctx, "rbd: map error %v, rbd output: %s", err, stderr)
// unmap rbd image if connection timeout // unmap rbd image if connection timeout
if strings.Contains(err.Error(), rbdMapConnectionTimeout) { 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 { if detErr != nil {
util.WarningLog(ctx, "rbd: %s unmap error %v", imagePath, detErr) 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 := "" kmsID := ""
if rv.Encrypted { if rv.isEncrypted() {
kmsID = rv.KMS.GetID() kmsID = rv.encryption.KMS.GetID()
} }
j, err := volJournal.Connect(rv.Monitors, rv.RadosNamespace, rv.conn.Creds) 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 := "" kmsID := ""
if rbdVol.Encrypted { if rbdVol.isEncrypted() {
kmsID = rbdVol.KMS.GetID() kmsID = rbdVol.encryption.KMS.GetID()
} }
j, err := volJournal.Connect(rbdVol.Monitors, rbdVol.RadosNamespace, cr) j, err := volJournal.Connect(rbdVol.Monitors, rbdVol.RadosNamespace, cr)

View File

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

View File

@ -51,6 +51,22 @@ const (
defaultConfigMapToRead = "csi-kms-connection-details" 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 // EncryptionKMS provides external Key Management System for encryption
// passphrases storage. // passphrases storage.
type EncryptionKMS interface { type EncryptionKMS interface {

View File

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