From d4961abdd827cc0ac35147cf033b48b0df3a8db3 Mon Sep 17 00:00:00 2001 From: Dongsu Park Date: Tue, 15 May 2018 12:55:08 +0200 Subject: [PATCH] validation: add a new test for NSPathMatchTypeError `checkNSPathMatchType` checks if the container returns an error when deliberately setting a wrong namespace type. Doing that, it is possible to verify `NSPathMatchTypeError`, i.e. `The runtime MUST generate an error if path is not associated with a namespace of type "type"`. See also https://github.com/opencontainers/runtime-tools/issues/572 Signed-off-by: Dongsu Park --- validation/linux_ns_path_type.go | 122 +++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 validation/linux_ns_path_type.go diff --git a/validation/linux_ns_path_type.go b/validation/linux_ns_path_type.go new file mode 100644 index 000000000..e06fc7ced --- /dev/null +++ b/validation/linux_ns_path_type.go @@ -0,0 +1,122 @@ +package main + +import ( + "fmt" + "os/exec" + "runtime" + "syscall" + + "github.com/mndrix/tap-go" + rspec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/runtime-tools/specerror" + "github.com/opencontainers/runtime-tools/validation/util" +) + +func getRuntimeToolsNamespace(ns string) string { + // Deal with exceptional cases of "net" and "mnt", because those strings + // cannot be recognized by mapStrToNamespace(), which actually expects + // "network" and "mount" respectively. + switch ns { + case "net": + return "network" + case "mnt": + return "mount" + } + + // In other cases, return just the original string + return ns +} + +func checkNSPathMatchType(t *tap.T, ns, wrongNs string) error { + // Deliberately set ns path with a wrong namespace, to check if the runtime + // returns error when running with the wrong namespace path. + unshareNsPath := fmt.Sprintf("/proc/self/ns/%s", wrongNs) + + g, err := util.GetDefaultGenerator() + if err != nil { + return fmt.Errorf("cannot get default config from generator: %v", err) + } + + rtns := getRuntimeToolsNamespace(ns) + g.AddOrReplaceLinuxNamespace(rtns, unshareNsPath) + + err = util.RuntimeOutsideValidate(g, func(config *rspec.Spec, state *rspec.State) error { + return nil + }) + + t.Ok(err != nil, fmt.Sprintf("got error when setting a wrong namespace path %q with type %s", unshareNsPath, rtns)) + if err == nil { + rfcError, errRfc := specerror.NewRFCError(specerror.NSPathMatchTypeError, + fmt.Errorf("got no error when setting a wrong namespace path %q with type %s", unshareNsPath, rtns), + rspec.Version) + if errRfc != nil { + return fmt.Errorf("cannot get new rfcError: %v", errRfc) + } + diagnostic := map[string]string{ + "expected": fmt.Sprintf("err == %v", err), + "actual": "err == nil", + "namespace type": rtns, + "level": rfcError.Level.String(), + "reference": rfcError.Reference, + } + t.YAML(diagnostic) + + return fmt.Errorf("cannot validate path with wrong type") + } + + return nil +} + +func testNSPathMatchType(t *tap.T, ns, unshareOpt, wrongNs string) error { + // Calling 'unshare' (part of util-linux) is easier than doing it from + // Golang: mnt namespaces cannot be unshared from multithreaded + // programs. + cmd := exec.Command("unshare", unshareOpt, "--fork", "sleep", "10000") + cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} + err := cmd.Start() + if err != nil { + return fmt.Errorf("cannot run unshare: %s", err) + } + defer func() { + if cmd.Process != nil { + cmd.Process.Kill() + } + cmd.Wait() + syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) + }() + if cmd.Process == nil { + return fmt.Errorf("process failed to start") + } + + return checkNSPathMatchType(t, ns, wrongNs) +} + +func main() { + t := tap.New() + t.Header(0) + + cases := []struct { + name string + unshareOpt string + wrongname string + }{ + {"cgroup", "--cgroup", "ipc"}, + {"ipc", "--ipc", "mnt"}, + {"mnt", "--mount", "net"}, + {"net", "--net", "pid"}, + {"pid", "--pid", "user"}, + {"user", "--user", "uts"}, + {"uts", "--uts", "cgroup"}, + } + + for _, c := range cases { + if "linux" != runtime.GOOS { + t.Skip(1, fmt.Sprintf("linux-specific namespace test: %s", c)) + } + + err := testNSPathMatchType(t, c.name, c.unshareOpt, c.wrongname) + t.Ok(err == nil, fmt.Sprintf("namespace path matches with type %s", c.name)) + } + + t.AutoPlan() +}