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( go_library(
name = "diskencryption", name = "diskencryption",
srcs = ["diskencryption.go"], srcs = [
"diskencryption.go",
"diskencryption_cgo.go",
"diskencryption_cross.go",
],
importpath = "github.com/edgelesssys/constellation/v2/bootstrapper/internal/diskencryption", importpath = "github.com/edgelesssys/constellation/v2/bootstrapper/internal/diskencryption",
visibility = ["//bootstrapper:__subpackages__"], visibility = ["//bootstrapper:__subpackages__"],
deps = [ deps = select({
"@io_bazel_rules_go//go/platform:android": [
"@com_github_martinjungblut_go_cryptsetup//:go-cryptsetup", "@com_github_martinjungblut_go_cryptsetup//:go-cryptsetup",
"@com_github_spf13_afero//:afero", "@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( go_test(
@ -18,12 +29,21 @@ go_test(
embed = [":diskencryption"], embed = [":diskencryption"],
# keep # keep
tags = ["manual"], tags = ["manual"],
deps = [ deps = select({
"@io_bazel_rules_go//go/platform:android": [
"@com_github_spf13_afero//:afero", "@com_github_spf13_afero//:afero",
"@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require", "@com_github_stretchr_testify//require",
"@org_uber_go_goleak//:goleak", "@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( 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. There should only be one instance using this package per process.
*/ */
package diskencryption 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 Copyright (c) Edgeless Systems GmbH