Skip to content

Commit 0ca2c74

Browse files
ratasaschagrunert
authored andcommitted
userns: Fix running tests inside a userns
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]>
1 parent 259d2eb commit 0ca2c74

File tree

1 file changed

+41
-3
lines changed

1 file changed

+41
-3
lines changed

pkg/validate/security_context_linux.go

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"os"
2525
"os/exec"
2626
"path/filepath"
27+
"slices"
2728
"strings"
2829
"sync"
2930
"syscall"
@@ -936,13 +937,29 @@ var _ = framework.KubeDescribe("Security Context", func() {
936937
podID, podConfig = createNamespacePodSandbox(rc, namespaceOption, podName, podLogPath)
937938
containerName := runUserNamespaceContainer(rc, ic, podID, podConfig)
938939

939-
// 4294967295 means that the entire range is available
940+
// If this test is run inside a userns, we need to check the
941+
// container userns is the same as the one we see outside.
940942
expectedOutput := hostUsernsContent()
941943
if expectedOutput == "" {
942944
Fail("failed to get host userns content")
943945
}
944-
// 4294967295 means that the entire range is available
945-
matchContainerOutputRe(podConfig, containerName, `\s+0\s+0\s+4294967295\n`)
946+
// The userns mapping can have several lines, we match each of them.
947+
for _, line := range strings.Split(expectedOutput, "\n") {
948+
if line == "" {
949+
continue
950+
}
951+
mapping := parseUsernsMappingLine(line)
952+
if len(mapping) != 3 {
953+
msg := fmt.Sprintf("slice: %#v, len: %v", mapping, len(mapping))
954+
Fail("Unexpected host mapping line: " + msg)
955+
}
956+
957+
// The container outputs the content of its /proc/self/uid_map.
958+
// That output should match the regex of the host userns content.
959+
containerId, hostId, length := mapping[0], mapping[1], mapping[2]
960+
regex := fmt.Sprintf(`\s+%v\s+%v\s+%v`, containerId, hostId, length)
961+
matchContainerOutputRe(podConfig, containerName, regex)
962+
}
946963
})
947964

948965
It("runtime should fail if more than one mapping provided", func() {
@@ -1564,3 +1581,24 @@ func rootfsPath(info map[string]string) string {
15641581
// always exist.
15651582
return filepath.Join(cfg.StateDir, "../")
15661583
}
1584+
1585+
func hostUsernsContent() string {
1586+
uidMapPath := "/proc/self/uid_map"
1587+
uidMapContent, err := os.ReadFile(uidMapPath)
1588+
if err != nil {
1589+
return ""
1590+
}
1591+
return string(uidMapContent)
1592+
}
1593+
1594+
func parseUsernsMappingLine(line string) []string {
1595+
// The line format is:
1596+
// <container-id> <host-id> <length>
1597+
// But there could be a lot of spaces between the fields.
1598+
line = strings.TrimSpace(line)
1599+
m := strings.Split(line, " ")
1600+
m = slices.DeleteFunc(m, func(s string) bool {
1601+
return s == ""
1602+
})
1603+
return m
1604+
}

0 commit comments

Comments
 (0)