From 54b588c07d05858c9bbc523eeff0badb85d53f76 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Thu, 18 Feb 2021 13:51:27 +0100 Subject: rootless cni without infra container Instead of creating an extra container create a network and mount namespace inside the podman user namespace. This ns is used to for rootless cni operations. This helps to align the rootless and rootful network code path. If we run as rootless we just have to set up a extra net ns and initialize slirp4netns in it. The ocicni lib will be called in that net ns. This design allows allows easier maintenance, no extra container with pause processes, support for rootless cni with --uidmap and possibly more. The biggest problem is backwards compatibility. I don't think live migration can be possible. If the user reboots or restart all cni containers everything should work as expected again. The user is left with the rootless-cni-infa container and image but this can safely be removed. To make the existing cni configs work we need execute the cni plugins in a extra mount namespace. This ensures that we can safely mount over /run and /var which have to be writeable for the cni plugins without removing access to these files by the main podman process. One caveat is that we need to keep the netns files at `XDG_RUNTIME_DIR/netns` accessible. `XDG_RUNTIME_DIR/rootless-cni/{run,var}` will be mounted to `/{run,var}`. To ensure that we keep the netns directory we bind mount this relative to the new root location, e.g. XDG_RUNTIME_DIR/rootless-cni/run/user/1000/netns before we mount the run directory. The run directory is mounted recursive, this makes the netns directory at the same path accessible as before. This also allows iptables-legacy to work because /run/xtables.lock is now writeable. Signed-off-by: Paul Holzinger --- pkg/netns/netns_linux.go | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'pkg') diff --git a/pkg/netns/netns_linux.go b/pkg/netns/netns_linux.go index 0b7d1782c..ecefb65ff 100644 --- a/pkg/netns/netns_linux.go +++ b/pkg/netns/netns_linux.go @@ -35,9 +35,9 @@ import ( "golang.org/x/sys/unix" ) -// get NSRunDir returns the dir of where to create the netNS. When running +// GetNSRunDir returns the dir of where to create the netNS. When running // rootless, it needs to be at a location writable by user. -func getNSRunDir() (string, error) { +func GetNSRunDir() (string, error) { if rootless.IsRootless() { rootlessDir, err := util.GetRuntimeDir() if err != nil { @@ -51,15 +51,21 @@ func getNSRunDir() (string, error) { // NewNS creates a new persistent (bind-mounted) network namespace and returns // an object representing that namespace, without switching to it. func NewNS() (ns.NetNS, error) { - nsRunDir, err := getNSRunDir() + b := make([]byte, 16) + _, err := rand.Reader.Read(b) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to generate random netns name: %v", err) } + nsName := fmt.Sprintf("cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) + return NewNSWithName(nsName) +} - b := make([]byte, 16) - _, err = rand.Reader.Read(b) +// NewNSWithName creates a new persistent (bind-mounted) network namespace and returns +// an object representing that namespace, without switching to it. +func NewNSWithName(name string) (ns.NetNS, error) { + nsRunDir, err := GetNSRunDir() if err != nil { - return nil, fmt.Errorf("failed to generate random netns name: %v", err) + return nil, err } // Create the directory for mounting network namespaces @@ -93,10 +99,8 @@ func NewNS() (ns.NetNS, error) { } } - nsName := fmt.Sprintf("cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) - // create an empty file at the mount point - nsPath := path.Join(nsRunDir, nsName) + nsPath := path.Join(nsRunDir, name) mountPointFd, err := os.Create(nsPath) if err != nil { return nil, err @@ -177,7 +181,7 @@ func NewNS() (ns.NetNS, error) { // UnmountNS unmounts the NS held by the netns object func UnmountNS(ns ns.NetNS) error { - nsRunDir, err := getNSRunDir() + nsRunDir, err := GetNSRunDir() if err != nil { return err } -- cgit v1.2.3-54-g00ecf From 00b2ec5e6f8ad332411271df1bdd968493cab2c2 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Thu, 18 Feb 2021 14:53:53 +0100 Subject: Add rootless support for cni and --uidmap This is supported with the new rootless cni logic. Signed-off-by: Paul Holzinger --- libpod/container_internal.go | 4 +--- libpod/networking_linux.go | 7 +++++-- pkg/specgen/generate/namespaces.go | 3 --- test/e2e/run_networking_test.go | 20 ++++++++++++-------- 4 files changed, 18 insertions(+), 16 deletions(-) (limited to 'pkg') diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 106e2569b..a53027ab2 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -966,9 +966,7 @@ func (c *Container) completeNetworkSetup() error { if err := c.syncContainer(); err != nil { return err } - if rootless.IsRootless() { - return c.runtime.setupRootlessNetNS(c) - } else if c.config.NetMode.IsSlirp4netns() { + if c.config.NetMode.IsSlirp4netns() { return c.runtime.setupSlirp4netns(c) } if err := c.runtime.setupNetNS(c); err != nil { diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index aac02d8cc..1bfb82cdb 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -972,8 +972,11 @@ func (r *Runtime) setupNetNS(ctr *Container) error { if _, err := rand.Reader.Read(b); err != nil { return errors.Wrapf(err, "failed to generate random netns name") } - - nsPath := fmt.Sprintf("/run/netns/cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) + nsPath, err := netns.GetNSRunDir() + if err != nil { + return err + } + nsPath = filepath.Join(nsPath, fmt.Sprintf("cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])) if err := os.MkdirAll(filepath.Dir(nsPath), 0711); err != nil { return err diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index b87375a92..845dfdad7 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -236,9 +236,6 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod. case specgen.Private: fallthrough case specgen.Bridge: - if postConfigureNetNS && rootless.IsRootless() { - return nil, errors.New("CNI networks not supported with user namespaces") - } portMappings, err := createPortMappings(ctx, s, img) if err != nil { return nil, err diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index 0e6e636bc..43eb8fe4e 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -641,22 +641,26 @@ var _ = Describe("Podman run networking", func() { Expect(run.OutputToString()).To(ContainSubstring(ipAddr)) }) - It("podman rootless fails custom CNI network with --uidmap", func() { - SkipIfNotRootless("The configuration works with rootless") - + It("podman cni network works across user ns", func() { netName := stringid.GenerateNonCryptoID() create := podmanTest.Podman([]string{"network", "create", netName}) create.WaitWithDefaultTimeout() Expect(create.ExitCode()).To(BeZero()) defer podmanTest.removeCNINetwork(netName) - run := podmanTest.Podman([]string{"run", "--rm", "--net", netName, "--uidmap", "0:1:4096", ALPINE, "true"}) + name := "nc-server" + run := podmanTest.Podman([]string{"run", "-d", "--name", name, "--net", netName, ALPINE, "nc", "-l", "-p", "8080"}) + run.WaitWithDefaultTimeout() + Expect(run.ExitCode()).To(Equal(0)) + + run = podmanTest.Podman([]string{"run", "--rm", "--net", netName, "--uidmap", "0:1:4096", ALPINE, "sh", "-c", fmt.Sprintf("echo podman | nc -w 1 %s.dns.podman 8080", name)}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(Equal(125)) + Expect(run.ExitCode()).To(Equal(0)) - remove := podmanTest.Podman([]string{"network", "rm", netName}) - remove.WaitWithDefaultTimeout() - Expect(remove.ExitCode()).To(BeZero()) + log := podmanTest.Podman([]string{"logs", name}) + log.WaitWithDefaultTimeout() + Expect(log.ExitCode()).To(Equal(0)) + Expect(log.OutputToString()).To(Equal("podman")) }) It("podman run with new:pod and static-ip", func() { -- cgit v1.2.3-54-g00ecf From 0743ead71289cf6198a14fddf071972df9b6a332 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Thu, 18 Feb 2021 23:37:03 +0100 Subject: Fix pod infra container cni network setup For rootless users the infra container used the slirp4netns net mode even when bridge was requested. We can support bridge networking for rootless users so we have allow this. The default is not changed. Signed-off-by: Paul Holzinger --- libpod/runtime_pod_infra_linux.go | 2 +- pkg/specgen/generate/pod_create.go | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'pkg') diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index 0a09e40ea..1ae375ed9 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -104,7 +104,7 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm default: // Since user namespace sharing is not implemented, we only need to check if it's rootless netmode := "bridge" - if isRootless || p.config.InfraContainer.Slirp4netns { + if p.config.InfraContainer.Slirp4netns { netmode = "slirp4netns" if len(p.config.InfraContainer.NetworkOptions) != 0 { options = append(options, WithNetworkOptions(p.config.InfraContainer.NetworkOptions)) diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index 5d7bf1930..20151f016 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -4,6 +4,7 @@ import ( "context" "github.com/containers/podman/v3/libpod" + "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/specgen" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -94,8 +95,19 @@ func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime) ([]libpod } switch p.NetNS.NSMode { - case specgen.Bridge, specgen.Default, "": - logrus.Debugf("Pod using default network mode") + case specgen.Default, "": + if p.NoInfra { + logrus.Debugf("No networking because the infra container is missing") + break + } + if rootless.IsRootless() { + logrus.Debugf("Pod will use slirp4netns") + options = append(options, libpod.WithPodSlirp4netns(p.NetworkOptions)) + } else { + logrus.Debugf("Pod using bridge network mode") + } + case specgen.Bridge: + logrus.Debugf("Pod using bridge network mode") case specgen.Host: logrus.Debugf("Pod will use host networking") options = append(options, libpod.WithPodHostNetwork()) -- cgit v1.2.3-54-g00ecf