From 9aa843a5dcc4e441327fae019590cffdade140a1 Mon Sep 17 00:00:00 2001 From: James Lu Date: Fri, 10 Jan 2025 20:03:06 +0800 Subject: [PATCH] feat(crypto): v2 volume encryption support ref: longhorn/longhorn 7355 Signed-off-by: James Lu --- app/cmd/server.go | 13 +++++++++++++ pkg/crypto/crypto.go | 28 ++++++++++++++++------------ pkg/rpc/server.go | 8 ++++---- pkg/server/share_manager.go | 10 +++++----- pkg/types/types.go | 20 +++++++++++++++++++- pkg/volume/volume.go | 1 + 6 files changed, 58 insertions(+), 22 deletions(-) diff --git a/app/cmd/server.go b/app/cmd/server.go index d649fe52..6d903f0c 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -1,6 +1,7 @@ package cmd import ( + "fmt" "net" "os" "os/signal" @@ -16,6 +17,7 @@ import ( "github.com/longhorn/longhorn-share-manager/pkg/rpc" "github.com/longhorn/longhorn-share-manager/pkg/server" + "github.com/longhorn/longhorn-share-manager/pkg/types" "github.com/longhorn/longhorn-share-manager/pkg/util" "github.com/longhorn/longhorn-share-manager/pkg/volume" ) @@ -33,6 +35,11 @@ func ServerCmd() cli.Command { Usage: "The volume to export via the nfs server", Required: true, }, + cli.StringFlag{ + Name: "dataEngine", + Usage: "The volume data engine", + Required: true, + }, cli.BoolFlag{ Name: "encrypted", Usage: "signals that a volume is encrypted", @@ -84,6 +91,7 @@ func ServerCmd() cli.Command { Action: func(c *cli.Context) { vol := volume.Volume{ Name: c.String("volume"), + DataEngine: c.String("dataEngine"), Passphrase: c.String("passphrase"), CryptoKeyCipher: c.String("crytpokeycipher"), CryptoKeyHash: c.String("crytpokeyhash"), @@ -106,6 +114,11 @@ func ServerCmd() cli.Command { func start(vol volume.Volume) error { logger := util.NewLogger() + if vol.DataEngine != types.DataEngineTypeV1 && vol.DataEngine != types.DataEngineTypeV2 { + logger.Errorf("Invalid data engine value: %s", vol.DataEngine) + return fmt.Errorf("invalid data engine value: %s", vol.DataEngine) + } + manager, err := server.NewShareManager(logger, vol) if err != nil { return err diff --git a/pkg/crypto/crypto.go b/pkg/crypto/crypto.go index 213d20e7..f7e892b0 100644 --- a/pkg/crypto/crypto.go +++ b/pkg/crypto/crypto.go @@ -29,8 +29,9 @@ func EncryptVolume(devicePath, passphrase, keyCipher, keyHash, keySize, pbkdf st } // OpenVolume opens volume so that it can be used by the client. -func OpenVolume(volume, devicePath, passphrase string) error { - devPath := types.GetVolumeDevicePath(volume, true) +// devicePath is the path of the volume on the host that will be opened for instance '/dev/longhorn/volume1' +func OpenVolume(volume, dataEngine, devicePath, passphrase string) error { + devPath := types.GetVolumeDevicePath(volume, dataEngine, true) if isOpen, _ := IsDeviceOpen(devPath); isOpen { logrus.Debugf("Device %s is already opened at %s", devicePath, devPath) return nil @@ -42,33 +43,36 @@ func OpenVolume(volume, devicePath, passphrase string) error { return err } - logrus.Debugf("Opening device %s with LUKS on %s", devicePath, volume) - _, err = nsexec.LuksOpen(volume, devicePath, passphrase, lhtypes.LuksTimeout) + encryptedDevName := types.GetEncryptVolumeName(volume, dataEngine) + logrus.Debugf("Opening device %s with LUKS on %s", devicePath, encryptedDevName) + _, err = nsexec.LuksOpen(encryptedDevName, devicePath, passphrase, lhtypes.LuksTimeout) if err != nil { - logrus.WithError(err).Warnf("Failed to open LUKS device %s", devicePath) + logrus.WithError(err).Warnf("Failed to open LUKS device %s to %s", devicePath, encryptedDevName) } 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.Debugf("Closing LUKS device %s", volume) - _, err = nsexec.LuksClose(volume, lhtypes.LuksTimeout) + deviceName := types.GetEncryptVolumeName(volume, dataEngine) + logrus.Debugf("Closing LUKS device %s", deviceName) + _, err = nsexec.LuksClose(deviceName, lhtypes.LuksTimeout) return err } -func ResizeEncryptoDevice(volume, passphrase string) error { - devPath := types.GetVolumeDevicePath(volume, true) +func ResizeEncryptoDevice(volume, dataEngine, passphrase string) error { + // devPath is the full path of the encrypted device on the host that will be resized + devPath := types.GetVolumeDevicePath(volume, dataEngine, true) if isOpen, err := IsDeviceOpen(devPath); err != nil { return err } else if !isOpen { - return fmt.Errorf("volume %v encrypto device is closed for resizing", volume) + return fmt.Errorf("volume %v encrypto device %s is closed for resizing", volume, devPath) } namespaces := []lhtypes.Namespace{lhtypes.NamespaceMnt, lhtypes.NamespaceIpc} @@ -77,7 +81,7 @@ func ResizeEncryptoDevice(volume, passphrase string) error { return err } - _, err = nsexec.LuksResize(volume, passphrase, lhtypes.LuksTimeout) + _, err = nsexec.LuksResize(types.GetEncryptVolumeName(volume, dataEngine), passphrase, lhtypes.LuksTimeout) return err } diff --git a/pkg/rpc/server.go b/pkg/rpc/server.go index d9798137..fd108ea7 100644 --- a/pkg/rpc/server.go +++ b/pkg/rpc/server.go @@ -69,7 +69,7 @@ func (s *ShareManagerServer) FilesystemTrim(ctx context.Context, req *smrpc.File } }() - devicePath := types.GetVolumeDevicePath(vol.Name, req.EncryptedDevice) + devicePath := types.GetVolumeDevicePath(vol.Name, vol.DataEngine, req.EncryptedDevice) if !volume.CheckDeviceValid(devicePath) { return &emptypb.Empty{}, grpcstatus.Errorf(grpccodes.FailedPrecondition, "volume %v is not valid", vol.Name) } @@ -134,7 +134,7 @@ func (s *ShareManagerServer) FilesystemResize(ctx context.Context, req *emptypb. } }() - devicePath := types.GetVolumeDevicePath(vol.Name, vol.IsEncrypted()) + devicePath := types.GetVolumeDevicePath(vol.Name, vol.DataEngine, vol.IsEncrypted()) if !volume.CheckDeviceValid(devicePath) { return &emptypb.Empty{}, grpcstatus.Errorf(grpccodes.FailedPrecondition, "volume %v is not valid", vol.Name) } @@ -161,7 +161,7 @@ func (s *ShareManagerServer) FilesystemResize(ctx context.Context, req *emptypb. return &emptypb.Empty{}, grpcstatus.Errorf(grpccodes.InvalidArgument, "unsupported disk encryption format %v", diskFormat) } - if err = crypto.ResizeEncryptoDevice(vol.Name, vol.Passphrase); err != nil { + if err = crypto.ResizeEncryptoDevice(vol.Name, vol.DataEngine, vol.Passphrase); err != nil { return &emptypb.Empty{}, grpcstatus.Errorf(grpccodes.Internal, "failed to resize crypto device %v for volume %v node expansion: %v", devicePath, vol.Name, err) } } @@ -309,7 +309,7 @@ func (s *ShareManagerServer) Mount(ctx context.Context, req *emptypb.Empty) (res log.Info("Mounting and exporting volume") - devicePath := types.GetVolumeDevicePath(vol.Name, false) + devicePath := types.GetVolumeDevicePath(vol.Name, vol.DataEngine, false) mountPath := types.GetMountPath(vol.Name) defer func() { diff --git a/pkg/server/share_manager.go b/pkg/server/share_manager.go index 0b2d33cc..78e7a0b3 100644 --- a/pkg/server/share_manager.go +++ b/pkg/server/share_manager.go @@ -103,7 +103,7 @@ func NewShareManager(logger logrus.FieldLogger, volume volume.Volume) (*ShareMan func (m *ShareManager) Run() error { vol := m.volume mountPath := types.GetMountPath(vol.Name) - devicePath := types.GetVolumeDevicePath(vol.Name, false) + devicePath := types.GetVolumeDevicePath(vol.Name, vol.DataEngine, false) defer func() { // if the server is exiting, try to unmount & teardown device before we terminate the container @@ -201,9 +201,9 @@ func (m *ShareManager) setupDevice(vol volume.Volume, devicePath string) (string } } - cryptoDevice := types.GetVolumeDevicePath(vol.Name, true) + cryptoDevice := types.GetVolumeDevicePath(vol.Name, vol.DataEngine, true) m.logger.Infof("Volume %s requires crypto device %s", vol.Name, cryptoDevice) - if err := crypto.OpenVolume(vol.Name, devicePath, vol.Passphrase); err != nil { + if err := crypto.OpenVolume(vol.Name, vol.DataEngine, devicePath, vol.Passphrase); err != nil { m.logger.WithError(err).Error("Failed to open encrypted volume") return "", err } @@ -217,12 +217,12 @@ func (m *ShareManager) setupDevice(vol volume.Volume, devicePath string) (string func (m *ShareManager) tearDownDevice(vol volume.Volume) error { // close any matching crypto device for this volume - cryptoDevice := types.GetVolumeDevicePath(vol.Name, true) + cryptoDevice := types.GetVolumeDevicePath(vol.Name, vol.DataEngine, true) if isOpen, err := crypto.IsDeviceOpen(cryptoDevice); err != nil { return err } else if isOpen { m.logger.Infof("Volume %s has active crypto device %s", vol.Name, cryptoDevice) - if err := crypto.CloseVolume(vol.Name); err != nil { + if err := crypto.CloseVolume(vol.Name, vol.DataEngine); err != nil { return err } m.logger.Infof("Volume %s closed active crypto device %s", vol.Name, cryptoDevice) diff --git a/pkg/types/types.go b/pkg/types/types.go index 86aa5e08..d2bf167e 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -12,11 +12,29 @@ const ( DevPath = "/dev" MapperDevPath = "/dev/mapper" + // The suffix for dm device name of the encrypted v2 volume. + MapperV2VolumeSuffix = "-encrypted" + ExportPath = "/export" + + DataEngineTypeV1 = "v1" + DataEngineTypeV2 = "v2" ) -func GetVolumeDevicePath(volumeName string, EncryptedDevice bool) string { +func GetEncryptVolumeName(volume, dataEngine string) string { + if dataEngine == DataEngineTypeV2 { + return volume + MapperV2VolumeSuffix + } + return volume +} + +func GetVolumeDevicePath(volumeName, dataEngine string, EncryptedDevice bool) string { if EncryptedDevice { + if dataEngine == DataEngineTypeV2 { + // v2 volume will use a dm device as default to control IO path when attaching. This dm device will be created with the same name as the volume name. + // The encrypted volume will be created with the volume name with "-encrypted" suffix to resolve the naming conflict. + return path.Join(MapperDevPath, GetEncryptVolumeName(volumeName, dataEngine)) + } return path.Join(MapperDevPath, volumeName) } return filepath.Join(DevPath, "longhorn", volumeName) diff --git a/pkg/volume/volume.go b/pkg/volume/volume.go index d5d3404a..8e9fc654 100644 --- a/pkg/volume/volume.go +++ b/pkg/volume/volume.go @@ -11,6 +11,7 @@ import ( type Volume struct { Name string + DataEngine string Passphrase string CryptoKeyCipher string CryptoKeyHash string