From 34e66cb01c3933d6f8ab8c6689b9afb78feafe12 Mon Sep 17 00:00:00 2001 From: AlexSSD7 Date: Sat, 26 Aug 2023 16:43:04 +0100 Subject: [PATCH] Restricted VM networking --- cmd/ls.go | 2 +- cmd/run.go | 2 +- cmd/shell.go | 21 ++++++++++++++++++++- cmd/utils.go | 23 ++--------------------- vm/vm.go | 10 +++++++--- 5 files changed, 31 insertions(+), 27 deletions(-) diff --git a/cmd/ls.go b/cmd/ls.go index 975ba7f..55c9447 100644 --- a/cmd/ls.go +++ b/cmd/ls.go @@ -27,7 +27,7 @@ var lsCmd = &cobra.Command{ fmt.Print(string(lsblkOut)) return 0 - }, nil)) + }, nil, false)) return nil }, diff --git a/cmd/run.go b/cmd/run.go index be02541..0f235d3 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -63,7 +63,7 @@ var runCmd = &cobra.Command{ HostIP: net.ParseIP("127.0.0.1"), // TODO: Make this changeable. HostPort: networkSharePort, VMPort: 445, - }})) + }}, false)) return nil }, diff --git a/cmd/shell.go b/cmd/shell.go index 32f3e60..3d0a8c4 100644 --- a/cmd/shell.go +++ b/cmd/shell.go @@ -4,6 +4,7 @@ import ( "context" "log/slog" "os" + "strings" "github.com/AlexSSD7/linsk/vm" "github.com/spf13/cobra" @@ -22,6 +23,22 @@ var shellCmd = &cobra.Command{ passthroughArg = args[0] } + var forwardPortsConfig []vm.PortForwardingConfig + + for i, fp := range strings.Split(forwardPortsFlagStr, ",") { + if fp == "" { + continue + } + + fpc, err := vm.ParsePortForwardString(fp) + if err != nil { + slog.Error("Failed to parse port forward string", "index", i, "value", fp, "error", err) + os.Exit(1) + } + + forwardPortsConfig = append(forwardPortsConfig, fpc) + } + os.Exit(runVM(passthroughArg, func(ctx context.Context, i *vm.Instance, fm *vm.FileManager) int { sc, err := i.DialSSH() if err != nil { @@ -103,14 +120,16 @@ var shellCmd = &cobra.Command{ } return 0 - }, nil)) + }, forwardPortsConfig, unrestrictedNetworkingFlag)) return nil }, } var forwardPortsFlagStr string +var unrestrictedNetworkingFlag bool func init() { + shellCmd.Flags().BoolVar(&unrestrictedNetworkingFlag, "unsafe-unrestricted-networking", false, "(UNSAFE) Enable unrestricted networking. This will allow the VM to connect to the internet.") shellCmd.Flags().StringVar(&forwardPortsFlagStr, "forward-ports", "", "Extra TCP port forwarding rules. Syntax: ':' OR '::'. Multiple rules split by comma are accepted.") } diff --git a/cmd/utils.go b/cmd/utils.go index d3a46f3..62ad9d3 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -7,7 +7,6 @@ import ( "os" "os/signal" "os/user" - "strings" "sync" "syscall" @@ -38,7 +37,7 @@ func doRootCheck() { } } -func runVM(passthroughArg string, fn func(context.Context, *vm.Instance, *vm.FileManager) int, forwardPorts []vm.PortForwardingConfig) int { +func runVM(passthroughArg string, fn func(context.Context, *vm.Instance, *vm.FileManager) int, forwardPorts []vm.PortForwardingConfig, unrestrictedNetworking bool) int { doRootCheck() var passthroughConfig []vm.USBDevicePassthroughConfig @@ -47,26 +46,8 @@ func runVM(passthroughArg string, fn func(context.Context, *vm.Instance, *vm.Fil passthroughConfig = []vm.USBDevicePassthroughConfig{getDevicePassthroughConfig(passthroughArg)} } - var forwardPortsConfig []vm.PortForwardingConfig - - for i, fp := range strings.Split(forwardPortsFlagStr, ",") { - if fp == "" { - continue - } - - fpc, err := vm.ParsePortForwardString(fp) - if err != nil { - slog.Error("Failed to parse port forward string", "index", i, "value", fp, "error", err) - os.Exit(1) - } - - forwardPortsConfig = append(forwardPortsConfig, fpc) - } - - forwardPortsConfig = append(forwardPortsConfig, forwardPorts...) - // TODO: Alpine image should be downloaded from somewhere. - vi, err := vm.NewInstance(slog.Default().With("caller", "vm"), "alpine-img/alpine.qcow2", passthroughConfig, vmDebugFlag, forwardPortsConfig) + vi, err := vm.NewInstance(slog.Default().With("caller", "vm"), "alpine-img/alpine.qcow2", passthroughConfig, vmDebugFlag, forwardPorts, unrestrictedNetworking) if err != nil { slog.Error("Failed to create vm instance", "error", err) os.Exit(1) diff --git a/vm/vm.go b/vm/vm.go index a99dc52..6a589b1 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -51,7 +51,7 @@ type Instance struct { canceled uint32 } -func NewInstance(logger *slog.Logger, alpineImagePath string, usbDevices []USBDevicePassthroughConfig, debug bool, extraPortForwardings []PortForwardingConfig) (*Instance, error) { +func NewInstance(logger *slog.Logger, alpineImagePath string, usbDevices []USBDevicePassthroughConfig, debug bool, extraPortForwardings []PortForwardingConfig, unrestrictedNetworking bool) (*Instance, error) { alpineImagePath = filepath.Clean(alpineImagePath) _, err := os.Stat(alpineImagePath) if err != nil { @@ -63,8 +63,6 @@ func NewInstance(logger *slog.Logger, alpineImagePath string, usbDevices []USBDe return nil, errors.Wrap(err, "get free port for ssh server") } - // TODO: Disable internet access - // TODO: Configurable memory allocation baseCmd := "qemu-system-x86_64" @@ -72,6 +70,12 @@ func NewInstance(logger *slog.Logger, alpineImagePath string, usbDevices []USBDe netdevOpts := "user,id=net0,hostfwd=tcp:127.0.0.1:" + fmt.Sprint(sshPort) + "-:22" + if !unrestrictedNetworking { + netdevOpts += ",restrict=on" + } else { + logger.Warn("Running with unsafe unrestricted networking") + } + for _, pf := range extraPortForwardings { hostIPStr := "" if pf.HostIP != nil {