Skip to content

Commit bc8c25d

Browse files
committed
feat: support v2 backing image cloning and encryption
ref: longhorn/longhorn 9996 Signed-off-by: Jack Lin <[email protected]>
1 parent 44753b5 commit bc8c25d

File tree

10 files changed

+923
-564
lines changed

10 files changed

+923
-564
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ go 1.23.0
44

55
toolchain go1.23.4
66

7+
replace github.com/longhorn/types v0.0.0-20241225162202-00d3a5fd7502 => github.com/chanyilin/types v0.0.0-20250122064930-c1b9017c2968
8+
79
require (
810
github.com/0xPolygon/polygon-edge v1.3.3
911
github.com/google/uuid v1.6.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 h1:SjZ2GvvOononHOpK
1212
github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8/go.mod h1:uEyr4WpAH4hio6LFriaPkL938XnrvLpNPmQHBdrmbIE=
1313
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
1414
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
15+
github.com/chanyilin/types v0.0.0-20250122064930-c1b9017c2968 h1:RSdTuXuVvvX7AMEUczdNhFYBtu6sHZ3jGd6I/CqAjJI=
16+
github.com/chanyilin/types v0.0.0-20250122064930-c1b9017c2968/go.mod h1:3jHuVDtpkXQzpnp4prguDBskVRric2kmF8aSPkRJ4jw=
1517
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
1618
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
1719
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@@ -57,8 +59,6 @@ github.com/longhorn/go-common-libs v0.0.0-20250107022351-ec79818ce8db h1:aXAcEW8
5759
github.com/longhorn/go-common-libs v0.0.0-20250107022351-ec79818ce8db/go.mod h1:Fm4sGHDO1JXsAI/DxdEJ/QFWYv+jJMJMfHaCyd+SnGA=
5860
github.com/longhorn/go-spdk-helper v0.0.0-20250116051812-eabe34a4219e h1:rBySu2+Dfrp0+xBwo989TskR9hBCe31H/F2m+PIOXxI=
5961
github.com/longhorn/go-spdk-helper v0.0.0-20250116051812-eabe34a4219e/go.mod h1:2NT1Xz4rBNecYAQmj/UqHz5D3UEDZZkiv2hcmVB7kqM=
60-
github.com/longhorn/types v0.0.0-20241225162202-00d3a5fd7502 h1:jgw7nosooLe1NQEdCGzM/nEOFzPcurNO+0PDsicc5+A=
61-
github.com/longhorn/types v0.0.0-20241225162202-00d3a5fd7502/go.mod h1:3jHuVDtpkXQzpnp4prguDBskVRric2kmF8aSPkRJ4jw=
6262
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
6363
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
6464
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package backingimagecrypto
2+
3+
import (
4+
"fmt"
5+
"path"
6+
"strings"
7+
"time"
8+
9+
"github.com/pkg/errors"
10+
"github.com/sirupsen/logrus"
11+
12+
lhns "github.com/longhorn/go-common-libs/ns"
13+
lhtypes "github.com/longhorn/go-common-libs/types"
14+
)
15+
16+
const (
17+
MapperFilePathPrefix = "/dev/mapper"
18+
19+
CryptoKeyDefaultCipher = "aes-xts-plain64"
20+
CryptoKeyDefaultHash = "sha256"
21+
CryptoKeyDefaultSize = "256"
22+
CryptoDefaultPBKDF = "argon2i"
23+
24+
CommandExecutionTimeout = 10 * time.Second
25+
26+
EncryptionMetaSize = 16 * 1024 * 1024 // 16MB
27+
)
28+
29+
// EncryptParams keeps the customized cipher options from the secret CR
30+
type EncryptParams struct {
31+
KeyProvider string
32+
KeyCipher string
33+
KeyHash string
34+
KeySize string
35+
PBKDF string
36+
}
37+
38+
func NewEncryptParams(keyProvider, keyCipher, keyHash, keySize, pbkdf string) *EncryptParams {
39+
return &EncryptParams{KeyProvider: keyProvider, KeyCipher: keyCipher, KeyHash: keyHash, KeySize: keySize, PBKDF: pbkdf}
40+
}
41+
42+
func (cp *EncryptParams) GetKeyCipher() string {
43+
if cp.KeyCipher == "" {
44+
return CryptoKeyDefaultCipher
45+
}
46+
return cp.KeyCipher
47+
}
48+
49+
func (cp *EncryptParams) GetKeyHash() string {
50+
if cp.KeyHash == "" {
51+
return CryptoKeyDefaultHash
52+
}
53+
return cp.KeyHash
54+
}
55+
56+
func (cp *EncryptParams) GetKeySize() string {
57+
if cp.KeySize == "" {
58+
return CryptoKeyDefaultSize
59+
}
60+
return cp.KeySize
61+
}
62+
63+
func (cp *EncryptParams) GetPBKDF() string {
64+
if cp.PBKDF == "" {
65+
return CryptoDefaultPBKDF
66+
}
67+
return cp.PBKDF
68+
}
69+
70+
// EncryptBackingImage encrypts provided device with LUKS.
71+
func EncryptBackingImage(devicePath, passphrase string, cryptoParams *EncryptParams) error {
72+
namespaces := []lhtypes.Namespace{lhtypes.NamespaceMnt, lhtypes.NamespaceIpc}
73+
nsexec, err := lhns.NewNamespaceExecutor(lhtypes.ProcessNone, lhtypes.HostProcDirectory, namespaces)
74+
if err != nil {
75+
return err
76+
}
77+
78+
logrus.Infof("Encrypting device %s with LUKS", devicePath)
79+
if _, err := nsexec.LuksFormat(
80+
devicePath, passphrase,
81+
cryptoParams.GetKeyCipher(),
82+
cryptoParams.GetKeyHash(),
83+
cryptoParams.GetKeySize(),
84+
cryptoParams.GetPBKDF(),
85+
lhtypes.LuksTimeout); err != nil {
86+
return errors.Wrapf(err, "failed to encrypt device %s with LUKS", devicePath)
87+
}
88+
return nil
89+
}
90+
91+
// OpenBackingImage opens backing image so that it can be used by the client.
92+
func OpenBackingImage(devicePath, passphrase, name string) error {
93+
if isOpen, _ := IsEncryptedDeviceOpened(BackingImageMapper(name)); isOpen {
94+
logrus.Infof("Device %s is already opened at %s", devicePath, BackingImageMapper(name))
95+
return nil
96+
}
97+
98+
namespaces := []lhtypes.Namespace{lhtypes.NamespaceMnt, lhtypes.NamespaceIpc}
99+
nsexec, err := lhns.NewNamespaceExecutor(lhtypes.ProcessNone, lhtypes.HostProcDirectory, namespaces)
100+
if err != nil {
101+
return err
102+
}
103+
104+
logrus.Infof("Opening device %s with LUKS", devicePath)
105+
_, err = nsexec.LuksOpen(GetLuksBackingImageName(name), devicePath, passphrase, lhtypes.LuksTimeout)
106+
if err != nil {
107+
logrus.WithError(err).Warnf("Failed to open LUKS device %s", devicePath)
108+
}
109+
return err
110+
}
111+
112+
// CloseBackingImage closes encrypted backing image so it can be detached.
113+
func CloseBackingImage(name string) error {
114+
namespaces := []lhtypes.Namespace{lhtypes.NamespaceMnt, lhtypes.NamespaceIpc}
115+
nsexec, err := lhns.NewNamespaceExecutor(lhtypes.ProcessNone, lhtypes.HostProcDirectory, namespaces)
116+
if err != nil {
117+
return err
118+
}
119+
120+
logrus.Infof("Closing LUKS device %s", GetLuksBackingImageName(name))
121+
_, err = nsexec.LuksClose(GetLuksBackingImageName(name), lhtypes.LuksTimeout)
122+
return err
123+
}
124+
125+
// IsEncryptedDeviceOpened determines if encrypted device is already open.
126+
func IsEncryptedDeviceOpened(device string) (bool, error) {
127+
_, mappedFile, err := DeviceEncryptionStatus(device)
128+
return mappedFile != "", err
129+
}
130+
131+
// DeviceEncryptionStatus looks to identify if the passed device is a LUKS mapping
132+
// and if so what the device is and the mapper name as used by LUKS.
133+
// If not, just returns the original device and an empty string.
134+
func DeviceEncryptionStatus(devicePath string) (mappedDevice, mapper string, err error) {
135+
if !strings.HasPrefix(devicePath, MapperFilePathPrefix) {
136+
return devicePath, "", nil
137+
}
138+
139+
namespaces := []lhtypes.Namespace{lhtypes.NamespaceMnt, lhtypes.NamespaceIpc}
140+
nsexec, err := lhns.NewNamespaceExecutor(lhtypes.ProcessNone, lhtypes.HostProcDirectory, namespaces)
141+
if err != nil {
142+
return devicePath, "", err
143+
}
144+
145+
backingImage := strings.TrimPrefix(devicePath, MapperFilePathPrefix+"/")
146+
stdout, err := nsexec.LuksStatus(backingImage, lhtypes.LuksTimeout)
147+
if err != nil {
148+
logrus.WithError(err).Warnf("Device %s is not an active LUKS device", devicePath)
149+
return devicePath, "", nil
150+
}
151+
152+
lines := strings.Split(string(stdout), "\n")
153+
if len(lines) < 1 {
154+
return "", "", fmt.Errorf("device encryption status returned no stdout for %s", devicePath)
155+
}
156+
157+
if !strings.Contains(lines[0], " is active") {
158+
// Implies this is not a LUKS device
159+
return devicePath, "", nil
160+
}
161+
162+
for i := 1; i < len(lines); i++ {
163+
kv := strings.SplitN(strings.TrimSpace(lines[i]), ":", 2)
164+
if len(kv) < 1 {
165+
return "", "", fmt.Errorf("device encryption status output for %s is badly formatted: %s",
166+
devicePath, lines[i])
167+
}
168+
if strings.Compare(kv[0], "device") == 0 {
169+
return strings.TrimSpace(kv[1]), backingImage, nil
170+
}
171+
}
172+
// Identified as LUKS, but failed to identify a mapped device
173+
return "", "", fmt.Errorf("mapped device not found in path %s", devicePath)
174+
}
175+
176+
func BackingImageMapper(uuid string) string {
177+
return path.Join(MapperFilePathPrefix, GetLuksBackingImageName(uuid))
178+
}
179+
180+
// TODO: Fix hard coded
181+
func GetLuksBackingImageName(name string) string {
182+
return fmt.Sprintf("%v-%v", "v2backing", name)
183+
}

pkg/client/client.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -825,22 +825,25 @@ func (c *SPDKClient) ReplicaRestoreStatus(replicaName string) (*spdkrpc.ReplicaR
825825
})
826826
}
827827

828-
func (c *SPDKClient) BackingImageCreate(name, backingImageUUID, lvsUUID string, size uint64, checksum string, fromAddress string, srcLvsUUID string) (*api.BackingImage, error) {
829-
if name == "" || backingImageUUID == "" || checksum == "" || lvsUUID == "" || size == 0 {
828+
func (c *SPDKClient) BackingImageCreate(name, backingImageUUID, lvsUUID string, size uint64, checksum, fromAddress, srcLvsUUID, srcBackingImageName, encryption string, credential map[string]string) (*api.BackingImage, error) {
829+
if name == "" || backingImageUUID == "" || lvsUUID == "" || srcBackingImageName == "" || size == 0 {
830830
return nil, fmt.Errorf("failed to start SPDK backing image: missing required parameters")
831831
}
832832
client := c.getSPDKServiceClient()
833833
ctx, cancel := context.WithTimeout(context.Background(), GRPCServiceTimeout)
834834
defer cancel()
835835

836836
resp, err := client.BackingImageCreate(ctx, &spdkrpc.BackingImageCreateRequest{
837-
Name: name,
838-
BackingImageUuid: backingImageUUID,
839-
LvsUuid: lvsUUID,
840-
Size: size,
841-
Checksum: checksum,
842-
FromAddress: fromAddress,
843-
SrcLvsUuid: srcLvsUUID,
837+
Name: name,
838+
BackingImageUuid: backingImageUUID,
839+
LvsUuid: lvsUUID,
840+
Size: size,
841+
Checksum: checksum,
842+
FromAddress: fromAddress,
843+
SrcLvsUuid: srcLvsUUID,
844+
SrcBackingImageName: srcBackingImageName,
845+
Encryption: encryption,
846+
Credential: credential,
844847
})
845848
if err != nil {
846849
return nil, errors.Wrap(err, "failed to start SPDK backing image")

0 commit comments

Comments
 (0)