constellation/disk-mapper/internal/diskencryption/diskencryption.go
miampf f16ccf5679
rewrote packages
keyservice
joinservice
upgrade-agent
measurement-reader
debugd
disk-mapper

rewrote joinservice main

rewrote some unit tests

rewrote upgrade-agent + some grpc functions

rewrote measurement-reader

rewrote debugd

removed unused import

removed forgotten zap reference in measurements reader

rewrote disk-mapper + tests

rewrote packages

verify
disk-mapper
malicious join
bootstrapper
attestationconfigapi
versionapi
internal/cloud/azure
disk-mapper tests
image/upload/internal/cmd

rewrote verify (WIP with loglevel increase)

rewrote forgotten zap references in disk-mapper

rewrote malicious join

rewrote bootstrapper

rewrote parts of internal/

rewrote attestationconfigapi (WIP)

rewrote versionapi cli

rewrote internal/cloud/azure

rewrote disk-mapper tests (untested by me rn)

rewrote image/upload/internal/cmd

removed forgotten zap references in verify/cmd

rewrote packages

hack/oci-pin
hack/qemu-metadata-api
debugd/internal/debugd/deploy
hack/bazel-deps-mirror
cli/internal/cmd
cli-k8s-compatibility

rewrote hack/qemu-metadata-api/server

rewrote debugd/internal/debugd/deploy

rewrote hack/bazel-deps-mirror

rewrote rest of hack/qemu-metadata-api

rewrote forgotten zap references in joinservice server

rewrote cli/internal/cmd

rewrote cli-k8s-compatibility

rewrote packages

internal/staticupload
e2d/internal/upgrade
internal/constellation/helm
internal/attestation/aws/snp
internal/attestation/azure/trustedlaunch
joinservice/internal/certcache/amkds

some missed unit tests

rewrote e2e/internal/upgrade

rewrote internal/constellation/helm

internal/attestation/aws/snp

internal/attestation/azure/trustedlaunch

joinservice/internal/certcache/amkds

search and replace test logging over all left *_test.go
2024-02-08 13:14:14 +01:00

133 lines
4.2 KiB
Go

/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
/*
Package diskencryption uses libcryptsetup to format and map crypt devices.
This is used by the disk-mapper to set up a node's state disk.
All interaction with libcryptsetup should be done here.
*/
package diskencryption
import (
"fmt"
"log/slog"
"time"
"github.com/edgelesssys/constellation/v2/internal/cryptsetup"
)
// DiskEncryption handles actions for formatting and mapping crypt devices.
type DiskEncryption struct {
device cryptDevice
devicePath string
log *slog.Logger
}
// New creates a new crypt device for the device at path.
func New(path string, log *slog.Logger) (*DiskEncryption, func(), error) {
device := cryptsetup.New()
_, err := device.Init(path)
if err != nil {
return nil, nil, fmt.Errorf("initializing crypt device for disk %q: %w", path, err)
}
d := &DiskEncryption{device: device, devicePath: path, log: log}
return d, d.free, 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.
func (d *DiskEncryption) DiskUUID() (string, error) {
return d.device.GetUUID()
}
// 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)
}
if err := d.device.KeyslotAddByVolumeKey(0, "", passphrase); err != nil {
return fmt.Errorf("adding keyslot: %w", err)
}
// wipe using 64MiB block size
if err := d.Wipe(67108864); err != nil {
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
}
// MapDisk maps a crypt device to /dev/mapper/target using the provided passphrase.
func (d *DiskEncryption) MapDisk(target, passphrase string) error {
if err := d.device.ActivateByPassphrase(target, 0, passphrase, cryptsetup.ReadWriteQueueBypass); err != nil {
return fmt.Errorf("mapping disk as %q: %w", target, err)
}
return nil
}
// UnmapDisk removes the mapping of target.
func (d *DiskEncryption) UnmapDisk(target string) error {
return d.device.Deactivate(target)
}
// Wipe overwrites the device with zeros to initialize integrity checksums.
func (d *DiskEncryption) Wipe(blockWipeSize int) error {
logProgress := func(size, offset uint64) {
prog := (float64(offset) / float64(size)) * 100
d.log.With(slog.String("progress", fmt.Sprintf("%.2f%%", prog))).Info("Wiping disk")
}
start := time.Now()
// wipe the device
if err := d.device.Wipe("integrity", blockWipeSize, 0, logProgress, 30*time.Second); err != nil {
return fmt.Errorf("wiping disk: %w", err)
}
d.log.With(slog.Duration("duration", time.Since(start))).Info("Wiping disk successful")
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
}