From 7ef3981abe2412727840a2886489a08c03a05299 Mon Sep 17 00:00:00 2001 From: Brent Baude Date: Fri, 7 May 2021 14:14:59 -0500 Subject: Enable port forwarding on host Using the gvproxy application on the host, we can now port forward from the machine vm on the host. It requires that 'gvproxy' be installed in an executable location. gvproxy can be found in the containers/gvisor-tap-vsock github repo. [NO TESTS NEEDED] Signed-off-by: Brent Baude --- pkg/machine/qemu/machine.go | 118 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 107 insertions(+), 11 deletions(-) (limited to 'pkg/machine/qemu/machine.go') diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 0bd711c90..31c355d4a 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -13,6 +13,8 @@ import ( "strings" "time" + "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/podman/v3/pkg/machine" "github.com/containers/podman/v3/utils" "github.com/containers/storage/pkg/homedir" @@ -82,9 +84,10 @@ func NewMachine(opts machine.InitOptions) (machine.VM, error) { cmd = append(cmd, []string{"-qmp", monitor.Network + ":/" + monitor.Address + ",server=on,wait=off"}...) // Add network - cmd = append(cmd, "-nic", "user,model=virtio,hostfwd=tcp::"+strconv.Itoa(vm.Port)+"-:22") - - socketPath, err := getSocketDir() + // Right now the mac address is hardcoded so that the host networking gives it a specific IP address. This is + // why we can only run one vm at a time right now + cmd = append(cmd, []string{"-netdev", "socket,id=vlan,fd=3", "-device", "virtio-net-pci,netdev=vlan,mac=5a:94:ef:e4:0c:ee"}...) + socketPath, err := getRuntimeDir() if err != nil { return nil, err } @@ -235,12 +238,35 @@ func (v *MachineVM) Init(opts machine.InitOptions) error { // Start executes the qemu command line and forks it func (v *MachineVM) Start(name string, _ machine.StartOptions) error { var ( - conn net.Conn - err error - wait time.Duration = time.Millisecond * 500 + conn net.Conn + err error + qemuSocketConn net.Conn + wait time.Duration = time.Millisecond * 500 ) + if err := v.startHostNetworking(); err != nil { + return errors.Errorf("unable to start host networking: %q", err) + } + qemuSocketPath, _, err := v.getSocketandPid() + + for i := 0; i < 6; i++ { + qemuSocketConn, err = net.Dial("unix", qemuSocketPath) + if err == nil { + break + } + time.Sleep(wait) + wait++ + } + if err != nil { + return err + } + + fd, err := qemuSocketConn.(*net.UnixConn).File() + if err != nil { + return err + } + attr := new(os.ProcAttr) - files := []*os.File{os.Stdin, os.Stdout, os.Stderr} + files := []*os.File{os.Stdin, os.Stdout, os.Stderr, fd} attr.Files = files logrus.Debug(v.CmdLine) cmd := v.CmdLine @@ -256,7 +282,7 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { return err } fmt.Println("Waiting for VM ...") - socketPath, err := getSocketDir() + socketPath, err := getRuntimeDir() if err != nil { return err } @@ -309,16 +335,42 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error { logrus.Error(err) } }() - _, err = qmpMonitor.Run(input) - return err + if _, err = qmpMonitor.Run(input); err != nil { + return err + } + _, pidFile, err := v.getSocketandPid() + if err != nil { + return err + } + if _, err := os.Stat(pidFile); os.IsNotExist(err) { + logrus.Infof("pid file %s does not exist", pidFile) + return nil + } + pidString, err := ioutil.ReadFile(pidFile) + if err != nil { + return err + } + pidNum, err := strconv.Atoi(string(pidString)) + if err != nil { + return err + } + + p, err := os.FindProcess(pidNum) + if p == nil && err != nil { + return err + } + return p.Kill() } // NewQMPMonitor creates the monitor subsection of our vm func NewQMPMonitor(network, name string, timeout time.Duration) (Monitor, error) { - rtDir, err := getSocketDir() + rtDir, err := getRuntimeDir() if err != nil { return Monitor{}, err } + if !rootless.IsRootless() { + rtDir = "/run" + } rtDir = filepath.Join(rtDir, "podman") if _, err := os.Stat(filepath.Join(rtDir)); os.IsNotExist(err) { // TODO 0644 is fine on linux but macos is weird @@ -533,3 +585,47 @@ func CheckActiveVM() (bool, string, error) { } return false, "", nil } + +// startHostNetworking runs a binary on the host system that allows users +// to setup port forwarding to the podman virtual machine +func (v *MachineVM) startHostNetworking() error { + binary, err := exec.LookPath(machine.ForwarderBinaryName) + if err != nil { + return err + } + // Listen on all at port 7777 for setting up and tearing + // down forwarding + listenSocket := "tcp://0.0.0.0:7777" + qemuSocket, pidFile, err := v.getSocketandPid() + if err != nil { + return err + } + attr := new(os.ProcAttr) + // Pass on stdin, stdout, stderr + files := []*os.File{os.Stdin, os.Stdout, os.Stderr} + attr.Files = files + cmd := []string{binary} + cmd = append(cmd, []string{"-listen", listenSocket, "-listen-qemu", fmt.Sprintf("unix://%s", qemuSocket), "-pid-file", pidFile}...) + // Add the ssh port + cmd = append(cmd, []string{"-ssh-port", fmt.Sprintf("%d", v.Port)}...) + if logrus.GetLevel() == logrus.DebugLevel { + cmd = append(cmd, "--debug") + fmt.Println(cmd) + } + _, err = os.StartProcess(cmd[0], cmd, attr) + return err +} + +func (v *MachineVM) getSocketandPid() (string, string, error) { + rtPath, err := getRuntimeDir() + if err != nil { + return "", "", err + } + if !rootless.IsRootless() { + rtPath = "/run" + } + socketDir := filepath.Join(rtPath, "podman") + pidFile := filepath.Join(socketDir, fmt.Sprintf("%s.pid", v.Name)) + qemuSocket := filepath.Join(socketDir, fmt.Sprintf("qemu_%s.sock", v.Name)) + return qemuSocket, pidFile, nil +} -- cgit v1.2.3-54-g00ecf