mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-24 23:19:39 -05:00
Add resize functions
Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
parent
2b80341d99
commit
6b3d45dd09
2
go.mod
2
go.mod
@ -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
4
go.sum
@ -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=
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user