forked from ceph/go-ceph
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
rbd: add EncryptionLoad2 implementing rbd_encryption_load2
Add a new Image method EncryptionLoad2 implementing rbd_encryption_load2. This method adds the ability to have different encryption schemes across parent images. Signed-off-by: John Mulligan <[email protected]> Fixes: ceph#1059
- Loading branch information
1 parent
f6c084c
commit 3aa05cc
Showing
4 changed files
with
331 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
//go:build !octopus && !pacific && !quincy && ceph_preview | ||
|
||
package rbd | ||
|
||
// #cgo LDFLAGS: -lrbd | ||
// /* force XSI-complaint strerror_r() */ | ||
// #define _POSIX_C_SOURCE 200112L | ||
// #undef _GNU_SOURCE | ||
// #include <rbd/librbd.h> | ||
import "C" | ||
|
||
import ( | ||
"unsafe" | ||
) | ||
|
||
// toEncryptionSpec returns a rbd_encryption_spec_t converted from the | ||
// cEncryptionData type. | ||
func (edata cEncryptionData) toEncryptionSpec() C.rbd_encryption_spec_t { | ||
var cSpec C.rbd_encryption_spec_t | ||
cSpec.format = edata.format | ||
cSpec.opts = edata.opts | ||
cSpec.opts_size = edata.optsSize | ||
return cSpec | ||
} | ||
|
||
// EncryptionLoad2 enables IO on an open encrypted image and ancestor images. | ||
// The first EncryptionOptions in the slice is applied to the image, the second | ||
// to the first ancestor, the third to the second ancestor and so on. | ||
// If the length of the slice is smaller than the number of ancestors the | ||
// final item in the slice will be applied to all remaining ancestors. | ||
// | ||
// Implements: | ||
// | ||
// int rbd_encryption_load2(rbd_image_t image, | ||
// const rbd_encryption_spec_t *specs, | ||
// size_t spec_count); | ||
func (image *Image) EncryptionLoad2(opts []EncryptionOptions) error { | ||
if image.image == nil { | ||
return ErrImageNotOpen | ||
} | ||
|
||
length := len(opts) | ||
eos := make([]cEncryptionData, length) | ||
cspecs := (*C.rbd_encryption_spec_t)(C.malloc( | ||
C.size_t(C.sizeof_rbd_encryption_spec_t * length))) | ||
specs := unsafe.Slice(cspecs, length) | ||
|
||
for idx, option := range opts { | ||
eos[idx] = option.allocateEncryptionOptions() | ||
specs[idx] = eos[idx].toEncryptionSpec() | ||
} | ||
defer func() { | ||
for _, eopt := range eos { | ||
eopt.free() | ||
} | ||
}() | ||
|
||
ret := C.rbd_encryption_load2( | ||
image.image, | ||
cspecs, | ||
C.size_t(length)) | ||
return getError(ret) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,261 @@ | ||
//go:build !octopus && !pacific && !quincy && ceph_preview | ||
|
||
package rbd | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestEncryptionLoad2(t *testing.T) { | ||
conn := radosConnect(t) | ||
defer conn.Shutdown() | ||
|
||
poolname := GetUUID() | ||
err := conn.MakePool(poolname) | ||
assert.NoError(t, err) | ||
defer conn.DeletePool(poolname) | ||
|
||
ioctx, err := conn.OpenIOContext(poolname) | ||
require.NoError(t, err) | ||
defer ioctx.Destroy() | ||
|
||
name := GetUUID() | ||
testImageSize := uint64(50) * 1024 * 1024 | ||
options := NewRbdImageOptions() | ||
assert.NoError(t, | ||
options.SetUint64(ImageOptionOrder, uint64(testImageOrder))) | ||
err = CreateImage(ioctx, name, testImageSize, options) | ||
assert.NoError(t, err) | ||
|
||
img, err := OpenImage(ioctx, name, NoSnapshot) | ||
assert.NoError(t, err) | ||
|
||
encOpts := EncryptionOptionsLUKS2{ | ||
Alg: EncryptionAlgorithmAES256, | ||
Passphrase: []byte("test-password"), | ||
} | ||
err = img.EncryptionFormat(encOpts) | ||
assert.NoError(t, err) | ||
|
||
// close the image so we can reopen it and load the encryption info | ||
// then write some encrypted data at the end of the image | ||
err = img.Close() | ||
assert.NoError(t, err) | ||
defer func() { | ||
assert.NoError(t, img.Remove()) | ||
}() | ||
|
||
testData := []byte("Jinxed wizards pluck ivy from the big quilt") | ||
var offset int64 | ||
|
||
t.Run("prepare", func(t *testing.T) { | ||
img, err = OpenImage(ioctx, name, NoSnapshot) | ||
assert.NoError(t, err) | ||
defer img.Close() | ||
err = img.EncryptionLoad2([]EncryptionOptions{encOpts}) | ||
assert.NoError(t, err) | ||
|
||
stats, err := img.Stat() | ||
require.NoError(t, err) | ||
offset = int64(stats.Size) - int64(len(testData)) | ||
|
||
nOut, err := img.WriteAt(testData, offset) | ||
assert.Equal(t, len(testData), nOut) | ||
assert.NoError(t, err) | ||
}) | ||
|
||
t.Run("readEnc", func(t *testing.T) { | ||
require.NotEqual(t, offset, 0) | ||
// Re-open the image, load the encryption format, and read the encrypted data | ||
img, err = OpenImage(ioctx, name, NoSnapshot) | ||
assert.NoError(t, err) | ||
defer img.Close() | ||
err = img.EncryptionLoad2([]EncryptionOptions{encOpts}) | ||
assert.NoError(t, err) | ||
|
||
inData := make([]byte, len(testData)) | ||
nIn, err := img.ReadAt(inData, offset) | ||
assert.Equal(t, nIn, len(inData)) | ||
assert.Equal(t, inData, testData) | ||
assert.NoError(t, err) | ||
}) | ||
|
||
t.Run("noEnc", func(t *testing.T) { | ||
require.NotEqual(t, offset, 0) | ||
// Re-open the image and attempt to read the encrypted data without loading the encryption | ||
img, err = OpenImage(ioctx, name, NoSnapshot) | ||
assert.NoError(t, err) | ||
defer img.Close() | ||
|
||
inData := make([]byte, len(testData)) | ||
nIn, err := img.ReadAt(inData, offset) | ||
assert.Equal(t, nIn, len(inData)) | ||
assert.NotEqual(t, inData, testData) | ||
assert.NoError(t, err) | ||
}) | ||
} | ||
|
||
func TestEncryptionLoad2WithParents(t *testing.T) { | ||
dlength := int64(32) | ||
testData1 := []byte("Very nice object ahead of change") | ||
testData2 := []byte("A nice object encryption applied") | ||
testData3 := []byte("A good object encryption abounds") | ||
testData4 := []byte("Another portion is here and well") | ||
written := [][]byte{} | ||
assert.EqualValues(t, len(testData1), dlength) | ||
assert.EqualValues(t, len(testData2), dlength) | ||
assert.EqualValues(t, len(testData3), dlength) | ||
assert.EqualValues(t, len(testData4), dlength) | ||
|
||
encOpts1 := EncryptionOptionsLUKS1{ | ||
Alg: EncryptionAlgorithmAES128, | ||
Passphrase: []byte("test-password"), | ||
} | ||
encOpts2 := EncryptionOptionsLUKS2{ | ||
Alg: EncryptionAlgorithmAES128, | ||
Passphrase: []byte("test-password"), | ||
} | ||
encOpts3 := EncryptionOptionsLUKS2{ | ||
Alg: EncryptionAlgorithmAES256, | ||
Passphrase: []byte("something-stronger"), | ||
} | ||
|
||
conn := radosConnect(t) | ||
defer conn.Shutdown() | ||
|
||
poolname := GetUUID() | ||
err := conn.MakePool(poolname) | ||
assert.NoError(t, err) | ||
defer conn.DeletePool(poolname) | ||
|
||
ioctx, err := conn.OpenIOContext(poolname) | ||
require.NoError(t, err) | ||
defer ioctx.Destroy() | ||
|
||
name := GetUUID() | ||
testImageSize := uint64(256) * 1024 * 1024 | ||
options := NewRbdImageOptions() | ||
assert.NoError(t, | ||
options.SetUint64(ImageOptionOrder, uint64(testImageOrder))) | ||
err = CreateImage(ioctx, name, testImageSize, options) | ||
assert.NoError(t, err) | ||
|
||
t.Run("prepare", func(t *testing.T) { | ||
img, err := OpenImage(ioctx, name, NoSnapshot) | ||
assert.NoError(t, err) | ||
defer img.Close() | ||
|
||
_, err = img.WriteAt(testData1, 0) | ||
assert.NoError(t, err) | ||
written = append(written, testData1) | ||
}) | ||
|
||
t.Run("createClone1", func(t *testing.T) { | ||
require.Len(t, written, 1) | ||
parent, err := OpenImage(ioctx, name, NoSnapshot) | ||
assert.NoError(t, err) | ||
defer parent.Close() | ||
snap, err := parent.CreateSnapshot("sn1") | ||
assert.NoError(t, err) | ||
err = snap.Protect() | ||
assert.NoError(t, err) | ||
|
||
err = CloneImage(ioctx, name, "sn1", ioctx, name+"clone1", options) | ||
assert.NoError(t, err) | ||
|
||
img, err := OpenImage(ioctx, name+"clone1", NoSnapshot) | ||
assert.NoError(t, err) | ||
defer img.Close() | ||
err = img.EncryptionFormat(encOpts1) | ||
assert.NoError(t, err) | ||
|
||
err = img.EncryptionLoad2([]EncryptionOptions{encOpts1}) | ||
assert.NoError(t, err) | ||
_, err = img.WriteAt(testData2, dlength) | ||
assert.NoError(t, err) | ||
written = append(written, testData2) | ||
}) | ||
|
||
t.Run("createClone2", func(t *testing.T) { | ||
require.Len(t, written, 2) | ||
parentName := name + "clone1" | ||
cloneName := name + "clone2" | ||
|
||
parent, err := OpenImage(ioctx, parentName, NoSnapshot) | ||
assert.NoError(t, err) | ||
defer parent.Close() | ||
snap, err := parent.CreateSnapshot("sn2") | ||
assert.NoError(t, err) | ||
err = snap.Protect() | ||
assert.NoError(t, err) | ||
|
||
err = CloneImage(ioctx, parentName, "sn2", ioctx, cloneName, options) | ||
assert.NoError(t, err) | ||
|
||
img, err := OpenImage(ioctx, cloneName, NoSnapshot) | ||
assert.NoError(t, err) | ||
defer img.Close() | ||
err = img.EncryptionFormat(encOpts2) | ||
assert.NoError(t, err) | ||
|
||
err = img.EncryptionLoad2([]EncryptionOptions{encOpts2, encOpts1}) | ||
assert.NoError(t, err) | ||
_, err = img.WriteAt(testData3, dlength*2) | ||
assert.NoError(t, err) | ||
written = append(written, testData3) | ||
}) | ||
|
||
t.Run("createClone3", func(t *testing.T) { | ||
require.Len(t, written, 3) | ||
parentName := name + "clone2" | ||
cloneName := name + "clone3" | ||
|
||
parent, err := OpenImage(ioctx, parentName, NoSnapshot) | ||
assert.NoError(t, err) | ||
defer parent.Close() | ||
snap, err := parent.CreateSnapshot("sn3") | ||
assert.NoError(t, err) | ||
err = snap.Protect() | ||
assert.NoError(t, err) | ||
|
||
err = CloneImage(ioctx, parentName, "sn3", ioctx, cloneName, options) | ||
assert.NoError(t, err) | ||
|
||
img, err := OpenImage(ioctx, cloneName, NoSnapshot) | ||
assert.NoError(t, err) | ||
defer img.Close() | ||
err = img.EncryptionFormat(encOpts3) | ||
assert.NoError(t, err) | ||
|
||
err = img.EncryptionLoad2([]EncryptionOptions{ | ||
encOpts3, encOpts2, encOpts1, | ||
}) | ||
assert.NoError(t, err) | ||
_, err = img.WriteAt(testData4, dlength*3) | ||
assert.NoError(t, err) | ||
written = append(written, testData4) | ||
}) | ||
|
||
t.Run("readAll", func(t *testing.T) { | ||
require.Len(t, written, 4) | ||
img, err := OpenImage(ioctx, name+"clone3", NoSnapshot) | ||
assert.NoError(t, err) | ||
defer img.Close() | ||
|
||
err = img.EncryptionLoad2([]EncryptionOptions{ | ||
encOpts3, encOpts2, encOpts1, | ||
}) | ||
assert.NoError(t, err) | ||
|
||
inData := make([]byte, int(dlength)) | ||
for idx, td := range written { | ||
n, err := img.ReadAt(inData, int64(idx)*dlength) | ||
assert.NoError(t, err) | ||
assert.Len(t, inData, n) | ||
assert.Equal(t, inData, td) | ||
} | ||
}) | ||
} |