Skip to content

Commit 3b082e9

Browse files
committed
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 <rodrigoca@microsoft.com>
1 parent 316d6d3 commit 3b082e9

File tree

1 file changed

+41
-3
lines changed

1 file changed

+41
-3
lines changed

pkg/validate/security_context_linux.go

+41-3
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 uid_map file should match host (separated with
958+
// spaces).
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)