From d0431b7a0291bf7c7fb673d8369ea757c376f520 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Thu, 17 Oct 2019 13:20:32 +0200 Subject: [PATCH] testutils: newNS() works in a rootless user namespace When running in a user namespace created by an unprivileged user the owner of /var/run will be reported as the unknown user (as defined in /proc/sys/kernel/overflowuid) so any access to the directory will fail. If the XDG_RUNTIME_DIR environment variable is set, check whether the current user is also the owner of /var/run. If the owner is different than the current user, use the $XDG_RUNTIME_DIR/netns directory. Signed-off-by: Giuseppe Scrivano --- pkg/testutils/netns_linux.go | 23 +++++++++++++++++++++-- test_linux.sh | 4 ++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/pkg/testutils/netns_linux.go b/pkg/testutils/netns_linux.go index 6d56e4050..f009bfb0a 100644 --- a/pkg/testutils/netns_linux.go +++ b/pkg/testutils/netns_linux.go @@ -22,17 +22,36 @@ import ( "runtime" "strings" "sync" + "syscall" "github.com/containernetworking/plugins/pkg/ns" "golang.org/x/sys/unix" ) -const nsRunDir = "/var/run/netns" +func getNsRunDir() string { + xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR") + + /// If XDG_RUNTIME_DIR is set, check if the current user owns /var/run. If + // the owner is different, we are most likely running in a user namespace. + // In that case use $XDG_RUNTIME_DIR/netns as runtime dir. + if xdgRuntimeDir != "" { + if s, err := os.Stat("/var/run"); err == nil { + st, ok := s.Sys().(*syscall.Stat_t) + if ok && int(st.Uid) != os.Geteuid() { + return path.Join(xdgRuntimeDir, "netns") + } + } + } + + return "/var/run/netns" +} // 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 := getNsRunDir() + b := make([]byte, 16) _, err := rand.Reader.Read(b) if err != nil { @@ -135,7 +154,7 @@ func NewNS() (ns.NetNS, error) { func UnmountNS(ns ns.NetNS) error { nsPath := ns.Path() // Only unmount if it's been bind-mounted (don't touch namespaces in /proc...) - if strings.HasPrefix(nsPath, nsRunDir) { + if strings.HasPrefix(nsPath, getNsRunDir()) { if err := unix.Unmount(nsPath, 0); err != nil { return fmt.Errorf("failed to unmount NS: at %s: %v", nsPath, err) } diff --git a/test_linux.sh b/test_linux.sh index 858534b3d..d726ddb30 100755 --- a/test_linux.sh +++ b/test_linux.sh @@ -57,3 +57,7 @@ if [ -n "${vetRes}" ]; then echo -e "govet checking failed:\n${vetRes}" exit 255 fi + +# Run the pkg/ns tests as non root user +mkdir /tmp/cni-rootless +(export XDG_RUNTIME_DIR=/tmp/cni-rootless; cd pkg/ns/; unshare -rmn go test)