Skip to content

Commit

Permalink
feat(crypto): add a suffix for v2 volume encryption
Browse files Browse the repository at this point in the history
when opening and closing a luks2 format device.

ref: longhorn/longhorn 7355

Signed-off-by: James Lu <james.lu@suse.com>
  • Loading branch information
mantissahz committed Jan 8, 2025
1 parent c5880dc commit 76e1361
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 29 deletions.
35 changes: 24 additions & 11 deletions csi/crypto/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ import (

lhns "github.com/longhorn/go-common-libs/ns"
lhtypes "github.com/longhorn/go-common-libs/types"
longhorn "github.com/longhorn/longhorn-manager/k8s/pkg/apis/longhorn/v1beta2"
)

const (
mapperFilePathPrefix = "/dev/mapper"
mapperV2VolumeSuffix = "-encrypted"

CryptoKeyDefaultCipher = "aes-xts-plain64"
CryptoKeyDefaultHash = "sha256"
Expand Down Expand Up @@ -69,7 +71,10 @@ func (cp *EncryptParams) GetPBKDF() string {
}

// VolumeMapper returns the path for mapped encrypted device.
func VolumeMapper(volume string) string {
func VolumeMapper(volume, dataEngine string) string {
if dataEngine == string(longhorn.DataEngineTypeV2) {
return path.Join(mapperFilePathPrefix, volume+mapperV2VolumeSuffix)
}
return path.Join(mapperFilePathPrefix, volume)
}

Expand All @@ -95,9 +100,9 @@ func EncryptVolume(devicePath, passphrase string, cryptoParams *EncryptParams) e
}

// OpenVolume opens volume so that it can be used by the client.
func OpenVolume(volume, devicePath, passphrase string) error {
if isOpen, _ := IsDeviceOpen(VolumeMapper(volume)); isOpen {
logrus.Infof("Device %s is already opened at %s", devicePath, VolumeMapper(volume))
func OpenVolume(volume, dataEngine, devicePath, passphrase string) error {
if isOpen, _ := IsDeviceOpen(VolumeMapper(volume, dataEngine)); isOpen {
logrus.Infof("Device %s is already opened at %s", devicePath, VolumeMapper(volume, dataEngine))
return nil
}

Expand All @@ -107,29 +112,37 @@ func OpenVolume(volume, devicePath, passphrase string) error {
return err
}

logrus.Infof("Opening device %s with LUKS on %s", devicePath, volume)
_, err = nsexec.LuksOpen(volume, devicePath, passphrase, lhtypes.LuksTimeout)
encryptVolumeName := volume
if dataEngine == string(longhorn.DataEngineTypeV2) {
encryptVolumeName = volume + mapperV2VolumeSuffix
}
logrus.Infof("Opening device %s with LUKS on %s", devicePath, encryptVolumeName)
_, err = nsexec.LuksOpen(encryptVolumeName, devicePath, passphrase, lhtypes.LuksTimeout)
if err != nil {
logrus.WithError(err).Warnf("Failed to open LUKS device %s", devicePath)
}
return err
}

// CloseVolume closes encrypted volume so it can be detached.
func CloseVolume(volume string) error {
func CloseVolume(volume, dataEngine string) error {
namespaces := []lhtypes.Namespace{lhtypes.NamespaceMnt, lhtypes.NamespaceIpc}
nsexec, err := lhns.NewNamespaceExecutor(lhtypes.ProcessNone, lhtypes.HostProcDirectory, namespaces)
if err != nil {
return err
}

logrus.Infof("Closing LUKS device %s", volume)
_, err = nsexec.LuksClose(volume, lhtypes.LuksTimeout)
encryptVolumeName := volume
if dataEngine == string(longhorn.DataEngineTypeV2) {
encryptVolumeName = volume + mapperV2VolumeSuffix
}
logrus.Infof("Closing LUKS device %s", encryptVolumeName)
_, err = nsexec.LuksClose(encryptVolumeName, lhtypes.LuksTimeout)
return err
}

func ResizeEncryptoDevice(volume, passphrase string) error {
if isOpen, err := IsDeviceOpen(VolumeMapper(volume)); err != nil {
func ResizeEncryptoDevice(volume, dataEngine, passphrase string) error {
if isOpen, err := IsDeviceOpen(VolumeMapper(volume, dataEngine)); err != nil {
return err
} else if !isOpen {
return fmt.Errorf("volume %v encrypto device is closed for resizing", volume)
Expand Down
39 changes: 21 additions & 18 deletions csi/node_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,8 @@ func (ns *NodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol
return nil, status.Errorf(codes.Internal, "failed to evaluate device filesystem %v format: %v", devicePath, err)
}

log.Infof("Volume %v device %v contains filesystem of format %v", volumeID, devicePath, diskFormat)
dataEngine := volume.DataEngine
log.Infof("Volume %v (%v) device %v contains filesystem of format %v", volumeID, dataEngine, devicePath, diskFormat)

if volume.Encrypted {
secrets := req.GetSecrets()
Expand All @@ -515,7 +516,7 @@ func (ns *NodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol
}
}

cryptoDevice := crypto.VolumeMapper(volumeID)
cryptoDevice := crypto.VolumeMapper(volumeID, dataEngine)
log.Infof("Volume %s requires crypto device %s", volumeID, cryptoDevice)

// check if the crypto device is open at the null path.
Expand All @@ -525,12 +526,12 @@ func (ns *NodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol
return nil, status.Errorf(codes.Internal, "failed to check if the crypto device %s for volume %s is mapped to the null path: %v", cryptoDevice, volumeID, err.Error())
} else if mappedToNullPath {
log.Warnf("Closing active crypto device %s for volume %s since the volume is not closed properly before", cryptoDevice, volumeID)
if err := crypto.CloseVolume(volumeID); err != nil {
if err := crypto.CloseVolume(volumeID, dataEngine); err != nil {
return nil, status.Errorf(codes.Internal, "failed to close active crypto device %s for volume %s: %v ", cryptoDevice, volumeID, err.Error())
}
}

if err := crypto.OpenVolume(volumeID, devicePath, passphrase); err != nil {
if err := crypto.OpenVolume(volumeID, dataEngine, devicePath, passphrase); err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

Expand Down Expand Up @@ -628,19 +629,20 @@ func (ns *NodeServer) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstag
// optionally try to retrieve the volume and check if it's an RWX volume
// if it is we let the share-manager clean up the crypto device
volume, _ := ns.apiClient.Volume.ById(volumeID)
if volume == nil || types.IsDataEngineV1(longhorn.DataEngineType(volume.DataEngine)) {
// Currently, only "RWO v1 volumes" and "block device with v1 volume.Migratable is true" supports encryption.
sharedAccess := requiresSharedAccess(volume, nil)
cleanupCryptoDevice := !sharedAccess || (sharedAccess && volume.Migratable)
if cleanupCryptoDevice {
cryptoDevice := crypto.VolumeMapper(volumeID)
if isOpen, err := crypto.IsDeviceOpen(cryptoDevice); err != nil {
dataEngine := string(longhorn.DataEngineTypeV1)
if volume != nil {
dataEngine = volume.DataEngine
}
sharedAccess := requiresSharedAccess(volume, nil)
cleanupCryptoDevice := !sharedAccess || (sharedAccess && volume.Migratable)
if cleanupCryptoDevice {
cryptoDevice := crypto.VolumeMapper(volumeID, dataEngine)
if isOpen, err := crypto.IsDeviceOpen(cryptoDevice); err != nil {
return nil, status.Error(codes.Internal, err.Error())
} else if isOpen {
log.Infof("Volume %s closing active crypto device %s", volumeID, cryptoDevice)
if err := crypto.CloseVolume(volumeID, volume.DataEngine); err != nil {
return nil, status.Error(codes.Internal, err.Error())
} else if isOpen {
log.Infof("Volume %s closing active crypto device %s", volumeID, cryptoDevice)
if err := crypto.CloseVolume(volumeID); err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
}
}
}
Expand Down Expand Up @@ -820,14 +822,15 @@ func (ns *NodeServer) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandV
return nil, fmt.Errorf("unknown filesystem type for volume %v node expansion", volumeID)
}

dataEngine := volume.DataEngine
devicePath, err = func() (string, error) {
if !volume.Encrypted {
return devicePath, nil
}
if diskFormat != "crypto_LUKS" {
return "", status.Errorf(codes.InvalidArgument, "unsupported disk encryption format %v", diskFormat)
}
devicePath = crypto.VolumeMapper(volumeID)
devicePath = crypto.VolumeMapper(volumeID, dataEngine)

// Need to enable feature gate in v1.25:
// https://github.com/kubernetes/enhancements/issues/3107
Expand All @@ -847,7 +850,7 @@ func (ns *NodeServer) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandV
}

// blindly resize the encrypto device
if err := crypto.ResizeEncryptoDevice(volumeID, passphrase); err != nil {
if err := crypto.ResizeEncryptoDevice(volumeID, dataEngine, passphrase); err != nil {
return "", status.Errorf(codes.InvalidArgument, "failed to resize crypto device %v for volume %v node expansion: %v", devicePath, volumeID, err)
}

Expand Down

0 comments on commit 76e1361

Please sign in to comment.