diff --git a/csi/crypto/crypto.go b/csi/crypto/crypto.go index 1cc52f3203..80b1218c62 100644 --- a/csi/crypto/crypto.go +++ b/csi/crypto/crypto.go @@ -8,11 +8,11 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/longhorn/longhorn-manager/types" - 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" + + "github.com/longhorn/longhorn-manager/types" ) const ( @@ -85,12 +85,21 @@ func VolumeMapper(volume, dataEngine string) string { // EncryptVolume encrypts provided device with LUKS. func EncryptVolume(devicePath, passphrase string, cryptoParams *EncryptParams) error { + isEncrypted, err := isDeviceEncrypted(devicePath) + if err != nil { + logrus.WithError(err).Warnf("Failed to check IsDeviceEncrypted before encrypting volume %v", devicePath) + return err + } + if isEncrypted { + logrus.Infof("The device %v is already encrypted. Skipping the encryption to avoid data lost", devicePath) + return nil + } + namespaces := []lhtypes.Namespace{lhtypes.NamespaceMnt, lhtypes.NamespaceIpc} nsexec, err := lhns.NewNamespaceExecutor(lhtypes.ProcessNone, lhtypes.HostProcDirectory, namespaces) if err != nil { return err } - logrus.Infof("Encrypting device %s with LUKS", devicePath) if _, err := nsexec.LuksFormat( devicePath, passphrase, @@ -182,6 +191,15 @@ func IsDeviceOpen(device string) (bool, error) { return mappedFile != "", err } +func isDeviceEncrypted(devicePath string) (bool, error) { + namespaces := []lhtypes.Namespace{lhtypes.NamespaceMnt, lhtypes.NamespaceIpc} + nsexec, err := lhns.NewNamespaceExecutor(lhtypes.ProcessNone, lhtypes.HostProcDirectory, namespaces) + if err != nil { + return false, err + } + return nsexec.IsLuks(devicePath, lhtypes.LuksTimeout) +} + // DeviceEncryptionStatus looks to identify if the passed device is a LUKS mapping // and if so what the device is and the mapper name as used by LUKS. // If not, just returns the original device and an empty string. diff --git a/vendor/github.com/longhorn/go-common-libs/ns/crypto.go b/vendor/github.com/longhorn/go-common-libs/ns/crypto.go index 5638b37dde..474d9e55b3 100644 --- a/vendor/github.com/longhorn/go-common-libs/ns/crypto.go +++ b/vendor/github.com/longhorn/go-common-libs/ns/crypto.go @@ -2,8 +2,10 @@ package ns import ( "time" + "os/exec" "github.com/longhorn/go-common-libs/types" + "github.com/pkg/errors" ) // LuksOpen runs cryptsetup luksOpen with the given passphrase and @@ -47,6 +49,24 @@ func (nsexec *Executor) LuksStatus(volume string, timeout time.Duration) (stdout return nsexec.Cryptsetup(args, timeout) } +func (nsexec *Executor) IsLuks(devicePath string, timeout time.Duration) (bool, error) { + args := []string{"isLuks", devicePath} + _, err := nsexec.Cryptsetup(args, timeout) + if err == nil { + return true, nil + } + var exitErr *exec.ExitError + if errors.As(err, &exitErr) { + if exitErr.ExitCode() == 1 { + // The device is not encrypted if exit code of 1 is returned + // Ref https://gitlab.com/cryptsetup/cryptsetup/-/blob/main/FAQ.md?plain=1#L2848 + return false, nil + } + } + return false, err +} + + // Cryptsetup runs cryptsetup without passphrase. It will return // 0 on success and a non-zero value on error. func (nsexec *Executor) Cryptsetup(args []string, timeout time.Duration) (stdout string, err error) {