From 8d12f19371eb9d91139f7b982cde2926ec8c8e74 Mon Sep 17 00:00:00 2001 From: aleks-mariusz Date: Tue, 16 Jun 2020 11:36:33 +0100 Subject: allow switching of port-forward approaches in rootless/using slirp4netns As of podman 1.8.0, because of commit da7595a, the default approach of providing port-forwarding in rootless mode has switched (and been hard-coded) to rootlessport, for the purpose of providing super performance. The side-effect of this switch is source within the container to the port-forwarded service always appears to originate from 127.0.0.1 (see issue #5138). This commit allows a user to specify if they want to revert to the previous approach of leveraging slirp4netns add_hostfwd() api which, although not as stellar performance, restores usefulness of seeing incoming traffic origin IP addresses. The change should be transparent; when not specified, rootlessport will continue to be used, however if specifying --net slirp4netns:slirplisten the old approach will be used. Note: the above may imply the restored port-forwarding via slirp4netns is not as performant as the new rootlessport approach, however the figures shared in the original commit that introduced rootlessport are as follows: slirp4netns: 8.3 Gbps, RootlessKit: 27.3 Gbps, which are more than sufficient for many use cases where the origin of traffic is more important than limits that cannot be reached due to bottlenecks elsewhere. Signed-off-by: Aleks Mariusz Signed-off-by: Giuseppe Scrivano --- pkg/specgen/namespaces.go | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'pkg/specgen/namespaces.go') diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go index 5f56b242b..e62c8b709 100644 --- a/pkg/specgen/namespaces.go +++ b/pkg/specgen/namespaces.go @@ -108,7 +108,19 @@ func validateNetNS(n *Namespace) error { return nil } switch n.NSMode { - case "", Default, Host, Path, FromContainer, FromPod, Private, NoNetwork, Bridge, Slirp: + case Slirp: + if n.Value != "" { + parts := strings.Split(n.Value, ",") + for _, p := range parts { + switch p { + case "port_handler=slirp4netns", "port_handler=rootlesskit": + default: + return errors.Errorf("invalid value for slirp %q", n.Value) + } + } + } + break + case "", Default, Host, Path, FromContainer, FromPod, Private, NoNetwork, Bridge: break default: return errors.Errorf("invalid network %q", n.NSMode) @@ -119,8 +131,8 @@ func validateNetNS(n *Namespace) error { if len(n.Value) < 1 { return errors.Errorf("namespace mode %s requires a value", n.NSMode) } - } else { - // All others must NOT set a string value + } else if n.NSMode != Slirp { + // All others except must NOT set a string value if len(n.Value) > 0 { return errors.Errorf("namespace value %s cannot be provided with namespace mode %s", n.Value, n.NSMode) } @@ -250,8 +262,12 @@ func ParseNetworkNamespace(ns string) (Namespace, []string, error) { var cniNetworks []string // Net defaults to Slirp on rootless switch { - case ns == "slirp4netns": + case ns == "slirp4netns", strings.HasPrefix(ns, "slirp4netns:"): + split := strings.SplitN(ns, ":", 2) toReturn.NSMode = Slirp + if len(split) > 1 { + toReturn.Value = split[1] + } case ns == "pod": toReturn.NSMode = FromPod case ns == "": -- cgit v1.2.3-54-g00ecf From 9be7029cdd4736f3ac33004e4364e3e7f3bd1db5 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Thu, 16 Jul 2020 12:19:51 +0200 Subject: libpod: pass down network options do not pass network specific options through the network namespace. Signed-off-by: Giuseppe Scrivano --- cmd/podman/common/netflags.go | 8 ++++++++ cmd/podman/common/specgen.go | 1 + libpod/container.go | 2 ++ libpod/networking_linux.go | 20 ++++++++++++++++++-- libpod/options.go | 13 +++++++++++++ pkg/domain/entities/types.go | 2 ++ pkg/namespaces/namespaces.go | 24 ------------------------ pkg/specgen/generate/namespaces.go | 5 ++++- pkg/specgen/namespaces.go | 14 -------------- pkg/specgen/specgen.go | 3 +++ test/e2e/run_networking_test.go | 23 +++++++++++++++++++++++ 11 files changed, 74 insertions(+), 41 deletions(-) (limited to 'pkg/specgen/namespaces.go') diff --git a/cmd/podman/common/netflags.go b/cmd/podman/common/netflags.go index 5c83a3c9c..82a82b310 100644 --- a/cmd/podman/common/netflags.go +++ b/cmd/podman/common/netflags.go @@ -2,6 +2,7 @@ package common import ( "net" + "strings" "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/libpod/define" @@ -164,11 +165,18 @@ func NetFlagsToNetOptions(cmd *cobra.Command) (*entities.NetOptions, error) { return nil, err } + parts := strings.SplitN(network, ":", 2) + ns, cniNets, err := specgen.ParseNetworkNamespace(network) if err != nil { return nil, err } + if len(parts) > 1 { + opts.NetworkOptions = make(map[string][]string) + opts.NetworkOptions[parts[0]] = strings.Split(parts[1], ",") + cniNets = nil + } opts.Network = ns opts.CNINetworks = cniNets } diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go index 8a265cedf..08099da4b 100644 --- a/cmd/podman/common/specgen.go +++ b/cmd/podman/common/specgen.go @@ -417,6 +417,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string s.DNSOptions = c.Net.DNSOptions s.StaticIP = c.Net.StaticIP s.StaticMAC = c.Net.StaticMAC + s.NetworkOptions = c.Net.NetworkOptions s.UseImageHosts = c.Net.NoHosts s.ImageVolumeMode = c.ImageVolume diff --git a/libpod/container.go b/libpod/container.go index f7abfb005..1ca38ae7e 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -341,6 +341,8 @@ type ContainerConfig struct { Networks []string `json:"networks,omitempty"` // Network mode specified for the default network. NetMode namespaces.NetworkMode `json:"networkMode,omitempty"` + // NetworkOptions are additional options for each network + NetworkOptions map[string][]string `json:"network_options,omitempty"` // Image Config diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index db8aa219b..9be8e0b14 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -223,6 +223,22 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) error { havePortMapping := len(ctr.Config().PortMappings) > 0 logPath := filepath.Join(ctr.runtime.config.Engine.TmpDir, fmt.Sprintf("slirp4netns-%s.log", ctr.config.ID)) + isSlirpHostForward := false + if ctr.config.NetworkOptions != nil { + slirpOptions := ctr.config.NetworkOptions["slirp4netns"] + for _, o := range slirpOptions { + switch o { + case "port_handler=slirp4netns": + isSlirpHostForward = true + case "port_handler=rootlesskit": + isSlirpHostForward = false + default: + return errors.Errorf("unknown option for slirp4netns: %q", o) + + } + } + } + cmdArgs := []string{} slirpFeatures, err := checkSlirpFlags(path) if err != nil { @@ -242,7 +258,7 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) error { } var apiSocket string - if havePortMapping && ctr.config.NetMode.IsPortForwardViaSlirpHostFwd() { + if havePortMapping && isSlirpHostForward { apiSocket = filepath.Join(ctr.runtime.config.Engine.TmpDir, fmt.Sprintf("%s.net", ctr.config.ID)) cmdArgs = append(cmdArgs, "--api-socket", apiSocket) } @@ -310,7 +326,7 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) error { } if havePortMapping { - if ctr.config.NetMode.IsPortForwardViaSlirpHostFwd() { + if isSlirpHostForward { return r.setupRootlessPortMappingViaSlirp(ctr, cmd, apiSocket) } else { return r.setupRootlessPortMappingViaRLK(ctr, netnsPath) diff --git a/libpod/options.go b/libpod/options.go index b3c11ebc1..32748a3c1 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1003,6 +1003,19 @@ func WithStaticIP(ip net.IP) CtrCreateOption { } } +// WithNetworkOptions sets additional options for the networks. +func WithNetworkOptions(options map[string][]string) CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return define.ErrCtrFinalized + } + + ctr.config.NetworkOptions = options + + return nil + } +} + // WithStaticMAC indicates that the container should request a static MAC from // the CNI plugins. // It cannot be set unless WithNetNS has already been passed. diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go index 7e910ff61..b313e5f8b 100644 --- a/pkg/domain/entities/types.go +++ b/pkg/domain/entities/types.go @@ -42,6 +42,8 @@ type NetOptions struct { PublishPorts []specgen.PortMapping StaticIP *net.IP StaticMAC *net.HardwareAddr + // NetworkOptions are additional options for each network + NetworkOptions map[string][]string } // All CLI inspect commands and inspect sub-commands use the same options diff --git a/pkg/namespaces/namespaces.go b/pkg/namespaces/namespaces.go index b4ec04699..7831af8f9 100644 --- a/pkg/namespaces/namespaces.go +++ b/pkg/namespaces/namespaces.go @@ -17,9 +17,7 @@ const ( nsType = "ns" podType = "pod" privateType = "private" - rlkFwdType = "port_handler=rootlesskit" shareableType = "shareable" - slirpFwdType = "port_handler=slirp4netns" slirpType = "slirp4netns" ) @@ -390,28 +388,6 @@ func (n NetworkMode) IsSlirp4netns() bool { return n == slirpType || strings.HasPrefix(string(n), slirpType+":") } -// IsPortForwardViaRootlessKit indicates if we are doing rootless port-forwarding via rootlesskit/rootlessport -func (n NetworkMode) IsPortForwardViaRootlessKit() bool { - if !n.IsSlirp4netns() { - return false - } - parts := strings.SplitN(string(n), ":", 2) - if len(parts) == 2 { - return parts[1] == rlkFwdType - } - return true -} - -// IsPortForwardViaSlirpHostFwd indicates if we are doing rootless port-forwarding via slirp4netns add_hostfwd() -func (n NetworkMode) IsPortForwardViaSlirpHostFwd() bool { - if !n.IsSlirp4netns() { - return false - } - // below here, implied IsSlirp4netns() == true - parts := strings.SplitN(string(n), ":", 2) - return len(parts) > 1 && parts[1] == slirpFwdType -} - // IsNS indicates a network namespace passed in by path (ns:) func (n NetworkMode) IsNS() bool { return strings.HasPrefix(string(n), nsType) diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index e038fc84e..a19009bc2 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -266,6 +266,9 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod. if s.StaticMAC != nil { toReturn = append(toReturn, libpod.WithStaticMAC(*s.StaticMAC)) } + if s.NetworkOptions != nil { + toReturn = append(toReturn, libpod.WithNetworkOptions(s.NetworkOptions)) + } return toReturn, nil } @@ -470,7 +473,7 @@ func GetNamespaceOptions(ns []string) ([]libpod.PodCreateOption, error) { case "pid": options = append(options, libpod.WithPodPID()) case "user": - return erroredOptions, errors.Errorf("User sharing functionality not supported on pod level") + continue case "ipc": options = append(options, libpod.WithPodIPC()) case "uts": diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go index e62c8b709..9bf2c5d05 100644 --- a/pkg/specgen/namespaces.go +++ b/pkg/specgen/namespaces.go @@ -109,16 +109,6 @@ func validateNetNS(n *Namespace) error { } switch n.NSMode { case Slirp: - if n.Value != "" { - parts := strings.Split(n.Value, ",") - for _, p := range parts { - switch p { - case "port_handler=slirp4netns", "port_handler=rootlesskit": - default: - return errors.Errorf("invalid value for slirp %q", n.Value) - } - } - } break case "", Default, Host, Path, FromContainer, FromPod, Private, NoNetwork, Bridge: break @@ -263,11 +253,7 @@ func ParseNetworkNamespace(ns string) (Namespace, []string, error) { // Net defaults to Slirp on rootless switch { case ns == "slirp4netns", strings.HasPrefix(ns, "slirp4netns:"): - split := strings.SplitN(ns, ":", 2) toReturn.NSMode = Slirp - if len(split) > 1 { - toReturn.Value = split[1] - } case ns == "pod": toReturn.NSMode = FromPod case ns == "": diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index 16d4b7c8c..3331bf8c8 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -378,6 +378,9 @@ type ContainerNetworkConfig struct { // Conflicts with UseImageHosts. // Optional. HostAdd []string `json:"hostadd,omitempty"` + // NetworkOptions are additional options for each network + // Optional. + NetworkOptions map[string][]string `json:"network_options,omitempty"` } // ContainerResourceConfig contains information on container resource limits. diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index 5a463d46f..50937a10b 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -221,6 +221,29 @@ var _ = Describe("Podman run networking", func() { Expect(ncBusy).To(ExitWithError()) }) + It("podman run network expose host port 8081 to container port 8000 using rootlesskit port handler", func() { + session := podmanTest.Podman([]string{"run", "--network", "slirp4netns:port_handler=rootlesskit", "-dt", "-p", "8081:8000", ALPINE, "/bin/sh"}) + session.Wait(30) + Expect(session.ExitCode()).To(Equal(0)) + + ncBusy := SystemExec("nc", []string{"-l", "-p", "8081"}) + Expect(ncBusy).To(ExitWithError()) + }) + + It("podman run network expose host port 8082 to container port 8000 using slirp4netns port handler", func() { + session := podmanTest.Podman([]string{"run", "--network", "slirp4netns:port_handler=slirp4netns", "-dt", "-p", "8082:8000", ALPINE, "/bin/sh"}) + session.Wait(30) + Expect(session.ExitCode()).To(Equal(0)) + ncBusy := SystemExec("nc", []string{"-l", "-p", "8082"}) + Expect(ncBusy).To(ExitWithError()) + }) + + It("podman run network expose host port 8080 to container port 8000 using invalid port handler", func() { + session := podmanTest.Podman([]string{"run", "--network", "slirp4netns:port_handler=invalid", "-dt", "-p", "8080:8000", ALPINE, "/bin/sh"}) + session.Wait(30) + Expect(session.ExitCode()).To(Not(Equal(0))) + }) + It("podman run network expose ports in image metadata", func() { session := podmanTest.Podman([]string{"create", "-dt", "-P", nginx}) session.Wait(90) -- cgit v1.2.3-54-g00ecf