constellation/bootstrapper/internal/diskencryption/diskencryption.go
Daniel Weiße 1077b7a48e
bootstrapper: wipe disk and reboot on non-recoverable error (#2971)
* Let JoinClient return fatal errors
* Mark disk for wiping if JoinClient or InitServer return errors
* Reboot system if bootstrapper detects an error
* Refactor joinClient start/stop implementation
* Fix joining nodes retrying kubeadm 3 times in all cases
* Write non-recoverable failures to syslog before rebooting

---------

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
2024-03-12 11:43:38 +01:00

83 lines
2.3 KiB
Go

/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
// Package diskencryption handles interaction with a node's state disk.
package diskencryption
import (
"fmt"
"github.com/edgelesssys/constellation/v2/internal/cryptsetup"
"github.com/spf13/afero"
)
const (
stateMapperDevice = "state"
initialKeyPath = "/run/cryptsetup-keys.d/state.key"
keyslot = 0
)
// DiskEncryption manages the encrypted state mapper device.
type DiskEncryption struct {
fs afero.Fs
device cryptdevice
}
// New creates a new Cryptsetup.
func New() *DiskEncryption {
return &DiskEncryption{
fs: afero.NewOsFs(),
device: cryptsetup.New(),
}
}
// Open opens the cryptdevice.
func (c *DiskEncryption) Open() (free func(), err error) {
return c.device.InitByName(stateMapperDevice)
}
// UUID gets the device's UUID.
// Only works after calling Open().
func (c *DiskEncryption) UUID() (string, error) {
return c.device.GetUUID()
}
// UpdatePassphrase switches the initial random passphrase of the mapped crypt device to a permanent passphrase.
// Only works after calling Open().
func (c *DiskEncryption) UpdatePassphrase(passphrase string) error {
initialPassphrase, err := c.getInitialPassphrase()
if err != nil {
return err
}
if err := c.device.KeyslotChangeByPassphrase(keyslot, keyslot, initialPassphrase, passphrase); err != nil {
return err
}
// Set token as initialized.
return c.device.SetConstellationStateDiskToken(cryptsetup.SetDiskInitialized)
}
// MarkDiskForReset marks the state disk as not initialized so it may be wiped (reset) on reboot.
func (c *DiskEncryption) MarkDiskForReset() error {
return c.device.SetConstellationStateDiskToken(cryptsetup.SetDiskNotInitialized)
}
// getInitialPassphrase retrieves the initial passphrase used on first boot.
func (c *DiskEncryption) getInitialPassphrase() (string, error) {
passphrase, err := afero.ReadFile(c.fs, initialKeyPath)
if err != nil {
return "", fmt.Errorf("reading first boot encryption passphrase from disk: %w", err)
}
return string(passphrase), nil
}
type cryptdevice interface {
InitByName(name string) (func(), error)
GetUUID() (string, error)
KeyslotChangeByPassphrase(currentKeyslot int, newKeyslot int, currentPassphrase string, newPassphrase string) error
SetConstellationStateDiskToken(bool) error
}