From 0fea76d2732935ae79cba922491c4727a5c9c22e Mon Sep 17 00:00:00 2001 From: AlexSSD7 Date: Wed, 27 Sep 2023 16:57:23 +0100 Subject: [PATCH] Allow opening LUKS containers inside `linsk ls` --- cmd/flags.go | 74 +++++++++++++++++++++++++++++++++++++++++++++++ cmd/ls.go | 14 +++++++++ cmd/run.go | 49 ++++++++----------------------- vm/filemanager.go | 50 ++++++++++++++++++++++---------- 4 files changed, 135 insertions(+), 52 deletions(-) create mode 100644 cmd/flags.go diff --git a/cmd/flags.go b/cmd/flags.go new file mode 100644 index 0000000..3767f91 --- /dev/null +++ b/cmd/flags.go @@ -0,0 +1,74 @@ +// Linsk - A utility to access Linux-native file systems on non-Linux operating systems. +// Copyright (c) 2023 The Linsk Authors. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package cmd + +import ( + "log/slog" + "os" + + "github.com/spf13/pflag" +) + +const defaultVMMountDevName = "vdb" + +func getLUKSContainerDevice() string { + var luksContainerDevice string + + if vmRuntimeLUKSContainerFlag != "" { + if vmRuntimeLUKSContainerEntireDriveFlag { + slog.Error("--luks-container and --luks-container-entire-drive (-c) cannot be both specified at once") + os.Exit(1) + } + + luksContainerDevice = vmRuntimeLUKSContainerFlag + } else if vmRuntimeLUKSContainerEntireDriveFlag { + luksContainerDevice = defaultVMMountDevName + } + + return luksContainerDevice +} + +var ( + vmRuntimeLUKSContainerFlag string + vmRuntimeLUKSContainerEntireDriveFlag bool + + // These are for internal use by the initVMRuntimeFlags and configureVMRuntimeFlags functions. + vmRuntimeInternalAllowLUKSLowMemoryFlag bool + + // These are to be initialized (set) by the initVMRuntimeFlags function. + vmRuntimeLUKSContainerDevice string +) + +func initVMRuntimeFlags(flags *pflag.FlagSet) { + flags.StringVar(&vmRuntimeLUKSContainerFlag, "luks-container", "", `Specifies a device path (without "dev/" prefix) to preopen as a LUKS container (password will be prompted). Useful for accessing LVM partitions behind LUKS.`) + flags.BoolVarP(&vmRuntimeLUKSContainerEntireDriveFlag, "luks-container-entire-drive", "c", false, `Similar to --luks-container, but this assumes that the entire passed-through volume is a LUKS container (password will be prompted).`) + flags.BoolVar(&vmRuntimeInternalAllowLUKSLowMemoryFlag, "allow-luks-low-memory", false, "Allow VM memory allocation lower than 2048 MiB when LUKS is enabled.") +} + +func configureVMRuntimeFlags() { + vmRuntimeLUKSContainerDevice = getLUKSContainerDevice() + + if (luksFlag || vmRuntimeLUKSContainerDevice != "") && !vmRuntimeInternalAllowLUKSLowMemoryFlag { + if vmMemAllocFlag < defaultMemAllocLUKS { + if vmMemAllocFlag != defaultMemAlloc { + slog.Warn("Enforcing minimum LUKS memory allocation. Please add --allow-luks-low-memory to disable this.", "min", vmMemAllocFlag, "specified", vmMemAllocFlag) + } + + vmMemAllocFlag = defaultMemAllocLUKS + } + } +} diff --git a/cmd/ls.go b/cmd/ls.go index a50ab8a..4ea1f66 100644 --- a/cmd/ls.go +++ b/cmd/ls.go @@ -32,7 +32,17 @@ var lsCmd = &cobra.Command{ Short: "Start a VM and list all user drives within the VM. Uses lsblk command under the hood.", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { + configureVMRuntimeFlags() + os.Exit(runVM(args[0], func(ctx context.Context, i *vm.VM, fm *vm.FileManager, trc *share.NetTapRuntimeContext) int { + if vmRuntimeLUKSContainerDevice != "" { + err := fm.PreopenLUKSContainer(vmRuntimeLUKSContainerDevice) + if err != nil { + slog.Error("Failed to preopen LUKS container", "error", err.Error()) + return 1 + } + } + lsblkOut, err := fm.Lsblk() if err != nil { slog.Error("Failed to list block devices in the VM", "error", err.Error()) @@ -49,3 +59,7 @@ var lsCmd = &cobra.Command{ }, nil, false, false)) }, } + +func init() { + initVMRuntimeFlags(lsCmd.Flags()) +} diff --git a/cmd/run.go b/cmd/run.go index 419fe46..7fac91e 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -35,24 +35,13 @@ var runCmd = &cobra.Command{ Short: "Start a VM and expose an FTP file share.", Args: cobra.RangeArgs(1, 3), Run: func(cmd *cobra.Command, args []string) { - var luksContainerDevice string + configureVMRuntimeFlags() - vmMountDevName := "vdb" - - if luksContainerFlag != "" { - if luksContainerEntireDriveFlag { - slog.Error("--luks-container and --luks-container-entire-drive (-c) cannot be both specified at once") - os.Exit(1) - } - - luksContainerDevice = luksContainerFlag - } else if luksContainerEntireDriveFlag { - luksContainerDevice = vmMountDevName - } + vmMountDevName := defaultVMMountDevName if len(args) > 1 { vmMountDevName = args[1] - } else if luksContainerDevice != "" { + } else if vmRuntimeLUKSContainerDevice != "" { slog.Error("Cannot use the default (entire) device with a LUKS container. Please specify the in-VM device name to mount as a second positional argument.") } @@ -84,16 +73,6 @@ var runCmd = &cobra.Command{ os.Exit(1) } - if (luksFlag || luksContainerDevice != "") && !allowLUKSLowMemoryFlag { - if vmMemAllocFlag < defaultMemAllocLUKS { - if vmMemAllocFlag != defaultMemAlloc { - slog.Warn("Enforcing minimum LUKS memory allocation. Please add --allow-luks-low-memory to disable this.", "min", vmMemAllocFlag, "specified", vmMemAllocFlag) - } - - vmMemAllocFlag = defaultMemAllocLUKS - } - } - os.Exit(runVM(args[0], func(ctx context.Context, i *vm.VM, fm *vm.FileManager, tapCtx *share.NetTapRuntimeContext) int { fsToLog := "" if fsTypeOverride != "" { @@ -103,7 +82,7 @@ var runCmd = &cobra.Command{ slog.Info("Mounting the device", "dev", vmMountDevName, "fs", fsToLog, "luks", luksFlag) err := fm.Mount(vmMountDevName, vm.MountOptions{ - LUKSContainerPreopen: luksContainerDevice, + LUKSContainerPreopen: vmRuntimeLUKSContainerDevice, FSTypeOverride: fsTypeOverride, LUKS: luksFlag, @@ -157,24 +136,20 @@ var runCmd = &cobra.Command{ } var ( - luksFlag bool - luksContainerFlag string - luksContainerEntireDriveFlag bool - allowLUKSLowMemoryFlag bool - shareListenIPFlag string - ftpExtIPFlag string - shareBackendFlag string - smbUseExternAddrFlag bool - debugShellFlag bool + luksFlag bool + shareListenIPFlag string + ftpExtIPFlag string + shareBackendFlag string + smbUseExternAddrFlag bool + debugShellFlag bool ) func init() { runCmd.Flags().BoolVarP(&luksFlag, "luks", "l", false, "Use cryptsetup to open a LUKS volume (password will be prompted).") - runCmd.Flags().StringVar(&luksContainerFlag, "luks-container", "", `Specifies a device path (without "dev/" prefix) to preopen as a LUKS container (password will be prompted). Useful for accessing LVM partitions behind LUKS.`) - runCmd.Flags().BoolVarP(&luksContainerEntireDriveFlag, "luks-container-entire-drive", "c", false, `Similar to --luks-container, but this assumes that the entire passed-through volume is a LUKS container (password will be prompted).`) - runCmd.Flags().BoolVar(&allowLUKSLowMemoryFlag, "allow-luks-low-memory", false, "Allow VM memory allocation lower than 2048 MiB when LUKS is enabled.") runCmd.Flags().BoolVar(&debugShellFlag, "debug-shell", false, "Start a VM shell when the network file share is active.") + initVMRuntimeFlags(runCmd.Flags()) + var defaultShareType string switch { case osspecifics.IsMacOS(): diff --git a/vm/filemanager.go b/vm/filemanager.go index 61882d0..5d06780 100644 --- a/vm/filemanager.go +++ b/vm/filemanager.go @@ -169,6 +169,39 @@ func (fm *FileManager) luksOpen(sc *ssh.Client, fullDevPath string, luksDMName s }) } +func (fm *FileManager) PreopenLUKSContainer(containerDevPath string) error { + sc, err := fm.vm.DialSSH() + if err != nil { + return errors.Wrap(err, "dial vm ssh") + } + + defer func() { _ = sc.Close() }() + + return fm.preopenLUKSContainerWithSSH(sc, containerDevPath) +} + +func (fm *FileManager) preopenLUKSContainerWithSSH(sc *ssh.Client, containerDevPath string) error { + if !utils.ValidateDevName(containerDevPath) { + return fmt.Errorf("bad luks container device name") + } + + fullContainerDevPath := "/dev/" + containerDevPath + + fm.logger.Info("Preopening a LUKS container", "container", fullContainerDevPath) + + err := fm.luksOpen(sc, fullContainerDevPath, "cryptcontainer") + if err != nil { + return errors.Wrap(err, "luks (pre)open container") + } + + err = fm.InitLVM() + if err != nil { + return errors.Wrap(err, "reinit lvm") + } + + return nil +} + func (fm *FileManager) Mount(devName string, mo MountOptions) error { if devName == "" { return fmt.Errorf("device name is empty") @@ -204,22 +237,9 @@ func (fm *FileManager) Mount(devName string, mo MountOptions) error { defer func() { _ = sc.Close() }() if mo.LUKSContainerPreopen != "" { - if !utils.ValidateDevName(mo.LUKSContainerPreopen) { - return fmt.Errorf("bad luks container device name") - } - - fullContainerDevPath := "/dev/" + mo.LUKSContainerPreopen - - fm.logger.Info("Preopening a LUKS container", "container", fullContainerDevPath) - - err := fm.luksOpen(sc, fullContainerDevPath, "cryptcontainer") + err := fm.preopenLUKSContainerWithSSH(sc, mo.LUKSContainerPreopen) if err != nil { - return errors.Wrap(err, "luks (pre)open container") - } - - err = fm.InitLVM() - if err != nil { - return errors.Wrap(err, "reinit lvm") + return errors.Wrap(err, "preopen luks container") } }