package nsenter import ( "bytes" "encoding/json" "fmt" "io" "io/ioutil" "os" "os/exec" "strings" "syscall" "testing" "github.com/opencontainers/runc/libcontainer" "github.com/vishvananda/netlink/nl" ) type pid struct { Pid int `json:"Pid"` } func TestNsenterValidPaths(t *testing.T) { args := []string{"nsenter-exec"} parent, child, err := newPipe() if err != nil { t.Fatalf("failed to create pipe %v", err) } namespaces := []string{ // join pid ns of the current process fmt.Sprintf("/proc/%d/ns/pid", os.Getpid()), } cmd := &exec.Cmd{ Path: os.Args[0], Args: args, ExtraFiles: []*os.File{child}, Env: []string{"_LIBCONTAINER_INITPIPE=3"}, Stdout: os.Stdout, Stderr: os.Stderr, } if err := cmd.Start(); err != nil { t.Fatalf("nsenter failed to start %v", err) } // write cloneFlags r := nl.NewNetlinkRequest(int(libcontainer.InitMsg), 0) r.AddData(&libcontainer.Int32msg{ Type: libcontainer.CloneFlagsAttr, Value: uint32(syscall.CLONE_NEWNET), }) r.AddData(&libcontainer.Bytemsg{ Type: libcontainer.NsPathsAttr, Value: []byte(strings.Join(namespaces, ",")), }) if _, err := io.Copy(parent, bytes.NewReader(r.Serialize())); err != nil { t.Fatal(err) } decoder := json.NewDecoder(parent) var pid *pid if err := decoder.Decode(&pid); err != nil { dir, _ := ioutil.ReadDir(fmt.Sprintf("/proc/%d/ns", os.Getpid())) for _, d := range dir { t.Log(d.Name()) } t.Fatalf("%v", err) } if err := cmd.Wait(); err != nil { t.Fatalf("nsenter exits with a non-zero exit status") } p, err := os.FindProcess(pid.Pid) if err != nil { t.Fatalf("%v", err) } p.Wait() } func TestNsenterInvalidPaths(t *testing.T) { args := []string{"nsenter-exec"} parent, child, err := newPipe() if err != nil { t.Fatalf("failed to create pipe %v", err) } namespaces := []string{ // join pid ns of the current process fmt.Sprintf("/proc/%d/ns/pid", -1), } cmd := &exec.Cmd{ Path: os.Args[0], Args: args, ExtraFiles: []*os.File{child}, Env: []string{"_LIBCONTAINER_INITPIPE=3"}, } if err := cmd.Start(); err != nil { t.Fatal(err) } // write cloneFlags r := nl.NewNetlinkRequest(int(libcontainer.InitMsg), 0) r.AddData(&libcontainer.Int32msg{ Type: libcontainer.CloneFlagsAttr, Value: uint32(syscall.CLONE_NEWNET), }) r.AddData(&libcontainer.Bytemsg{ Type: libcontainer.NsPathsAttr, Value: []byte(strings.Join(namespaces, ",")), }) if _, err := io.Copy(parent, bytes.NewReader(r.Serialize())); err != nil { t.Fatal(err) } if err := cmd.Wait(); err == nil { t.Fatalf("nsenter exits with a zero exit status") } } func init() { if strings.HasPrefix(os.Args[0], "nsenter-") { os.Exit(0) } return } func newPipe() (parent *os.File, child *os.File, err error) { fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0) if err != nil { return nil, nil, err } return os.NewFile(uintptr(fds[1]), "parent"), os.NewFile(uintptr(fds[0]), "child"), nil }