bootstrapper: allow building without cgo dependencies for linting

This commit is contained in:
Malte Poll 2023-05-17 17:35:36 +02:00 committed by Malte Poll
parent 78085cba68
commit 94758bc392
5 changed files with 210 additions and 129 deletions

View File

@ -3,13 +3,24 @@ load("//bazel/go:go_test.bzl", "go_ld_test", "go_test")
go_library(
name = "diskencryption",
srcs = ["diskencryption.go"],
srcs = [
"diskencryption.go",
"diskencryption_cgo.go",
"diskencryption_cross.go",
],
importpath = "github.com/edgelesssys/constellation/v2/bootstrapper/internal/diskencryption",
visibility = ["//bootstrapper:__subpackages__"],
deps = [
deps = select({
"@io_bazel_rules_go//go/platform:android": [
"@com_github_martinjungblut_go_cryptsetup//:go-cryptsetup",
"@com_github_spf13_afero//:afero",
],
"@io_bazel_rules_go//go/platform:linux": [
"@com_github_martinjungblut_go_cryptsetup//:go-cryptsetup",
"@com_github_spf13_afero//:afero",
],
"//conditions:default": [],
}),
)
go_test(
@ -18,12 +29,21 @@ go_test(
embed = [":diskencryption"],
# keep
tags = ["manual"],
deps = [
deps = select({
"@io_bazel_rules_go//go/platform:android": [
"@com_github_spf13_afero//:afero",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",
"@org_uber_go_goleak//:goleak",
],
"@io_bazel_rules_go//go/platform:linux": [
"@com_github_spf13_afero//:afero",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",
"@org_uber_go_goleak//:goleak",
],
"//conditions:default": [],
}),
)
go_ld_test(

View File

@ -11,121 +11,3 @@ This package is not thread safe, since libcryptsetup is not thread safe.
There should only be one instance using this package per process.
*/
package diskencryption
import (
"errors"
"fmt"
"sync"
"github.com/martinjungblut/go-cryptsetup"
"github.com/spf13/afero"
)
const (
stateMapperDevice = "state"
initialKeyPath = "/run/cryptsetup-keys.d/state.key"
keyslot = 0
)
var (
// packageLock is needed to block concurrent use of package functions, since libcryptsetup is not thread safe.
// See: https://gitlab.com/cryptsetup/cryptsetup/-/issues/710
// https://stackoverflow.com/questions/30553386/cryptsetup-backend-safe-with-multithreading
packageLock = sync.Mutex{}
errDeviceNotOpen = errors.New("cryptdevice not open")
errDeviceAlreadyOpen = errors.New("cryptdevice already open")
)
// Cryptsetup manages the encrypted state mapper device.
type Cryptsetup struct {
fs afero.Fs
device cryptdevice
initByName initByName
}
// New creates a new Cryptsetup.
func New() *Cryptsetup {
return &Cryptsetup{
fs: afero.NewOsFs(),
initByName: func(name string) (cryptdevice, error) {
return cryptsetup.InitByName(name)
},
}
}
// Open opens the cryptdevice.
func (c *Cryptsetup) Open() error {
packageLock.Lock()
defer packageLock.Unlock()
if c.device != nil {
return errDeviceAlreadyOpen
}
var err error
c.device, err = c.initByName(stateMapperDevice)
if err != nil {
return fmt.Errorf("initializing crypt device for mapped device %q: %w", stateMapperDevice, err)
}
return nil
}
// Close closes the cryptdevice.
func (c *Cryptsetup) Close() error {
packageLock.Lock()
defer packageLock.Unlock()
if c.device == nil {
return errDeviceNotOpen
}
c.device.Free()
c.device = nil
return nil
}
// UUID gets the device's UUID.
// Only works after calling Open().
func (c *Cryptsetup) UUID() (string, error) {
packageLock.Lock()
defer packageLock.Unlock()
if c.device == nil {
return "", errDeviceNotOpen
}
uuid := c.device.GetUUID()
if uuid == "" {
return "", fmt.Errorf("unable to get UUID for mapped device %q", stateMapperDevice)
}
return uuid, nil
}
// UpdatePassphrase switches the initial random passphrase of the mapped crypt device to a permanent passphrase.
// Only works after calling Open().
func (c *Cryptsetup) UpdatePassphrase(passphrase string) error {
packageLock.Lock()
defer packageLock.Unlock()
if c.device == nil {
return errDeviceNotOpen
}
initialPassphrase, err := c.getInitialPassphrase()
if err != nil {
return err
}
if err := c.device.KeyslotChangeByPassphrase(keyslot, keyslot, initialPassphrase, passphrase); err != nil {
return fmt.Errorf("changing passphrase for mapped device %q: %w", stateMapperDevice, err)
}
return nil
}
// getInitialPassphrase retrieves the initial passphrase used on first boot.
func (c *Cryptsetup) 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 {
GetUUID() string
KeyslotChangeByPassphrase(currentKeyslot int, newKeyslot int, currentPassphrase string, newPassphrase string) error
Free() bool
}
type initByName func(name string) (cryptdevice, error)

View File

@ -0,0 +1,127 @@
//go:build linux && cgo
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package diskencryption
import (
"errors"
"fmt"
"sync"
"github.com/martinjungblut/go-cryptsetup"
"github.com/spf13/afero"
)
const (
stateMapperDevice = "state"
initialKeyPath = "/run/cryptsetup-keys.d/state.key"
keyslot = 0
)
var (
// packageLock is needed to block concurrent use of package functions, since libcryptsetup is not thread safe.
// See: https://gitlab.com/cryptsetup/cryptsetup/-/issues/710
// https://stackoverflow.com/questions/30553386/cryptsetup-backend-safe-with-multithreading
packageLock = sync.Mutex{}
errDeviceNotOpen = errors.New("cryptdevice not open")
errDeviceAlreadyOpen = errors.New("cryptdevice already open")
)
// Cryptsetup manages the encrypted state mapper device.
type Cryptsetup struct {
fs afero.Fs
device cryptdevice
initByName initByName
}
// New creates a new Cryptsetup.
func New() *Cryptsetup {
return &Cryptsetup{
fs: afero.NewOsFs(),
initByName: func(name string) (cryptdevice, error) {
return cryptsetup.InitByName(name)
},
}
}
// Open opens the cryptdevice.
func (c *Cryptsetup) Open() error {
packageLock.Lock()
defer packageLock.Unlock()
if c.device != nil {
return errDeviceAlreadyOpen
}
var err error
c.device, err = c.initByName(stateMapperDevice)
if err != nil {
return fmt.Errorf("initializing crypt device for mapped device %q: %w", stateMapperDevice, err)
}
return nil
}
// Close closes the cryptdevice.
func (c *Cryptsetup) Close() error {
packageLock.Lock()
defer packageLock.Unlock()
if c.device == nil {
return errDeviceNotOpen
}
c.device.Free()
c.device = nil
return nil
}
// UUID gets the device's UUID.
// Only works after calling Open().
func (c *Cryptsetup) UUID() (string, error) {
packageLock.Lock()
defer packageLock.Unlock()
if c.device == nil {
return "", errDeviceNotOpen
}
uuid := c.device.GetUUID()
if uuid == "" {
return "", fmt.Errorf("unable to get UUID for mapped device %q", stateMapperDevice)
}
return uuid, nil
}
// UpdatePassphrase switches the initial random passphrase of the mapped crypt device to a permanent passphrase.
// Only works after calling Open().
func (c *Cryptsetup) UpdatePassphrase(passphrase string) error {
packageLock.Lock()
defer packageLock.Unlock()
if c.device == nil {
return errDeviceNotOpen
}
initialPassphrase, err := c.getInitialPassphrase()
if err != nil {
return err
}
if err := c.device.KeyslotChangeByPassphrase(keyslot, keyslot, initialPassphrase, passphrase); err != nil {
return fmt.Errorf("changing passphrase for mapped device %q: %w", stateMapperDevice, err)
}
return nil
}
// getInitialPassphrase retrieves the initial passphrase used on first boot.
func (c *Cryptsetup) 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 {
GetUUID() string
KeyslotChangeByPassphrase(currentKeyslot int, newKeyslot int, currentPassphrase string, newPassphrase string) error
Free() bool
}
type initByName func(name string) (cryptdevice, error)

View File

@ -0,0 +1,50 @@
//go:build !linux || !cgo
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
/*
Package diskencryption handles interaction with a node's state disk.
This package is not thread safe, since libcryptsetup is not thread safe.
There should only be one instance using this package per process.
*/
package diskencryption
import "errors"
// Cryptsetup manages the encrypted state mapper device.
type Cryptsetup struct{}
// New creates a new Cryptsetup.
// This function panics if CGO is disabled.
func New() *Cryptsetup {
return &Cryptsetup{}
}
// Open opens the cryptdevice.
// This function does nothing if CGO is disabled.
func (c *Cryptsetup) Open() error {
return errors.New("using cryptsetup requires building with CGO")
}
// Close closes the cryptdevice.
// This function errors if CGO is disabled.
func (c *Cryptsetup) Close() error {
return errors.New("using cryptsetup requires building with CGO")
}
// UUID gets the device's UUID.
// This function errors if CGO is disabled.
func (c *Cryptsetup) UUID() (string, error) {
return "", errors.New("using cryptsetup requires building with CGO")
}
// UpdatePassphrase switches the initial random passphrase of the mapped crypt device to a permanent passphrase.
// This function errors if CGO is disabled.
func (c *Cryptsetup) UpdatePassphrase(_ string) error {
return errors.New("using cryptsetup requires building with CGO")
}

View File

@ -1,3 +1,5 @@
//go:build linux && cgo
/*
Copyright (c) Edgeless Systems GmbH