mirror of https://github.com/containers/podman.git
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 <bbaude@redhat.com>
This commit is contained in:
parent
7dd463bad1
commit
7ef3981abe
|
@ -85,7 +85,10 @@ func DefineNetFlags(cmd *cobra.Command) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NetFlagsToNetOptions(cmd *cobra.Command) (*entities.NetOptions, error) {
|
// NetFlagsToNetOptions parses the network flags for the given cmd.
|
||||||
|
// The netnsFromConfig bool is used to indicate if the --network flag
|
||||||
|
// should always be parsed regardless if it was set on the cli.
|
||||||
|
func NetFlagsToNetOptions(cmd *cobra.Command, netnsFromConfig bool) (*entities.NetOptions, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
@ -193,7 +196,9 @@ func NetFlagsToNetOptions(cmd *cobra.Command) (*entities.NetOptions, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.Flags().Changed("network") {
|
// parse the --network value only when the flag is set or we need to use
|
||||||
|
// the netns config value, e.g. when --pod is not used
|
||||||
|
if netnsFromConfig || cmd.Flag("network").Changed {
|
||||||
network, err := cmd.Flags().GetString("network")
|
network, err := cmd.Flags().GetString("network")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -87,7 +87,7 @@ func create(cmd *cobra.Command, args []string) error {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
cliVals.Net, err = common.NetFlagsToNetOptions(cmd)
|
cliVals.Net, err = common.NetFlagsToNetOptions(cmd, cliVals.Pod == "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,7 +106,7 @@ func init() {
|
||||||
|
|
||||||
func run(cmd *cobra.Command, args []string) error {
|
func run(cmd *cobra.Command, args []string) error {
|
||||||
var err error
|
var err error
|
||||||
cliVals.Net, err = common.NetFlagsToNetOptions(cmd)
|
cliVals.Net, err = common.NetFlagsToNetOptions(cmd, cliVals.Pod == "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,10 +166,11 @@ func create(cmd *cobra.Command, args []string) error {
|
||||||
defer errorhandling.SyncQuiet(podIDFD)
|
defer errorhandling.SyncQuiet(podIDFD)
|
||||||
}
|
}
|
||||||
|
|
||||||
createOptions.Net, err = common.NetFlagsToNetOptions(cmd)
|
createOptions.Net, err = common.NetFlagsToNetOptions(cmd, createOptions.Infra)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(createOptions.Net.PublishPorts) > 0 {
|
if len(createOptions.Net.PublishPorts) > 0 {
|
||||||
if !createOptions.Infra {
|
if !createOptions.Infra {
|
||||||
return errors.Errorf("you must have an infra container to publish port bindings to the host")
|
return errors.Errorf("you must have an infra container to publish port bindings to the host")
|
||||||
|
|
|
@ -273,7 +273,6 @@ func (r *Runtime) GetRootlessCNINetNs(new bool) (*RootlessCNI, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "error creating rootless cni network namespace")
|
return nil, errors.Wrap(err, "error creating rootless cni network namespace")
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup slirp4netns here
|
// setup slirp4netns here
|
||||||
path := r.config.Engine.NetworkCmdPath
|
path := r.config.Engine.NetworkCmdPath
|
||||||
if path == "" {
|
if path == "" {
|
||||||
|
@ -437,9 +436,32 @@ func (r *Runtime) GetRootlessCNINetNs(new bool) (*RootlessCNI, error) {
|
||||||
return rootlessCNINS, nil
|
return rootlessCNINS, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setPrimaryMachineIP is used for podman-machine and it sets
|
||||||
|
// and environment variable with the IP address of the podman-machine
|
||||||
|
// host.
|
||||||
|
func setPrimaryMachineIP() error {
|
||||||
|
// no connection is actually made here
|
||||||
|
conn, err := net.Dial("udp", "8.8.8.8:80")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := conn.Close(); err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
addr := conn.LocalAddr().(*net.UDPAddr)
|
||||||
|
return os.Setenv("PODMAN_MACHINE_HOST", addr.IP.String())
|
||||||
|
}
|
||||||
|
|
||||||
// setUpOCICNIPod will set up the cni networks, on error it will also tear down the cni
|
// setUpOCICNIPod will set up the cni networks, on error it will also tear down the cni
|
||||||
// networks. If rootless it will join/create the rootless cni namespace.
|
// networks. If rootless it will join/create the rootless cni namespace.
|
||||||
func (r *Runtime) setUpOCICNIPod(podNetwork ocicni.PodNetwork) ([]ocicni.NetResult, error) {
|
func (r *Runtime) setUpOCICNIPod(podNetwork ocicni.PodNetwork) ([]ocicni.NetResult, error) {
|
||||||
|
if r.config.MachineEnabled() {
|
||||||
|
if err := setPrimaryMachineIP(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
rootlessCNINS, err := r.GetRootlessCNINetNs(true)
|
rootlessCNINS, err := r.GetRootlessCNINetNs(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -32,6 +32,7 @@ var (
|
||||||
ErrVMAlreadyExists = errors.New("VM already exists")
|
ErrVMAlreadyExists = errors.New("VM already exists")
|
||||||
ErrVMAlreadyRunning = errors.New("VM already running")
|
ErrVMAlreadyRunning = errors.New("VM already running")
|
||||||
ErrMultipleActiveVM = errors.New("only one VM can be active at a time")
|
ErrMultipleActiveVM = errors.New("only one VM can be active at a time")
|
||||||
|
ForwarderBinaryName = "gvproxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Download struct {
|
type Download struct {
|
||||||
|
|
|
@ -118,6 +118,7 @@ func getDirs(usrName string) []Directory {
|
||||||
// in one swoop, then the leading dirs are creates as root.
|
// in one swoop, then the leading dirs are creates as root.
|
||||||
newDirs := []string{
|
newDirs := []string{
|
||||||
"/home/" + usrName + "/.config",
|
"/home/" + usrName + "/.config",
|
||||||
|
"/home/" + usrName + "/.config/containers",
|
||||||
"/home/" + usrName + "/.config/systemd",
|
"/home/" + usrName + "/.config/systemd",
|
||||||
"/home/" + usrName + "/.config/systemd/user",
|
"/home/" + usrName + "/.config/systemd/user",
|
||||||
"/home/" + usrName + "/.config/systemd/user/default.target.wants",
|
"/home/" + usrName + "/.config/systemd/user/default.target.wants",
|
||||||
|
@ -159,6 +160,22 @@ func getFiles(usrName string) []File {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Set containers.conf up for core user to use cni networks
|
||||||
|
// by default
|
||||||
|
files = append(files, File{
|
||||||
|
Node: Node{
|
||||||
|
Group: getNodeGrp(usrName),
|
||||||
|
Path: "/home/" + usrName + "/.config/containers/containers.conf",
|
||||||
|
User: getNodeUsr(usrName),
|
||||||
|
},
|
||||||
|
FileEmbedded1: FileEmbedded1{
|
||||||
|
Append: nil,
|
||||||
|
Contents: Resource{
|
||||||
|
Source: strToPtr("data:,%5Bcontainers%5D%0D%0Anetns%3D%22bridge%22%0D%0Arootless_networking%3D%22cni%22"),
|
||||||
|
},
|
||||||
|
Mode: intToPtr(484),
|
||||||
|
},
|
||||||
|
})
|
||||||
// Add a file into linger
|
// Add a file into linger
|
||||||
files = append(files, File{
|
files = append(files, File{
|
||||||
Node: Node{
|
Node: Node{
|
||||||
|
|
|
@ -13,6 +13,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/podman/v3/pkg/rootless"
|
||||||
|
|
||||||
"github.com/containers/podman/v3/pkg/machine"
|
"github.com/containers/podman/v3/pkg/machine"
|
||||||
"github.com/containers/podman/v3/utils"
|
"github.com/containers/podman/v3/utils"
|
||||||
"github.com/containers/storage/pkg/homedir"
|
"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"}...)
|
cmd = append(cmd, []string{"-qmp", monitor.Network + ":/" + monitor.Address + ",server=on,wait=off"}...)
|
||||||
|
|
||||||
// Add network
|
// Add network
|
||||||
cmd = append(cmd, "-nic", "user,model=virtio,hostfwd=tcp::"+strconv.Itoa(vm.Port)+"-:22")
|
// 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
|
||||||
socketPath, err := getSocketDir()
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -235,12 +238,35 @@ func (v *MachineVM) Init(opts machine.InitOptions) error {
|
||||||
// Start executes the qemu command line and forks it
|
// Start executes the qemu command line and forks it
|
||||||
func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
|
func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
|
||||||
var (
|
var (
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
err error
|
err error
|
||||||
wait time.Duration = time.Millisecond * 500
|
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)
|
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
|
attr.Files = files
|
||||||
logrus.Debug(v.CmdLine)
|
logrus.Debug(v.CmdLine)
|
||||||
cmd := v.CmdLine
|
cmd := v.CmdLine
|
||||||
|
@ -256,7 +282,7 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println("Waiting for VM ...")
|
fmt.Println("Waiting for VM ...")
|
||||||
socketPath, err := getSocketDir()
|
socketPath, err := getRuntimeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -309,16 +335,42 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
|
||||||
logrus.Error(err)
|
logrus.Error(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
_, err = qmpMonitor.Run(input)
|
if _, err = qmpMonitor.Run(input); err != nil {
|
||||||
return err
|
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
|
// NewQMPMonitor creates the monitor subsection of our vm
|
||||||
func NewQMPMonitor(network, name string, timeout time.Duration) (Monitor, error) {
|
func NewQMPMonitor(network, name string, timeout time.Duration) (Monitor, error) {
|
||||||
rtDir, err := getSocketDir()
|
rtDir, err := getRuntimeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Monitor{}, err
|
return Monitor{}, err
|
||||||
}
|
}
|
||||||
|
if !rootless.IsRootless() {
|
||||||
|
rtDir = "/run"
|
||||||
|
}
|
||||||
rtDir = filepath.Join(rtDir, "podman")
|
rtDir = filepath.Join(rtDir, "podman")
|
||||||
if _, err := os.Stat(filepath.Join(rtDir)); os.IsNotExist(err) {
|
if _, err := os.Stat(filepath.Join(rtDir)); os.IsNotExist(err) {
|
||||||
// TODO 0644 is fine on linux but macos is weird
|
// TODO 0644 is fine on linux but macos is weird
|
||||||
|
@ -533,3 +585,47 @@ func CheckActiveVM() (bool, string, error) {
|
||||||
}
|
}
|
||||||
return false, "", nil
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getSocketDir() (string, error) {
|
func getRuntimeDir() (string, error) {
|
||||||
tmpDir, ok := os.LookupEnv("TMPDIR")
|
tmpDir, ok := os.LookupEnv("TMPDIR")
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", errors.New("unable to resolve TMPDIR")
|
return "", errors.New("unable to resolve TMPDIR")
|
||||||
|
|
|
@ -5,7 +5,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (v *MachineVM) addArchOptions() []string {
|
func (v *MachineVM) addArchOptions() []string {
|
||||||
opts := []string{"-cpu", "host"}
|
opts := []string{"-machine", "q35,accel=hvf:tcg"}
|
||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
package qemu
|
package qemu
|
||||||
|
|
||||||
import "github.com/containers/podman/v3/pkg/util"
|
import (
|
||||||
|
"github.com/containers/podman/v3/pkg/rootless"
|
||||||
|
"github.com/containers/podman/v3/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
func getSocketDir() (string, error) {
|
func getRuntimeDir() (string, error) {
|
||||||
|
if !rootless.IsRootless() {
|
||||||
|
return "/run", nil
|
||||||
|
}
|
||||||
return util.GetRuntimeDir()
|
return util.GetRuntimeDir()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue