Device passthrough and root checks
This commit is contained in:
parent
433deeab5e
commit
64d3891c48
5 changed files with 151 additions and 101 deletions
|
|
@ -1,47 +0,0 @@
|
|||
//go:build !windows
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func prepareVMCmd(cmd *exec.Cmd) {
|
||||
// This is to prevent Ctrl+C propagating to the child process.
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
}
|
||||
|
||||
func terminateProcess(pid int) error {
|
||||
return syscall.Kill(-pid, syscall.SIGTERM)
|
||||
}
|
||||
|
||||
// This is never used except for a band-aid that would check
|
||||
// that there are no double-mounts.
|
||||
func checkDeviceSeemsMounted(devPathPrefix string) (bool, error) {
|
||||
// Quite a bit hacky implementation, but it's to be used as a failsafe band-aid anyway.
|
||||
absDevPathPrefix, err := filepath.Abs(devPathPrefix)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "get abs path")
|
||||
}
|
||||
|
||||
mounts, err := exec.Command("mount").Output()
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "run mount command")
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(string(mounts), "\n") {
|
||||
// I know, I know, this is a rare band-aid.
|
||||
if strings.HasPrefix(line, devPathPrefix) || strings.HasPrefix(line, absDevPathPrefix) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
// go:build windows
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func prepareVMCmd(cmd *exec.Cmd) {
|
||||
// This is to prevent Ctrl+C propagating to the child process.
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
|
||||
}
|
||||
}
|
||||
|
||||
func terminateProcess(pid int) error {
|
||||
return exec.Command("TASKKILL", "/T", "/F", "/PID", fmt.Sprint(pid)).Run()
|
||||
}
|
||||
|
||||
var physicalDriveRegexp = regexp.MustCompile(`PhysicalDrive(\d+)`)
|
||||
|
||||
// This is never used except for a band-aid that would check
|
||||
// that there are no double-mounts.
|
||||
func checkDeviceSeemsMounted(path string) (bool, error) {
|
||||
// Quite a bit hacky implementation, but it's to be used as a failsafe band-aid anyway.
|
||||
matches := physicalDriveRegexp.FindAllStringSubmatch(path, 1)
|
||||
if len(matches) == 0 {
|
||||
return false, fmt.Errorf("bad device path '%v'", path)
|
||||
}
|
||||
|
||||
match := matches[0]
|
||||
|
||||
if want, have := 2, len(match); want != have {
|
||||
return false, fmt.Errorf("bad match items length: want %v, have %v (%v)", want, have, match)
|
||||
}
|
||||
|
||||
out, err := exec.Command("wmic", "path", "Win32_LogicalDiskToPartition", "get", "Antecedent").Output()
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "exec wmic cmd")
|
||||
}
|
||||
|
||||
return strings.Contains(string(out), fmt.Sprintf("Disk #%v", match[1])), nil
|
||||
}
|
||||
15
vm/vm.go
15
vm/vm.go
|
|
@ -19,6 +19,7 @@ import (
|
|||
"log/slog"
|
||||
|
||||
"github.com/AlexSSD7/linsk/nettap"
|
||||
"github.com/AlexSSD7/linsk/osspecifics"
|
||||
"github.com/AlexSSD7/linsk/sshutil"
|
||||
"github.com/AlexSSD7/linsk/utils"
|
||||
"github.com/alessio/shellescape"
|
||||
|
|
@ -115,9 +116,7 @@ func NewVM(logger *slog.Logger, cfg VMConfig) (*VM, error) {
|
|||
var accel string
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
// TODO: whpx accel is broken in Windows. Long term solution looks to be use Hyper-V.
|
||||
|
||||
// For Windows, we need to install QEMU using an installer and add it to PATH.
|
||||
// TODO: To document: For Windows, we need to install QEMU using an installer and add it to PATH.
|
||||
// Then, we should enable Windows Hypervisor Platform in "Turn Windows features on or off".
|
||||
// IMPORTANT: We should also install libusbK drivers for USB devices we want to pass through.
|
||||
// This can be easily done with a program called Zadiag by Akeo.
|
||||
|
|
@ -216,14 +215,14 @@ func NewVM(logger *slog.Logger, cfg VMConfig) (*VM, error) {
|
|||
}
|
||||
|
||||
if len(cfg.PassthroughConfig.Block) != 0 {
|
||||
logger.Warn("Detected raw block device passthrough. Please note that it's YOUR responsibility to ensure that no device is mounted in your OS and the VM at the same time. Otherwise, you run serious risks. No further warnings will be issued.")
|
||||
logger.Warn("Using raw block device passthrough. Please note that it's YOUR responsibility to ensure that no device is mounted in your OS and the VM at the same time. Otherwise, you run serious risks. No further warnings will be issued.")
|
||||
}
|
||||
|
||||
for _, dev := range cfg.PassthroughConfig.Block {
|
||||
// It's always a user's responsibility to ensure that no drives are mounted
|
||||
// in both host and guest system. This should serve as the last resort.
|
||||
{
|
||||
seemsMounted, err := checkDeviceSeemsMounted(dev.Path)
|
||||
seemsMounted, err := osspecifics.CheckDeviceSeemsMounted(dev.Path)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "check whether device seems to be mounted (path '%v')", dev.Path)
|
||||
}
|
||||
|
|
@ -275,7 +274,7 @@ func NewVM(logger *slog.Logger, cfg VMConfig) (*VM, error) {
|
|||
cmd.Stderr = stderrBuf
|
||||
|
||||
// This function is OS-specific.
|
||||
prepareVMCmd(cmd)
|
||||
osspecifics.SetNewProcessGroupCmd(cmd)
|
||||
|
||||
userReader := bufio.NewReader(userRead)
|
||||
|
||||
|
|
@ -456,7 +455,7 @@ func (vm *VM) Cancel() error {
|
|||
if vm.cmd.Process == nil {
|
||||
interruptErr = fmt.Errorf("process is not started")
|
||||
} else {
|
||||
interruptErr = terminateProcess(vm.cmd.Process.Pid)
|
||||
interruptErr = osspecifics.TerminateProcess(vm.cmd.Process.Pid)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -582,7 +581,7 @@ func (vm *VM) runPeriodicHostMountChecker() {
|
|||
return
|
||||
case <-time.After(time.Second):
|
||||
for _, dev := range vm.originalCfg.PassthroughConfig.Block {
|
||||
seemsMounted, err := checkDeviceSeemsMounted(dev.Path)
|
||||
seemsMounted, err := osspecifics.CheckDeviceSeemsMounted(dev.Path)
|
||||
if err != nil {
|
||||
vm.logger.Warn("Failed to check if a passed device seems to be mounted", "dev-path", dev.Path)
|
||||
continue
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue