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 <gscrivan@redhat.com>
This commit is contained in:
Giuseppe Scrivano 2019-10-17 13:20:32 +02:00
parent 411d060b81
commit 85083ea434
No known key found for this signature in database
GPG Key ID: E4730F97F60286ED
3 changed files with 26 additions and 3 deletions

View File

@ -1,6 +1,6 @@
language: go language: go
sudo: required sudo: required
dist: trusty dist: xenial
go: go:
- 1.11.x - 1.11.x

View File

@ -22,17 +22,36 @@ import (
"runtime" "runtime"
"strings" "strings"
"sync" "sync"
"syscall"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
"golang.org/x/sys/unix" "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 // Creates a new persistent (bind-mounted) network namespace and returns an object
// representing that namespace, without switching to it. // representing that namespace, without switching to it.
func NewNS() (ns.NetNS, error) { func NewNS() (ns.NetNS, error) {
nsRunDir := getNsRunDir()
b := make([]byte, 16) b := make([]byte, 16)
_, err := rand.Reader.Read(b) _, err := rand.Reader.Read(b)
if err != nil { if err != nil {
@ -135,7 +154,7 @@ func NewNS() (ns.NetNS, error) {
func UnmountNS(ns ns.NetNS) error { func UnmountNS(ns ns.NetNS) error {
nsPath := ns.Path() nsPath := ns.Path()
// Only unmount if it's been bind-mounted (don't touch namespaces in /proc...) // 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 { if err := unix.Unmount(nsPath, 0); err != nil {
return fmt.Errorf("failed to unmount NS: at %s: %v", nsPath, err) return fmt.Errorf("failed to unmount NS: at %s: %v", nsPath, err)
} }

View File

@ -57,3 +57,7 @@ if [ -n "${vetRes}" ]; then
echo -e "govet checking failed:\n${vetRes}" echo -e "govet checking failed:\n${vetRes}"
exit 255 exit 255
fi 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)