mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-02-03 02:50:03 -05:00
disk-mapper: allow building without cgo dependencies for linting
This commit is contained in:
parent
ad85dacf6a
commit
21d4e5864f
@ -5,13 +5,60 @@ go_library(
|
|||||||
srcs = [
|
srcs = [
|
||||||
"cryptdevice.go",
|
"cryptdevice.go",
|
||||||
"mapper.go",
|
"mapper.go",
|
||||||
|
"mapper_cgo.go",
|
||||||
|
"mapper_cross.go",
|
||||||
],
|
],
|
||||||
importpath = "github.com/edgelesssys/constellation/v2/disk-mapper/internal/mapper",
|
importpath = "github.com/edgelesssys/constellation/v2/disk-mapper/internal/mapper",
|
||||||
visibility = ["//disk-mapper:__subpackages__"],
|
visibility = ["//disk-mapper:__subpackages__"],
|
||||||
deps = [
|
deps = select({
|
||||||
"//internal/cryptsetup",
|
"@io_bazel_rules_go//go/platform:aix": [
|
||||||
"//internal/logger",
|
"//internal/logger",
|
||||||
"@com_github_martinjungblut_go_cryptsetup//:go-cryptsetup",
|
],
|
||||||
"@org_uber_go_zap//:zap",
|
"@io_bazel_rules_go//go/platform:android": [
|
||||||
],
|
"//internal/cryptsetup",
|
||||||
|
"//internal/logger",
|
||||||
|
"@com_github_martinjungblut_go_cryptsetup//:go-cryptsetup",
|
||||||
|
"@org_uber_go_zap//:zap",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:darwin": [
|
||||||
|
"//internal/logger",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:dragonfly": [
|
||||||
|
"//internal/logger",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:freebsd": [
|
||||||
|
"//internal/logger",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:illumos": [
|
||||||
|
"//internal/logger",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:ios": [
|
||||||
|
"//internal/logger",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:js": [
|
||||||
|
"//internal/logger",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:linux": [
|
||||||
|
"//internal/cryptsetup",
|
||||||
|
"//internal/logger",
|
||||||
|
"@com_github_martinjungblut_go_cryptsetup//:go-cryptsetup",
|
||||||
|
"@org_uber_go_zap//:zap",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:netbsd": [
|
||||||
|
"//internal/logger",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:openbsd": [
|
||||||
|
"//internal/logger",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:plan9": [
|
||||||
|
"//internal/logger",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:solaris": [
|
||||||
|
"//internal/logger",
|
||||||
|
],
|
||||||
|
"@io_bazel_rules_go//go/platform:windows": [
|
||||||
|
"//internal/logger",
|
||||||
|
],
|
||||||
|
"//conditions:default": [],
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//go:build linux && cgo
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright (c) Edgeless Systems GmbH
|
Copyright (c) Edgeless Systems GmbH
|
||||||
|
|
||||||
|
@ -14,152 +14,3 @@ All interaction with libcryptsetup should be done here.
|
|||||||
Warning: This package is not thread safe, since libcryptsetup is not thread safe.
|
Warning: This package is not thread safe, since libcryptsetup is not thread safe.
|
||||||
*/
|
*/
|
||||||
package mapper
|
package mapper
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
ccryptsetup "github.com/edgelesssys/constellation/v2/internal/cryptsetup"
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
|
||||||
cryptsetup "github.com/martinjungblut/go-cryptsetup"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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
|
|
||||||
var packageLock = sync.Mutex{}
|
|
||||||
|
|
||||||
// Mapper handles actions for formatting and mapping crypt devices.
|
|
||||||
type Mapper struct {
|
|
||||||
device cryptDevice
|
|
||||||
log *logger.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new crypt device for the device at path.
|
|
||||||
func New(path string, log *logger.Logger) (*Mapper, error) {
|
|
||||||
packageLock.Lock()
|
|
||||||
device, err := cryptsetup.Init(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("initializing crypt device for disk %q: %w", path, err)
|
|
||||||
}
|
|
||||||
return &Mapper{device: device, log: log}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes and frees memory allocated for the crypt device.
|
|
||||||
func (m *Mapper) Close() error {
|
|
||||||
defer packageLock.Unlock()
|
|
||||||
if m.device.Free() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return errors.New("unable to close crypt device")
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsLUKSDevice returns true if the device is formatted as a LUKS device.
|
|
||||||
func (m *Mapper) IsLUKSDevice() bool {
|
|
||||||
return m.device.Load(cryptsetup.LUKS2{}) == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DiskUUID gets the device's UUID.
|
|
||||||
func (m *Mapper) DiskUUID() string {
|
|
||||||
return strings.ToLower(m.device.GetUUID())
|
|
||||||
}
|
|
||||||
|
|
||||||
// FormatDisk formats the disk and adds passphrase in keyslot 0.
|
|
||||||
func (m *Mapper) FormatDisk(passphrase string) error {
|
|
||||||
luksParams := cryptsetup.LUKS2{
|
|
||||||
SectorSize: 4096,
|
|
||||||
Integrity: "hmac(sha256)",
|
|
||||||
PBKDFType: &cryptsetup.PbkdfType{
|
|
||||||
// Use low memory recommendation from https://datatracker.ietf.org/doc/html/rfc9106#section-7
|
|
||||||
Type: "argon2id",
|
|
||||||
TimeMs: 2000,
|
|
||||||
Iterations: 3,
|
|
||||||
ParallelThreads: 4,
|
|
||||||
MaxMemoryKb: 65536, // ~64MiB
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
genericParams := cryptsetup.GenericParams{
|
|
||||||
Cipher: "aes",
|
|
||||||
CipherMode: "xts-plain64",
|
|
||||||
VolumeKeySize: 96, // 32*2 bytes for aes-xts-plain64 encryption, 32 bytes for hmac(sha256) integrity
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := m.device.Format(luksParams, genericParams); err != nil {
|
|
||||||
return fmt.Errorf("formatting disk: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := m.device.KeyslotAddByVolumeKey(0, "", passphrase); err != nil {
|
|
||||||
return fmt.Errorf("adding keyslot: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// wipe using 64MiB block size
|
|
||||||
if err := m.Wipe(67108864); err != nil {
|
|
||||||
return fmt.Errorf("wiping disk: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MapDisk maps a crypt device to /dev/mapper/target using the provided passphrase.
|
|
||||||
func (m *Mapper) MapDisk(target, passphrase string) error {
|
|
||||||
if err := m.device.ActivateByPassphrase(target, 0, passphrase, ccryptsetup.ReadWriteQueueBypass); err != nil {
|
|
||||||
return fmt.Errorf("mapping disk as %q: %w", target, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmapDisk removes the mapping of target.
|
|
||||||
func (m *Mapper) UnmapDisk(target string) error {
|
|
||||||
return m.device.Deactivate(target)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wipe overwrites the device with zeros to initialize integrity checksums.
|
|
||||||
func (m *Mapper) Wipe(blockWipeSize int) error {
|
|
||||||
// Activate as temporary device using the internal volume key
|
|
||||||
tmpDevice := "tmp-cryptsetup-integrity"
|
|
||||||
if err := m.device.ActivateByVolumeKey(tmpDevice, "", 0, (cryptsetup.CRYPT_ACTIVATE_PRIVATE | cryptsetup.CRYPT_ACTIVATE_NO_JOURNAL)); err != nil {
|
|
||||||
return fmt.Errorf("activating as temporary device: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// set progress logging callback once every 30 seconds
|
|
||||||
ticker := time.NewTicker(30 * time.Second)
|
|
||||||
firstReq := make(chan struct{}, 1)
|
|
||||||
firstReq <- struct{}{}
|
|
||||||
defer ticker.Stop()
|
|
||||||
logProgress := func(size, offset uint64) {
|
|
||||||
prog := (float64(offset) / float64(size)) * 100
|
|
||||||
m.log.With(zap.String("progress", fmt.Sprintf("%.2f%%", prog))).Infof("Wiping disk")
|
|
||||||
}
|
|
||||||
|
|
||||||
progressCallback := func(size, offset uint64) int {
|
|
||||||
select {
|
|
||||||
case <-firstReq:
|
|
||||||
logProgress(size, offset)
|
|
||||||
case <-ticker.C:
|
|
||||||
logProgress(size, offset)
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
start := time.Now()
|
|
||||||
// wipe the device
|
|
||||||
if err := m.device.Wipe("/dev/mapper/"+tmpDevice, cryptsetup.CRYPT_WIPE_ZERO, 0, 0, blockWipeSize, 0, progressCallback); err != nil {
|
|
||||||
return fmt.Errorf("wiping disk: %w", err)
|
|
||||||
}
|
|
||||||
m.log.With(zap.Duration("duration", time.Since(start))).Infof("Wiping disk successful")
|
|
||||||
|
|
||||||
// Deactivate the temporary device
|
|
||||||
if err := m.device.Deactivate(tmpDevice); err != nil {
|
|
||||||
return fmt.Errorf("deactivating temporary device: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
158
disk-mapper/internal/mapper/mapper_cgo.go
Normal file
158
disk-mapper/internal/mapper/mapper_cgo.go
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
//go:build linux && cgo
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) Edgeless Systems GmbH
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package mapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
ccryptsetup "github.com/edgelesssys/constellation/v2/internal/cryptsetup"
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||||
|
cryptsetup "github.com/martinjungblut/go-cryptsetup"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
var packageLock = sync.Mutex{}
|
||||||
|
|
||||||
|
// Mapper handles actions for formatting and mapping crypt devices.
|
||||||
|
type Mapper struct {
|
||||||
|
device cryptDevice
|
||||||
|
log *logger.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new crypt device for the device at path.
|
||||||
|
func New(path string, log *logger.Logger) (*Mapper, error) {
|
||||||
|
packageLock.Lock()
|
||||||
|
device, err := cryptsetup.Init(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("initializing crypt device for disk %q: %w", path, err)
|
||||||
|
}
|
||||||
|
return &Mapper{device: device, log: log}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes and frees memory allocated for the crypt device.
|
||||||
|
func (m *Mapper) Close() error {
|
||||||
|
defer packageLock.Unlock()
|
||||||
|
if m.device.Free() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("unable to close crypt device")
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLUKSDevice returns true if the device is formatted as a LUKS device.
|
||||||
|
func (m *Mapper) IsLUKSDevice() bool {
|
||||||
|
return m.device.Load(cryptsetup.LUKS2{}) == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiskUUID gets the device's UUID.
|
||||||
|
func (m *Mapper) DiskUUID() string {
|
||||||
|
return strings.ToLower(m.device.GetUUID())
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatDisk formats the disk and adds passphrase in keyslot 0.
|
||||||
|
func (m *Mapper) FormatDisk(passphrase string) error {
|
||||||
|
luksParams := cryptsetup.LUKS2{
|
||||||
|
SectorSize: 4096,
|
||||||
|
Integrity: "hmac(sha256)",
|
||||||
|
PBKDFType: &cryptsetup.PbkdfType{
|
||||||
|
// Use low memory recommendation from https://datatracker.ietf.org/doc/html/rfc9106#section-7
|
||||||
|
Type: "argon2id",
|
||||||
|
TimeMs: 2000,
|
||||||
|
Iterations: 3,
|
||||||
|
ParallelThreads: 4,
|
||||||
|
MaxMemoryKb: 65536, // ~64MiB
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
genericParams := cryptsetup.GenericParams{
|
||||||
|
Cipher: "aes",
|
||||||
|
CipherMode: "xts-plain64",
|
||||||
|
VolumeKeySize: 96, // 32*2 bytes for aes-xts-plain64 encryption, 32 bytes for hmac(sha256) integrity
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.device.Format(luksParams, genericParams); err != nil {
|
||||||
|
return fmt.Errorf("formatting disk: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.device.KeyslotAddByVolumeKey(0, "", passphrase); err != nil {
|
||||||
|
return fmt.Errorf("adding keyslot: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// wipe using 64MiB block size
|
||||||
|
if err := m.Wipe(67108864); err != nil {
|
||||||
|
return fmt.Errorf("wiping disk: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapDisk maps a crypt device to /dev/mapper/target using the provided passphrase.
|
||||||
|
func (m *Mapper) MapDisk(target, passphrase string) error {
|
||||||
|
if err := m.device.ActivateByPassphrase(target, 0, passphrase, ccryptsetup.ReadWriteQueueBypass); err != nil {
|
||||||
|
return fmt.Errorf("mapping disk as %q: %w", target, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmapDisk removes the mapping of target.
|
||||||
|
func (m *Mapper) UnmapDisk(target string) error {
|
||||||
|
return m.device.Deactivate(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wipe overwrites the device with zeros to initialize integrity checksums.
|
||||||
|
func (m *Mapper) Wipe(blockWipeSize int) error {
|
||||||
|
// Activate as temporary device using the internal volume key
|
||||||
|
tmpDevice := "tmp-cryptsetup-integrity"
|
||||||
|
if err := m.device.ActivateByVolumeKey(tmpDevice, "", 0, (cryptsetup.CRYPT_ACTIVATE_PRIVATE | cryptsetup.CRYPT_ACTIVATE_NO_JOURNAL)); err != nil {
|
||||||
|
return fmt.Errorf("activating as temporary device: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// set progress logging callback once every 30 seconds
|
||||||
|
ticker := time.NewTicker(30 * time.Second)
|
||||||
|
firstReq := make(chan struct{}, 1)
|
||||||
|
firstReq <- struct{}{}
|
||||||
|
defer ticker.Stop()
|
||||||
|
logProgress := func(size, offset uint64) {
|
||||||
|
prog := (float64(offset) / float64(size)) * 100
|
||||||
|
m.log.With(zap.String("progress", fmt.Sprintf("%.2f%%", prog))).Infof("Wiping disk")
|
||||||
|
}
|
||||||
|
|
||||||
|
progressCallback := func(size, offset uint64) int {
|
||||||
|
select {
|
||||||
|
case <-firstReq:
|
||||||
|
logProgress(size, offset)
|
||||||
|
case <-ticker.C:
|
||||||
|
logProgress(size, offset)
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
// wipe the device
|
||||||
|
if err := m.device.Wipe("/dev/mapper/"+tmpDevice, cryptsetup.CRYPT_WIPE_ZERO, 0, 0, blockWipeSize, 0, progressCallback); err != nil {
|
||||||
|
return fmt.Errorf("wiping disk: %w", err)
|
||||||
|
}
|
||||||
|
m.log.With(zap.Duration("duration", time.Since(start))).Infof("Wiping disk successful")
|
||||||
|
|
||||||
|
// Deactivate the temporary device
|
||||||
|
if err := m.device.Deactivate(tmpDevice); err != nil {
|
||||||
|
return fmt.Errorf("deactivating temporary device: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
66
disk-mapper/internal/mapper/mapper_cross.go
Normal file
66
disk-mapper/internal/mapper/mapper_cross.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
//go:build !linux || !cgo
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) Edgeless Systems GmbH
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package mapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mapper handles actions for formatting and mapping crypt devices.
|
||||||
|
type Mapper struct{}
|
||||||
|
|
||||||
|
// New creates a new crypt device for the device at path.
|
||||||
|
// This function errors if CGO is disabled.
|
||||||
|
func New(_ string, _ *logger.Logger) (*Mapper, error) {
|
||||||
|
return nil, errors.New("using mapper requires building with CGO")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes and frees memory allocated for the crypt device.
|
||||||
|
// This function errors if CGO is disabled.
|
||||||
|
func (m *Mapper) Close() error {
|
||||||
|
return errors.New("using mapper requires building with CGO")
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLUKSDevice returns true if the device is formatted as a LUKS device.
|
||||||
|
// This function does nothing if CGO is disabled.
|
||||||
|
func (m *Mapper) IsLUKSDevice() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiskUUID gets the device's UUID.
|
||||||
|
// This function does nothing if CGO is disabled.
|
||||||
|
func (m *Mapper) DiskUUID() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatDisk formats the disk and adds passphrase in keyslot 0.
|
||||||
|
// This function errors if CGO is disabled.
|
||||||
|
func (m *Mapper) FormatDisk(_ string) error {
|
||||||
|
return errors.New("using mapper requires building with CGO")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapDisk maps a crypt device to /dev/mapper/target using the provided passphrase.
|
||||||
|
// This function errors if CGO is disabled.
|
||||||
|
func (m *Mapper) MapDisk(_, _ string) error {
|
||||||
|
return errors.New("using mapper requires building with CGO")
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmapDisk removes the mapping of target.
|
||||||
|
// This function errors if CGO is disabled.
|
||||||
|
func (m *Mapper) UnmapDisk(_ string) error {
|
||||||
|
return errors.New("using mapper requires building with CGO")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wipe overwrites the device with zeros to initialize integrity checksums.
|
||||||
|
// This function errors if CGO is disabled.
|
||||||
|
func (m *Mapper) Wipe(_ int) error {
|
||||||
|
return errors.New("using mapper requires building with CGO")
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
//go:build integration
|
//go:build integration && cgo
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright (c) Edgeless Systems GmbH
|
Copyright (c) Edgeless Systems GmbH
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//go:build integration
|
//go:build integration && linux && cgo
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright (c) Edgeless Systems GmbH
|
Copyright (c) Edgeless Systems GmbH
|
||||||
|
Loading…
x
Reference in New Issue
Block a user