Add create command for LUKS volume provisioning and Makefile
New `arca create <device> <alias>` command that formats a device with LUKS encryption, creates a filesystem, optionally enrolls a FIDO2 keyslot, and adds the device to the arca config. Adds Makefile with arca, vet, lint, test, and clean targets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -101,6 +101,112 @@ func Unmount(mountpoint string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LuksFormat formats a device with LUKS encryption using the given passphrase.
|
||||
func LuksFormat(devicePath, passphrase string) error {
|
||||
args := []string{"cryptsetup", "luksFormat", "-q", "--key-file=-", devicePath}
|
||||
args = withPrivilege(args)
|
||||
|
||||
verbose.Printf("exec: %s", strings.Join(args, " "))
|
||||
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Stdin = strings.NewReader(passphrase)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("cryptsetup luksFormat: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OpenWithPassphrase opens a LUKS device using a passphrase piped via stdin.
|
||||
func OpenWithPassphrase(devicePath, mapperName, passphrase string) error {
|
||||
args := []string{"cryptsetup", "open", "--key-file=-", devicePath, mapperName}
|
||||
args = withPrivilege(args)
|
||||
|
||||
verbose.Printf("exec: %s", strings.Join(args, " "))
|
||||
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Stdin = strings.NewReader(passphrase)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("cryptsetup open: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LuksUUID returns the UUID of a LUKS device.
|
||||
func LuksUUID(devicePath string) (string, error) {
|
||||
args := []string{"cryptsetup", "luksUUID", devicePath}
|
||||
args = withPrivilege(args)
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Stdout = &buf
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return "", fmt.Errorf("cryptsetup luksUUID: %w", err)
|
||||
}
|
||||
return strings.TrimSpace(buf.String()), nil
|
||||
}
|
||||
|
||||
// Mkfs creates a filesystem on the given device.
|
||||
func Mkfs(devicePath, fsType string) error {
|
||||
mkfsCmd := fmt.Sprintf("mkfs.%s", fsType)
|
||||
args := withPrivilege([]string{mkfsCmd, devicePath})
|
||||
|
||||
verbose.Printf("exec: %s", strings.Join(args, " "))
|
||||
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("%s: %w", mkfsCmd, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListFIDO2Devices returns the output of systemd-cryptenroll --fido2-device=list,
|
||||
// or an error if no devices are found or the command is unavailable.
|
||||
func ListFIDO2Devices() (string, error) {
|
||||
if _, err := exec.LookPath("systemd-cryptenroll"); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
cmd := exec.Command("systemd-cryptenroll", "--fido2-device=list")
|
||||
cmd.Stdout = &buf
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(buf.String()), nil
|
||||
}
|
||||
|
||||
// EnrollFIDO2 enrolls a FIDO2 device as a keyslot on the given LUKS device.
|
||||
// This is interactive — it prompts for the existing passphrase and FIDO2 touch.
|
||||
func EnrollFIDO2(devicePath string) error {
|
||||
args := []string{"systemd-cryptenroll", "--fido2-device=auto", devicePath}
|
||||
args = withPrivilege(args)
|
||||
|
||||
verbose.Printf("exec: %s", strings.Join(args, " "))
|
||||
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("systemd-cryptenroll: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MapperName returns "arca-<dev>" from a device path, e.g. "/dev/sda1" -> "arca-sda1".
|
||||
func MapperName(devicePath string) string {
|
||||
return "arca-" + filepath.Base(devicePath)
|
||||
|
||||
Reference in New Issue
Block a user