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 {
initErr error
activateErr error
deactivateErr error
formatErr error
loadErr error
wipeErr error
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,
},
}
@ -103,113 +122,116 @@ func TestOpenCryptDevice(t *testing.T) {
someErr := errors.New("error")
testCases := map[string]struct {
source string
volumeID string
dek string
integrity bool
mapper *stubCryptDevice
diskInfo func(disk string) (string, error)
wantErr bool
source string
volumeID string
passphrase string
integrity bool
mapper *stubCryptDevice
diskInfo func(disk string) (string, error)
wantErr bool
}{
"success with Load": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(testDEK),
mapper: &stubCryptDevice{},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: false,
source: "/dev/some-device",
volumeID: "volume0",
passphrase: string(testDEK),
mapper: &stubCryptDevice{},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: false,
},
"success with error on Load": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(testDEK),
mapper: &stubCryptDevice{loadErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: false,
source: "/dev/some-device",
volumeID: "volume0",
passphrase: string(testDEK),
mapper: &stubCryptDevice{loadErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: false,
},
"success with integrity": {
source: "/dev/some-device",
volumeID: "volume0",
dek: 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,
source: "/dev/some-device",
volumeID: "volume0",
passphrase: string(append(testDEK, testDEK[:32]...)),
integrity: true,
mapper: &stubCryptDevice{loadErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: false,
},
"error on Init": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(testDEK),
mapper: &stubCryptDevice{initErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: true,
source: "/dev/some-device",
volumeID: "volume0",
passphrase: string(testDEK),
mapper: &stubCryptDevice{initErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: true,
},
"error on Format": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(testDEK),
mapper: &stubCryptDevice{loadErr: someErr, formatErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: true,
source: "/dev/some-device",
volumeID: "volume0",
passphrase: string(testDEK),
mapper: &stubCryptDevice{loadErr: someErr, formatErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: true,
},
"error on Activate": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(testDEK),
mapper: &stubCryptDevice{activateErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: true,
source: "/dev/some-device",
volumeID: "volume0",
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),
mapper: &stubCryptDevice{loadErr: someErr},
diskInfo: func(disk string) (string, error) { return "", someErr },
wantErr: true,
source: "/dev/some-device",
volumeID: "volume0",
passphrase: string(testDEK),
mapper: &stubCryptDevice{loadErr: someErr},
diskInfo: func(disk string) (string, error) { return "", someErr },
wantErr: true,
},
"disk is already formatted": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(testDEK),
mapper: &stubCryptDevice{loadErr: someErr},
diskInfo: func(disk string) (string, error) { return "ext4", nil },
wantErr: true,
source: "/dev/some-device",
volumeID: "volume0",
passphrase: string(testDEK),
mapper: &stubCryptDevice{loadErr: someErr},
diskInfo: func(disk string) (string, error) { return "ext4", nil },
wantErr: true,
},
"error with integrity on wipe": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(append(testDEK, testDEK[:32]...)),
integrity: true,
mapper: &stubCryptDevice{loadErr: someErr, wipeErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: true,
source: "/dev/some-device",
volumeID: "volume0",
passphrase: string(append(testDEK, testDEK[:32]...)),
integrity: true,
mapper: &stubCryptDevice{loadErr: someErr, wipeErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: true,
},
"error with integrity on activate": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(append(testDEK, testDEK[:32]...)),
integrity: true,
mapper: &stubCryptDevice{loadErr: someErr, activateErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: true,
source: "/dev/some-device",
volumeID: "volume0",
passphrase: string(append(testDEK, testDEK[:32]...)),
integrity: true,
mapper: &stubCryptDevice{loadErr: someErr, activateErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: true,
},
"error with integrity on deactivate": {
source: "/dev/some-device",
volumeID: "volume0",
dek: string(append(testDEK, testDEK[:32]...)),
integrity: true,
mapper: &stubCryptDevice{loadErr: someErr, deactivateErr: someErr},
diskInfo: func(disk string) (string, error) { return "", nil },
wantErr: true,
source: "/dev/some-device",
volumeID: "volume0",
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,
},
}
@ -217,12 +239,18 @@ func TestOpenCryptDevice(t *testing.T) {
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