Skip to content

Commit

Permalink
userns: Fix running tests inside a userns
Browse files Browse the repository at this point in the history
containerd creates a userns and inside there, it runs the critest tool.
However, in that setup, the length of containerd's userns is not the
whole UID space.

Let's verify that the length of the userns inside the pod, when we
created it with NamespaceMode_NODE (IOW, when not using a new userns for
the pod) is the same as outside the pod.

This works fine when contained itself runs inside a userns.

Signed-off-by: Rodrigo Campos <[email protected]>
  • Loading branch information
rata committed Jul 9, 2024
1 parent 316d6d3 commit c81525d
Showing 1 changed file with 41 additions and 3 deletions.
44 changes: 41 additions & 3 deletions pkg/validate/security_context_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"os"
"os/exec"
"path/filepath"
"slices"
"strings"
"sync"
"syscall"
Expand Down Expand Up @@ -936,13 +937,29 @@ var _ = framework.KubeDescribe("Security Context", func() {
podID, podConfig = createNamespacePodSandbox(rc, namespaceOption, podName, podLogPath)
containerName := runUserNamespaceContainer(rc, ic, podID, podConfig)

// 4294967295 means that the entire range is available
// If this test is run inside a userns, we need to check the
// container userns is the same as the one we see outside.
expectedOutput := hostUsernsContent()
if expectedOutput == "" {
Fail("failed to get host userns content")
}
// 4294967295 means that the entire range is available
matchContainerOutputRe(podConfig, containerName, `\s+0\s+0\s+4294967295\n`)
// The userns mapping can have several lines, we match each of them.
for _, line := range strings.Split(expectedOutput, "\n") {
if line == "" {
continue
}
mapping := parseUsernsMappingLine(line)
if len(mapping) != 3 {
msg := fmt.Sprintf("slice: %#v, len: %v", mapping, len(mapping))
Fail("Unexpected host mapping line: " + msg)
}

// The container outputs the content of its /proc/self/uid_map.
// That output should match the regex of the host userns content.
containerId, hostId, length := mapping[0], mapping[1], mapping[2]
regex := fmt.Sprintf(`\s+%v\s+%v\s+%v`, containerId, hostId, length)
matchContainerOutputRe(podConfig, containerName, regex)
}
})

It("runtime should fail if more than one mapping provided", func() {
Expand Down Expand Up @@ -1564,3 +1581,24 @@ func rootfsPath(info map[string]string) string {
// always exist.
return filepath.Join(cfg.StateDir, "../")
}

func hostUsernsContent() string {
uidMapPath := "/proc/self/uid_map"
uidMapContent, err := os.ReadFile(uidMapPath)
if err != nil {
return ""
}
return string(uidMapContent)
}

func parseUsernsMappingLine(line string) []string {
// The line format is:
// <container-id> <host-id> <length>
// But there could be a lot of spaces between the fields.
line = strings.TrimSpace(line)
m := strings.Split(line, " ")
m = slices.DeleteFunc(m, func(s string) bool {
return s == ""
})
return m
}

0 comments on commit c81525d

Please sign in to comment.