disk-mapper: set LUKS2 token to allow reusing unintialized state disks (#2083)

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2023-07-18 16:20:03 +02:00 committed by GitHub
parent dc373971b2
commit 6a40c73ff7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 161 additions and 31 deletions

View file

@ -24,23 +24,30 @@ import (
// DiskEncryption handles actions for formatting and mapping crypt devices.
type DiskEncryption struct {
device cryptDevice
log *logger.Logger
device cryptDevice
devicePath string
log *logger.Logger
}
// New creates a new crypt device for the device at path.
func New(path string, log *logger.Logger) (*DiskEncryption, func(), error) {
device := cryptsetup.New()
free, err := device.Init(path)
_, err := device.Init(path)
if err != nil {
return nil, nil, fmt.Errorf("initializing crypt device for disk %q: %w", path, err)
}
return &DiskEncryption{device: device, log: log}, free, nil
d := &DiskEncryption{device: device, devicePath: path, log: log}
return d, d.free, nil
}
// IsLUKSDevice returns true if the device is formatted as a LUKS device.
func (d *DiskEncryption) IsLUKSDevice() bool {
return d.device.LoadLUKS2() == nil
// IsInitialized returns true if the device is formatted as a LUKS device,
// and has been successfully initialized before (successfully joined a cluster).
func (d *DiskEncryption) IsInitialized() bool {
if err := d.device.LoadLUKS2(); err != nil {
return false
}
return d.device.ConstellationStateDiskTokenIsInitialized()
}
// DiskUUID gets the device's UUID.
@ -50,6 +57,14 @@ func (d *DiskEncryption) DiskUUID() (string, error) {
// FormatDisk formats the disk and adds passphrase in keyslot 0.
func (d *DiskEncryption) FormatDisk(passphrase string) error {
// Successfully calling LoadLUKS2() before FormatDisk() will cause format to fail.
// To make sure format is idempotent, we need to run it on a freshly initialized device.
// Therefore we free the device and reinitialize it.
d.free()
if _, err := d.device.Init(d.devicePath); err != nil {
return fmt.Errorf("re-initializing crypt device for disk %q: %w", d.devicePath, err)
}
if err := d.device.Format(cryptsetup.FormatIntegrity); err != nil {
return fmt.Errorf("formatting disk: %w", err)
}
@ -63,6 +78,9 @@ func (d *DiskEncryption) FormatDisk(passphrase string) error {
return fmt.Errorf("wiping disk: %w", err)
}
if err := d.device.SetConstellationStateDiskToken(cryptsetup.SetDiskNotInitialized); err != nil {
return fmt.Errorf("setting disk token: %w", err)
}
return nil
}
@ -96,13 +114,21 @@ func (d *DiskEncryption) Wipe(blockWipeSize int) error {
return nil
}
func (d *DiskEncryption) free() {
d.device.Free()
}
type cryptDevice interface {
ActivateByPassphrase(deviceName string, keyslot int, passphrase string, flags int) error
ActivateByVolumeKey(deviceName string, volumeKey string, volumeKeySize int, flags int) error
Deactivate(deviceName string) error
Format(integrity bool) error
Free()
GetUUID() (string, error)
Init(path string) (func(), error)
LoadLUKS2() error
KeyslotAddByVolumeKey(keyslot int, volumeKey string, passphrase string) error
SetConstellationStateDiskToken(diskIsInitialized bool) error
ConstellationStateDiskTokenIsInitialized() bool
Wipe(name string, wipeBlockSize int, flags int, logCallback func(size, offset uint64), logFrequency time.Duration) error
}

View file

@ -413,6 +413,10 @@ func (s *stubMapper) UnmapDisk(string) error {
return s.unmapDiskErr
}
func (s *stubMapper) SetDiskToInitialized() error {
return nil
}
type stubMounter struct {
mountCalled bool
mountErr error

View file

@ -9,6 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
package integration
import (
"encoding/json"
"flag"
"fmt"
"os"
@ -16,8 +17,9 @@ import (
"testing"
"github.com/edgelesssys/constellation/v2/disk-mapper/internal/diskencryption"
ccryptsetup "github.com/edgelesssys/constellation/v2/internal/cryptsetup"
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/martinjungblut/go-cryptsetup"
cryptsetup "github.com/martinjungblut/go-cryptsetup"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
@ -68,7 +70,7 @@ func TestMapper(t *testing.T) {
require.NoError(err, "failed to initialize crypt device")
defer free()
assert.False(mapper.IsLUKSDevice())
assert.False(mapper.IsInitialized())
// Format and map disk
passphrase := "unit-test"
@ -76,23 +78,33 @@ func TestMapper(t *testing.T) {
require.NoError(mapper.MapDisk(mappedDevice, passphrase), "failed to map disk")
require.NoError(mapper.UnmapDisk(mappedDevice), "failed to remove disk mapping")
assert.True(mapper.IsLUKSDevice())
// Make sure token was set
ccrypt := ccryptsetup.New()
freeDevice, err := ccrypt.Init(devicePath)
require.NoError(err, "failed to initialize crypt device")
defer freeDevice()
require.NoError(ccrypt.LoadLUKS2(), "failed to load LUKS2")
tokenJSON, err := ccrypt.TokenJSONGet(ccryptsetup.ConstellationStateDiskTokenID)
require.NoError(err, "token should have been set")
var token struct {
Type string `json:"type"`
Keyslots []string `json:"keyslots"`
DiskIsInitialized bool `json:"diskIsInitialized"`
}
require.NoError(json.Unmarshal([]byte(tokenJSON), &token))
assert.False(token.DiskIsInitialized, "disk should be marked as not initialized")
assert.False(ccrypt.ConstellationStateDiskTokenIsInitialized(), "disk should be marked as not initialized")
// Disk should still be marked as not initialized because token is set to false.
assert.False(mapper.IsInitialized())
// Try to map disk with incorrect passphrase
assert.Error(mapper.MapDisk(mappedDevice, "invalid-passphrase"), "was able to map disk with incorrect passphrase")
}
/*
type fakeMetadataAPI struct{}
func (f *fakeMetadataAPI) List(ctx context.Context) ([]metadata.InstanceMetadata, error) {
return []metadata.InstanceMetadata{
{
Name: "instanceName",
ProviderID: "fake://instance-id",
Role: role.Unknown,
VPCIP: "192.0.2.1",
},
}, nil
// Disk can be reformatted without manually re-initializing a mapper
passphrase2 := passphrase + "2"
require.NoError(mapper.FormatDisk(passphrase2), "failed to format disk")
require.NoError(mapper.MapDisk(mappedDevice, passphrase2), "failed to map disk")
require.NoError(mapper.UnmapDisk(mappedDevice), "failed to remove disk mapping")
}
*/