Skip to content

Commit 0d6b580

Browse files
phlogistonjohnmergify[bot]
authored andcommitted
rbd: add EncryptionOptionsLUKS for opening LUKS images
Add EncryptionOptionsLUKS type for opening, but not formatting, existing LUKS images generically. Signed-off-by: John Mulligan <[email protected]>
1 parent ccb22a5 commit 0d6b580

File tree

2 files changed

+170
-0
lines changed

2 files changed

+170
-0
lines changed

rbd/encryption_opt_luks.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//go:build !octopus && !pacific && !quincy && ceph_preview
2+
3+
package rbd
4+
5+
// #cgo LDFLAGS: -lrbd
6+
// /* force XSI-complaint strerror_r() */
7+
// #define _POSIX_C_SOURCE 200112L
8+
// #undef _GNU_SOURCE
9+
// #include <stdlib.h>
10+
// #include <rbd/librbd.h>
11+
import "C"
12+
13+
import (
14+
"unsafe"
15+
)
16+
17+
// EncryptionOptionsLUKS generic options for either LUKS v1 or v2. May only be
18+
// used for opening existing images - not valid for the EncryptionFormat call.
19+
type EncryptionOptionsLUKS struct {
20+
Passphrase []byte
21+
}
22+
23+
func (opts EncryptionOptionsLUKS) allocateEncryptionOptions() cEncryptionData {
24+
var cOpts C.rbd_encryption_luks_format_options_t
25+
var retData cEncryptionData
26+
// CBytes allocates memory. it will be freed when cEncryptionData.free is called
27+
cOpts.passphrase = (*C.char)(C.CBytes(opts.Passphrase))
28+
cOpts.passphrase_size = C.size_t(len(opts.Passphrase))
29+
retData.opts = C.rbd_encryption_options_t(&cOpts)
30+
retData.optsSize = C.size_t(C.sizeof_rbd_encryption_luks_format_options_t)
31+
retData.free = func() { C.free(unsafe.Pointer(cOpts.passphrase)) }
32+
retData.format = C.RBD_ENCRYPTION_FORMAT_LUKS
33+
return retData
34+
}
35+
36+
func (opts EncryptionOptionsLUKS) writeEncryptionSpec(spec *C.rbd_encryption_spec_t) func() {
37+
/* only C memory should be attached to spec */
38+
cPassphrase := (*C.char)(C.CBytes(opts.Passphrase))
39+
cOptsSize := C.size_t(C.sizeof_rbd_encryption_luks_format_options_t)
40+
cOpts := (*C.rbd_encryption_luks_format_options_t)(C.malloc(cOptsSize))
41+
cOpts.passphrase = cPassphrase
42+
cOpts.passphrase_size = C.size_t(len(opts.Passphrase))
43+
44+
spec.format = C.RBD_ENCRYPTION_FORMAT_LUKS
45+
spec.opts = C.rbd_encryption_options_t(cOpts)
46+
spec.opts_size = cOptsSize
47+
return func() {
48+
C.free(unsafe.Pointer(cOpts.passphrase))
49+
C.free(unsafe.Pointer(cOpts))
50+
}
51+
}

rbd/encryption_opt_luks_test.go

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
//go:build !octopus && !pacific && !quincy && ceph_preview
2+
3+
package rbd
4+
5+
import (
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestEncryptionOptionsLUKS(t *testing.T) {
13+
conn := radosConnect(t)
14+
defer conn.Shutdown()
15+
16+
poolname := GetUUID()
17+
err := conn.MakePool(poolname)
18+
assert.NoError(t, err)
19+
defer conn.DeletePool(poolname)
20+
21+
ioctx, err := conn.OpenIOContext(poolname)
22+
require.NoError(t, err)
23+
defer ioctx.Destroy()
24+
25+
name := GetUUID()
26+
testImageSize := uint64(50) * 1024 * 1024
27+
options := NewRbdImageOptions()
28+
assert.NoError(t,
29+
options.SetUint64(ImageOptionOrder, uint64(testImageOrder)))
30+
err = CreateImage(ioctx, name, testImageSize, options)
31+
assert.NoError(t, err)
32+
33+
img, err := OpenImage(ioctx, name, NoSnapshot)
34+
assert.NoError(t, err)
35+
36+
encOpts := EncryptionOptionsLUKS2{
37+
Alg: EncryptionAlgorithmAES256,
38+
Passphrase: []byte("sesame-123-luks-ury"),
39+
}
40+
err = img.EncryptionFormat(encOpts)
41+
assert.NoError(t, err)
42+
43+
// close the image so we can reopen it and load the encryption info
44+
// then write some encrypted data at the end of the image
45+
err = img.Close()
46+
assert.NoError(t, err)
47+
defer func() {
48+
assert.NoError(t, img.Remove())
49+
}()
50+
51+
testData := []byte("Another day another image to unlock")
52+
var offset int64
53+
54+
t.Run("prepare", func(t *testing.T) {
55+
img, err = OpenImage(ioctx, name, NoSnapshot)
56+
assert.NoError(t, err)
57+
defer img.Close()
58+
err = img.EncryptionLoad2([]EncryptionOptions{encOpts})
59+
assert.NoError(t, err)
60+
61+
stats, err := img.Stat()
62+
require.NoError(t, err)
63+
offset = int64(stats.Size) - int64(len(testData))
64+
65+
nOut, err := img.WriteAt(testData, offset)
66+
assert.Equal(t, len(testData), nOut)
67+
assert.NoError(t, err)
68+
})
69+
70+
unlock := []EncryptionOptions{
71+
EncryptionOptionsLUKS{Passphrase: []byte("sesame-123-luks-ury")},
72+
}
73+
74+
t.Run("readEncLoad", func(t *testing.T) {
75+
require.NotEqual(t, offset, 0)
76+
// Re-open the image, using the generic luks encryption options
77+
img, err = OpenImage(ioctx, name, NoSnapshot)
78+
assert.NoError(t, err)
79+
defer img.Close()
80+
err = img.EncryptionLoad(unlock[0])
81+
assert.NoError(t, err)
82+
83+
inData := make([]byte, len(testData))
84+
nIn, err := img.ReadAt(inData, offset)
85+
assert.Equal(t, nIn, len(testData))
86+
assert.Equal(t, inData, testData)
87+
assert.NoError(t, err)
88+
})
89+
90+
t.Run("readEncLoad2", func(t *testing.T) {
91+
require.NotEqual(t, offset, 0)
92+
// Re-open the image, using the generic luks encryption options
93+
img, err = OpenImage(ioctx, name, NoSnapshot)
94+
assert.NoError(t, err)
95+
defer img.Close()
96+
err = img.EncryptionLoad2(unlock)
97+
assert.NoError(t, err)
98+
99+
inData := make([]byte, len(testData))
100+
nIn, err := img.ReadAt(inData, offset)
101+
assert.Equal(t, nIn, len(testData))
102+
assert.Equal(t, inData, testData)
103+
assert.NoError(t, err)
104+
})
105+
106+
t.Run("noEnc", func(t *testing.T) {
107+
require.NotEqual(t, offset, 0)
108+
// Re-open the image and attempt to read the encrypted data without loading the encryption
109+
img, err = OpenImage(ioctx, name, NoSnapshot)
110+
assert.NoError(t, err)
111+
defer img.Close()
112+
113+
inData := make([]byte, len(testData))
114+
nIn, err := img.ReadAt(inData, offset)
115+
assert.Equal(t, nIn, len(testData))
116+
assert.NotEqual(t, inData, testData)
117+
assert.NoError(t, err)
118+
})
119+
}

0 commit comments

Comments
 (0)