From 94758bc392c75e624549d86424c6bee5443e80d5 Mon Sep 17 00:00:00 2001 From: Malte Poll Date: Wed, 17 May 2023 17:35:36 +0200 Subject: [PATCH] bootstrapper: allow building without cgo dependencies for linting --- .../internal/diskencryption/BUILD.bazel | 42 ++++-- .../internal/diskencryption/diskencryption.go | 118 ---------------- .../diskencryption/diskencryption_cgo.go | 127 ++++++++++++++++++ .../diskencryption/diskencryption_cross.go | 50 +++++++ .../diskencryption/diskencryption_test.go | 2 + 5 files changed, 210 insertions(+), 129 deletions(-) create mode 100644 bootstrapper/internal/diskencryption/diskencryption_cgo.go create mode 100644 bootstrapper/internal/diskencryption/diskencryption_cross.go diff --git a/bootstrapper/internal/diskencryption/BUILD.bazel b/bootstrapper/internal/diskencryption/BUILD.bazel index 85437d8af..fd6a04826 100644 --- a/bootstrapper/internal/diskencryption/BUILD.bazel +++ b/bootstrapper/internal/diskencryption/BUILD.bazel @@ -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 = [ - "@com_github_martinjungblut_go_cryptsetup//:go-cryptsetup", - "@com_github_spf13_afero//:afero", - ], + 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 = [ - "@com_github_spf13_afero//:afero", - "@com_github_stretchr_testify//assert", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], + 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( diff --git a/bootstrapper/internal/diskencryption/diskencryption.go b/bootstrapper/internal/diskencryption/diskencryption.go index 277f6740b..2a498b655 100644 --- a/bootstrapper/internal/diskencryption/diskencryption.go +++ b/bootstrapper/internal/diskencryption/diskencryption.go @@ -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) diff --git a/bootstrapper/internal/diskencryption/diskencryption_cgo.go b/bootstrapper/internal/diskencryption/diskencryption_cgo.go new file mode 100644 index 000000000..250252bc5 --- /dev/null +++ b/bootstrapper/internal/diskencryption/diskencryption_cgo.go @@ -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) diff --git a/bootstrapper/internal/diskencryption/diskencryption_cross.go b/bootstrapper/internal/diskencryption/diskencryption_cross.go new file mode 100644 index 000000000..58f732109 --- /dev/null +++ b/bootstrapper/internal/diskencryption/diskencryption_cross.go @@ -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") +} diff --git a/bootstrapper/internal/diskencryption/diskencryption_test.go b/bootstrapper/internal/diskencryption/diskencryption_test.go index 1450365a1..29673869a 100644 --- a/bootstrapper/internal/diskencryption/diskencryption_test.go +++ b/bootstrapper/internal/diskencryption/diskencryption_test.go @@ -1,3 +1,5 @@ +//go:build linux && cgo + /* Copyright (c) Edgeless Systems GmbH