Add resize functions

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2022-05-10 10:43:48 +02:00 committed by Daniel Weiße
parent 2b80341d99
commit 6b3d45dd09
5 changed files with 298 additions and 137 deletions

2
go.mod
View File

@ -220,3 +220,5 @@ require (
sigs.k8s.io/kustomize/kyaml v0.13.6 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
)
replace github.com/martinjungblut/go-cryptsetup => github.com/daniel-weisse/go-cryptsetup v0.0.0-20220510090142-d35a60c619db

4
go.sum
View File

@ -512,6 +512,8 @@ github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1S
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
github.com/daniel-weisse/go-cryptsetup v0.0.0-20220510090142-d35a60c619db h1:MgKZLtrp/goZpLnshCnx7j5YwjfibjCt60Sl/pPJHtg=
github.com/daniel-weisse/go-cryptsetup v0.0.0-20220510090142-d35a60c619db/go.mod h1:gZoZ0+POlM1ge/VUxWpMmZVNPzzMJ7l436CgkQ5+qzU=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -1016,8 +1018,6 @@ github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
github.com/martinjungblut/go-cryptsetup v0.0.0-20220421194528-92e17766b2e7 h1:/KmytOTrWo56d+J/h4VKakoCu0PxQ796+NCTfOQFCYc=
github.com/martinjungblut/go-cryptsetup v0.0.0-20220421194528-92e17766b2e7/go.mod h1:gZoZ0+POlM1ge/VUxWpMmZVNPzzMJ7l436CgkQ5+qzU=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=

View File

@ -11,6 +11,7 @@ import (
"sync"
"time"
"github.com/edgelesssys/constellation/internal/constants"
cryptsetup "github.com/martinjungblut/go-cryptsetup"
"k8s.io/klog/v2"
mount "k8s.io/mount-utils"
@ -30,6 +31,11 @@ const (
// https://stackoverflow.com/questions/30553386/cryptsetup-backend-safe-with-multithreading
var packageLock = sync.Mutex{}
func init() {
cryptsetup.SetDebugLevel(cryptsetup.CRYPT_LOG_NORMAL)
cryptsetup.SetLogCallback(func(level int, message string) { klog.V(4).Infof("libcryptsetup: %s", message) })
}
// KeyCreator is an interface to create data encryption keys.
type KeyCreator interface {
GetDEK(ctx context.Context, dekID string, dekSize int) ([]byte, error)
@ -40,9 +46,15 @@ type DeviceMapper interface {
// Init initializes a crypt device backed by 'devicePath'.
// Sets the deviceMapper to the newly allocated Device or returns any error encountered.
Init(devicePath string) error
// InitByName initializes a crypt device from provided active device 'name'.
// Sets the deviceMapper to the newly allocated Device or returns any error encountered.
InitByName(name string) error
// ActivateByPassphrase activates a device by using a passphrase from a specific keyslot.
// Returns nil on success, or an error otherwise.
ActivateByPassphrase(deviceName string, keyslot int, passphrase string, flags int) error
// ActivateByVolumeKey activates a device by using a volume key.
// Returns nil on success, or an error otherwise.
ActivateByVolumeKey(deviceName, volumeKey string, volumeKeySize, flags int) error
ActivateByVolumeKey(deviceName string, volumeKey string, volumeKeySize int, flags int) error
// Deactivate deactivates a device.
// Returns nil on success, or an error otherwise.
Deactivate(deviceName string) error
@ -54,6 +66,9 @@ type DeviceMapper interface {
// Load loads crypt device parameters from the on-disk header.
// Returns nil on success, or an error otherwise.
Load(cryptsetup.DeviceType) error
// KeyslotAddByVolumeKey adds a key slot using a volume key to perform the required security check.
// Returns nil on success, or an error otherwise.
KeyslotAddByVolumeKey(keyslot int, volumeKey string, passphrase string) error
// Wipe removes existing data and clears the device for use with dm-integrity.
// Returns nil on success, or an error otherwise.
Wipe(devicePath string, pattern int, offset, length uint64, wipeBlockSize int, flags int, progress func(size, offset uint64) int) error
@ -78,6 +93,17 @@ func (c *CryptDevice) Init(devicePath string) error {
return nil
}
// InitByName initializes a crypt device from provided active device 'name'.
// Sets the deviceMapper to the newly allocated Device or returns any error encountered.
func (c *CryptDevice) InitByName(name string) error {
device, err := cryptsetup.InitByName(name)
if err != nil {
return err
}
c.Device = device
return nil
}
// Free releases crypt device context and used memory.
func (c *CryptDevice) Free() bool {
res := c.Device.Free()
@ -142,17 +168,31 @@ func (c *CryptMapper) CloseCryptDevice(volumeID string) error {
func (c *CryptMapper) OpenCryptDevice(ctx context.Context, source, volumeID string, integrity bool) (string, error) {
klog.V(4).Infof("Fetching data encryption key for volume %q", volumeID)
keySize := keySizeCrypt
if integrity {
keySize = keySizeIntegrity
}
dek, err := c.kms.GetDEK(ctx, volumeID, keySize)
passphrase, err := c.kms.GetDEK(ctx, volumeID, constants.StateDiskKeyLength)
if err != nil {
return "", err
}
if len(passphrase) != constants.StateDiskKeyLength {
return "", fmt.Errorf("expected key length to be [%d] but got [%d]", constants.StateDiskKeyLength, len(passphrase))
}
m := &mount.SafeFormatAndMount{Exec: utilexec.New()}
return openCryptDevice(c.mapper, source, volumeID, string(dek), integrity, m.GetDiskFormat)
return openCryptDevice(c.mapper, source, volumeID, string(passphrase), integrity, m.GetDiskFormat)
}
// ResizeCryptDevice resizes the underlying crypt device and returns the mapped device path.
func (c *CryptMapper) ResizeCryptDevice(ctx context.Context, volumeID string) (string, error) {
dek, err := c.kms.GetDEK(ctx, volumeID, constants.StateDiskKeyLength)
if err != nil {
return "", err
}
klog.V(4).Infof("Resizing LUKS2 partition %q", cryptPrefix+volumeID)
if err := resizeCryptDevice(c.mapper, volumeID, string(dek)); err != nil {
return "", err
}
return cryptPrefix + volumeID, nil
}
// closeCryptDevice closes the crypt device mapped for volumeID.
@ -161,9 +201,8 @@ func closeCryptDevice(device DeviceMapper, source, volumeID, deviceType string)
defer packageLock.Unlock()
klog.V(4).Infof("Unmapping dm-%s volume %q for device %q", deviceType, cryptPrefix+volumeID, source)
cryptsetup.SetLogCallback(func(level int, message string) { klog.V(4).Infof("libcryptsetup: %s", message) })
if err := device.Init(source); err != nil {
if err := device.InitByName(volumeID); err != nil {
klog.Errorf("Could not initialize dm-%s to unmap device %q: %s", deviceType, source, err)
return fmt.Errorf("could not initialize dm-%s to unmap device %q: %w", deviceType, source, err)
}
@ -179,26 +218,18 @@ func closeCryptDevice(device DeviceMapper, source, volumeID, deviceType string)
}
// openCryptDevice maps the volume at source to the crypt device identified by volumeID.
func openCryptDevice(device DeviceMapper, source, volumeID, dek string, integrity bool, diskInfo func(disk string) (string, error)) (string, error) {
func openCryptDevice(device DeviceMapper, source, volumeID, passphrase string, integrity bool, diskInfo func(disk string) (string, error)) (string, error) {
packageLock.Lock()
defer packageLock.Unlock()
var integrityType string
keySize := len(dek)
keySize := keySizeCrypt
if integrity {
if len(dek) != keySizeIntegrity {
return "", fmt.Errorf("invalid key size for crypt with integrity: expected [%d], got [%d]", keySizeIntegrity, len(dek))
}
integrityType = "hmac(sha256)"
}
if !integrity && (len(dek) != keySizeCrypt) {
return "", fmt.Errorf("invalid key length for plain crypt: expected [%d], got [%d]", keySizeCrypt, len(dek))
keySize = keySizeIntegrity
}
klog.V(4).Infof("Mapping device %q to dm-crypt volume %q", source, cryptPrefix+volumeID)
cryptsetup.SetLogCallback(func(level int, message string) { klog.V(4).Infof("libcryptsetup: %s", message) })
// Initialize the block device
if err := device.Init(source); err != nil {
@ -240,24 +271,28 @@ func openCryptDevice(device DeviceMapper, source, volumeID, dek string, integrit
cryptsetup.GenericParams{
Cipher: "aes",
CipherMode: "xts-plain64",
VolumeKey: dek,
VolumeKeySize: keySize,
}); err != nil {
klog.Errorf("Formatting device %q failed: %s", source, err)
return "", fmt.Errorf("formatting device %q failed: %w", source, err)
}
// Add a new keyslot using the internal volume key
if err := device.KeyslotAddByVolumeKey(0, "", passphrase); err != nil {
return "", fmt.Errorf("adding keyslot: %w", err)
}
needWipe = true
}
if integrity && needWipe {
if err := performWipe(device, volumeID, dek); err != nil {
if err := performWipe(device, volumeID); err != nil {
return "", fmt.Errorf("wiping device: %w", err)
}
}
klog.V(4).Infof("Activating LUKS2 device %q", cryptPrefix+volumeID)
if err := device.ActivateByVolumeKey(volumeID, dek, keySize, 0); err != nil {
if err := device.ActivateByPassphrase(volumeID, 0, passphrase, 0); err != nil {
klog.Errorf("Trying to activate dm-crypt volume: %s", err)
return "", fmt.Errorf("trying to activate dm-crypt volume: %w", err)
}
@ -268,12 +303,12 @@ func openCryptDevice(device DeviceMapper, source, volumeID, dek string, integrit
}
// performWipe handles setting up parameters and clearing the device for dm-integrity.
func performWipe(device DeviceMapper, volumeID, dek string) error {
func performWipe(device DeviceMapper, volumeID string) error {
klog.V(4).Infof("Preparing device for dm-integrity. This may take while...")
tmpDevice := "temporary-cryptsetup-" + volumeID
// Active as temporary device
if err := device.ActivateByVolumeKey(tmpDevice, dek, len(dek), (cryptsetup.CRYPT_ACTIVATE_PRIVATE | cryptsetup.CRYPT_ACTIVATE_NO_JOURNAL)); err != nil {
if err := device.ActivateByVolumeKey(tmpDevice, "", 0, (cryptsetup.CRYPT_ACTIVATE_PRIVATE | cryptsetup.CRYPT_ACTIVATE_NO_JOURNAL)); err != nil {
klog.Errorf("Trying to activate temporary dm-crypt volume: %s", err)
return fmt.Errorf("trying to activate temporary dm-crypt volume: %w", err)
}
@ -284,7 +319,7 @@ func performWipe(device DeviceMapper, volumeID, dek string) error {
// If we are printing to a terminal we can show continues updates
progressCallback = func(size, offset uint64) int {
prog := (float64(offset) / float64(size)) * 100
fmt.Printf("\033[2K\rWipe in progress: %.2f%%", prog)
fmt.Printf("\033[1A\033[2K\rWipe in progress: %.2f%%\n", prog)
return 0
}
} else {
@ -312,11 +347,10 @@ func performWipe(device DeviceMapper, volumeID, dek string) error {
}
}
// Wipe the device using the same options as used in cryptsetup: https://gitlab.com/cryptsetup/cryptsetup/-/blob/master/src/cryptsetup.c#L1178
// Wipe the device using the same options as used in cryptsetup: https://gitlab.com/cryptsetup/cryptsetup/-/blob/v2.4.3/src/cryptsetup.c#L1345
if err := device.Wipe(cryptPrefix+tmpDevice, cryptsetup.CRYPT_WIPE_ZERO, 0, 0, 1024*1024, 0, progressCallback); err != nil {
return err
}
fmt.Println()
// Deactivate the temporary device
if err := device.Deactivate(tmpDevice); err != nil {
@ -328,6 +362,33 @@ func performWipe(device DeviceMapper, volumeID, dek string) error {
return nil
}
func resizeCryptDevice(device DeviceMapper, name, passphrase string) error {
packageLock.Lock()
defer packageLock.Unlock()
if err := device.InitByName(name); err != nil {
return fmt.Errorf("initializing device: %w", err)
}
defer device.Free()
if err := device.Load(cryptsetup.LUKS2{}); err != nil {
return fmt.Errorf("loading device: %w", err)
}
if err := device.ActivateByPassphrase("", 0, passphrase, cryptsetup.CRYPT_ACTIVATE_KEYRING_KEY); err != nil {
klog.Errorf("Unable to activate keyring for crypt device %q with passphrase: %s", name, err)
return fmt.Errorf("activating keyrung for crypt device %q with passphrase: %w", name, err)
}
if err := device.Resize(name, 0); err != nil {
klog.Errorf("Unable to resize crypt device: %s", err)
return fmt.Errorf("resizing device: %w", err)
}
klog.V(4).Infof("Successfully resized LUKS2 partition for %q", cryptPrefix+name)
return nil
}
// IsIntegrityFS checks if the fstype string contains an integrity suffix.
// If yes, returns the trimmed fstype and true, fstype and false otherwise.
func IsIntegrityFS(fstype string) (string, bool) {

View File

@ -22,27 +22,41 @@ var testDEK = []byte{
}
type stubCryptDevice struct {
deviceName string
initErr error
initByNameErr error
activateErr error
activatePassErr error
deactivateErr error
formatErr error
loadErr error
keySlotAddCalled bool
keySlotAddErr error
wipeErr error
resizeErr error
}
func (c *stubCryptDevice) Init(devicePath string) error {
func (c *stubCryptDevice) Init(string) error {
return c.initErr
}
func (c *stubCryptDevice) ActivateByVolumeKey(deviceName, volumeKey string, volumeKeySize, flags int) error {
func (c *stubCryptDevice) InitByName(string) error {
return c.initByNameErr
}
func (c *stubCryptDevice) ActivateByVolumeKey(string, string, int, int) error {
return c.activateErr
}
func (c *stubCryptDevice) Deactivate(deviceName string) error {
func (c *stubCryptDevice) ActivateByPassphrase(string, int, string, int) error {
return c.activatePassErr
}
func (c *stubCryptDevice) Deactivate(string) error {
return c.deactivateErr
}
func (c *stubCryptDevice) Format(deviceType cryptsetup.DeviceType, genericParams cryptsetup.GenericParams) error {
func (c *stubCryptDevice) Format(cryptsetup.DeviceType, cryptsetup.GenericParams) error {
return c.formatErr
}
@ -54,10 +68,19 @@ func (c *stubCryptDevice) Load(cryptsetup.DeviceType) error {
return c.loadErr
}
func (c *stubCryptDevice) Wipe(devicePath string, pattern int, offset, length uint64, wipeBlockSize int, flags int, progress func(size, offset uint64) int) error {
func (c *stubCryptDevice) KeyslotAddByVolumeKey(int, string, string) error {
c.keySlotAddCalled = true
return c.keySlotAddErr
}
func (c *stubCryptDevice) Wipe(string, int, uint64, uint64, int, int, func(size, offset uint64) int) error {
return c.wipeErr
}
func (c *stubCryptDevice) Resize(string, uint64) error {
return c.resizeErr
}
func TestCloseCryptDevice(t *testing.T) {
testCases := map[string]struct {
mapper *stubCryptDevice
@ -67,16 +90,12 @@ func TestCloseCryptDevice(t *testing.T) {
mapper: &stubCryptDevice{},
wantErr: false,
},
"error on Init": {
mapper: &stubCryptDevice{
initErr: errors.New("error"),
},
"error on InitByName": {
mapper: &stubCryptDevice{initByNameErr: errors.New("error")},
wantErr: true,
},
"error on Deactivate": {
mapper: &stubCryptDevice{
deactivateErr: errors.New("error"),
},
mapper: &stubCryptDevice{deactivateErr: errors.New("error")},
wantErr: true,
},
}
@ -105,7 +124,7 @@ func TestOpenCryptDevice(t *testing.T) {
testCases := map[string]struct {
source string
volumeID string
dek string
passphrase string
integrity bool
mapper *stubCryptDevice
diskInfo func(disk string) (string, error)
@ -114,7 +133,7 @@ func TestOpenCryptDevice(t *testing.T) {
"success with Load": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(testDEK),
passphrase: string(testDEK),
mapper: &stubCryptDevice{},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: false,
@ -122,7 +141,7 @@ func TestOpenCryptDevice(t *testing.T) {
"success with error on Load": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(testDEK),
passphrase: string(testDEK),
mapper: &stubCryptDevice{loadErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: false,
@ -130,24 +149,16 @@ func TestOpenCryptDevice(t *testing.T) {
"success with integrity": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(append(testDEK, testDEK[:32]...)),
passphrase: string(append(testDEK, testDEK[:32]...)),
integrity: true,
mapper: &stubCryptDevice{loadErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: false,
},
"incorrect key size": {
source: "/dev/some-device",
volumeID: "volume0",
dek: "short",
mapper: &stubCryptDevice{},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: true,
},
"error on Init": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(testDEK),
passphrase: string(testDEK),
mapper: &stubCryptDevice{initErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: true,
@ -155,7 +166,7 @@ func TestOpenCryptDevice(t *testing.T) {
"error on Format": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(testDEK),
passphrase: string(testDEK),
mapper: &stubCryptDevice{loadErr: someErr, formatErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: true,
@ -163,15 +174,15 @@ func TestOpenCryptDevice(t *testing.T) {
"error on Activate": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(testDEK),
mapper: &stubCryptDevice{activateErr: someErr},
passphrase: string(testDEK),
mapper: &stubCryptDevice{activatePassErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: true,
},
"error on diskInfo": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(testDEK),
passphrase: string(testDEK),
mapper: &stubCryptDevice{loadErr: someErr},
diskInfo: func(disk string) (string, error) { return "", someErr },
wantErr: true,
@ -179,7 +190,7 @@ func TestOpenCryptDevice(t *testing.T) {
"disk is already formatted": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(testDEK),
passphrase: string(testDEK),
mapper: &stubCryptDevice{loadErr: someErr},
diskInfo: func(disk string) (string, error) { return "ext4", nil },
wantErr: true,
@ -187,7 +198,7 @@ func TestOpenCryptDevice(t *testing.T) {
"error with integrity on wipe": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(append(testDEK, testDEK[:32]...)),
passphrase: string(append(testDEK, testDEK[:32]...)),
integrity: true,
mapper: &stubCryptDevice{loadErr: someErr, wipeErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
@ -196,7 +207,7 @@ func TestOpenCryptDevice(t *testing.T) {
"error with integrity on activate": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(append(testDEK, testDEK[:32]...)),
passphrase: string(append(testDEK, testDEK[:32]...)),
integrity: true,
mapper: &stubCryptDevice{loadErr: someErr, activateErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
@ -205,24 +216,41 @@ func TestOpenCryptDevice(t *testing.T) {
"error with integrity on deactivate": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(append(testDEK, testDEK[:32]...)),
passphrase: string(append(testDEK, testDEK[:32]...)),
integrity: true,
mapper: &stubCryptDevice{loadErr: someErr, deactivateErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: true,
},
"error on adding keyslot": {
source: "/dev/some-device",
volumeID: "volume0",
passphrase: string(testDEK),
mapper: &stubCryptDevice{
loadErr: someErr,
keySlotAddErr: someErr,
},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
out, err := openCryptDevice(tc.mapper, tc.source, tc.volumeID, tc.dek, tc.integrity, tc.diskInfo)
out, err := openCryptDevice(tc.mapper, tc.source, tc.volumeID, tc.passphrase, tc.integrity, tc.diskInfo)
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
assert.Equal(cryptPrefix+tc.volumeID, out)
if tc.mapper.loadErr == nil {
assert.False(tc.mapper.keySlotAddCalled)
} else {
assert.True(tc.mapper.keySlotAddCalled)
}
}
})
}
@ -232,6 +260,60 @@ func TestOpenCryptDevice(t *testing.T) {
assert.NoError(t, err)
}
func TestResizeCryptDevice(t *testing.T) {
volumeID := "pvc-123"
someErr := errors.New("error")
testCases := map[string]struct {
volumeID string
device *stubCryptDevice
wantErr bool
}{
"success": {
volumeID: volumeID,
device: &stubCryptDevice{},
},
"InitByName fails": {
volumeID: volumeID,
device: &stubCryptDevice{initByNameErr: someErr},
wantErr: true,
},
"Load fails": {
volumeID: volumeID,
device: &stubCryptDevice{loadErr: someErr},
wantErr: true,
},
"Resize fails": {
volumeID: volumeID,
device: &stubCryptDevice{resizeErr: someErr},
wantErr: true,
},
"ActivateByPassphrase fails": {
volumeID: volumeID,
device: &stubCryptDevice{activatePassErr: someErr},
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
mapper := &CryptMapper{
kms: kms.NewStaticKMS(),
mapper: tc.device,
}
res, err := mapper.ResizeCryptDevice(context.Background(), tc.volumeID)
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
assert.Equal(cryptPrefix+tc.volumeID, res)
}
})
}
}
func TestIsIntegrityFS(t *testing.T) {
testCases := map[string]struct {
wantIntegrity bool

View File

@ -29,9 +29,13 @@ func teardown() {
exec.Command("/bin/rm", "-f", DevicePath).Run()
}
func resize() {
exec.Command("/bin/dd", "if=/dev/zero", fmt.Sprintf("of=%s", DevicePath), "bs=32M", "count=1", "oflag=append", "conv=notrunc").Run()
}
func TestMain(m *testing.M) {
if os.Getuid() != 0 {
fmt.Printf("This test suite requires root privileges, as libcrypsetup uses the kernel's device mapper.\n")
fmt.Printf("This test suite requires root privileges, as libcryptsetup uses the kernel's device mapper.\n")
os.Exit(1)
}
@ -53,7 +57,7 @@ func TestOpenAndClose(t *testing.T) {
newPath, err := mapper.OpenCryptDevice(context.Background(), DevicePath, DeviceName, false)
require.NoError(err)
assert.Equal(newPath, "/dev/mapper/"+DeviceName)
assert.Equal("/dev/mapper/"+DeviceName, newPath)
// assert crypt device got created
_, err = os.Stat(newPath)
@ -62,6 +66,13 @@ func TestOpenAndClose(t *testing.T) {
_, err = os.Stat(newPath + "_dif")
assert.True(os.IsNotExist(err))
// Resize the device
resize()
resizedPath, err := mapper.ResizeCryptDevice(context.Background(), DeviceName)
require.NoError(err)
assert.Equal("/dev/mapper/"+DeviceName, resizedPath)
assert.NoError(mapper.CloseCryptDevice(DeviceName))
// assert crypt device got removed
@ -80,7 +91,7 @@ func TestOpenAndCloseIntegrity(t *testing.T) {
newPath, err := mapper.OpenCryptDevice(context.Background(), DevicePath, DeviceName, true)
require.NoError(err)
assert.Equal(newPath, "/dev/mapper/"+DeviceName)
assert.Equal("/dev/mapper/"+DeviceName, newPath)
// assert crypt device got created
_, err = os.Stat(newPath)
@ -89,6 +100,11 @@ func TestOpenAndCloseIntegrity(t *testing.T) {
_, err = os.Stat(newPath + "_dif")
assert.NoError(err)
// integrity devices do not support resizing
resize()
_, err = mapper.ResizeCryptDevice(context.Background(), DeviceName)
assert.Error(err)
assert.NoError(mapper.CloseCryptDevice(DeviceName))
// assert crypt device got removed