Skip to content

Commit 3a1f73d

Browse files
authored
Merge pull request #1354 from saschagrunert/user-namespaces-tests
Add user namespaces tests
2 parents 3542da5 + 4d1e304 commit 3a1f73d

File tree

4 files changed

+234
-10
lines changed

4 files changed

+234
-10
lines changed

.github/workflows/containerd.yml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -298,27 +298,28 @@ jobs:
298298
set -o errexit
299299
set -o nounset
300300
set -o pipefail
301+
set -x
301302
302-
BDIR="$(mktemp -d -p $PWD)"
303+
BDIR="/var/lib/containerd-critest"
303304
echo "containerd temp dir: ${BDIR}"
304-
mkdir -p ${BDIR}/{root,state}
305+
sudo mkdir -p ${BDIR}/{root,state}
305306
306-
cat > ${BDIR}/config.toml <<EOF
307+
sudo bash -c 'cat > ${BDIR}/config.toml <<EOF
307308
version = 2
308309
[plugins]
309310
[plugins.cri.containerd.default_runtime]
310311
runtime_type = \"${{matrix.runtime}}\"
311-
EOF
312+
EOF'
312313
313314
# Remove possibly existing containerd configuration
314315
sudo rm -rf /etc/containerd
315-
316-
sudo PATH=$PATH /usr/local/bin/containerd -a ${BDIR}/c.sock -root ${BDIR}/root -state ${BDIR}/state -log-level debug &> ${BDIR}/containerd-cri.log &
316+
sudo PATH=$PATH bash -c "/usr/local/bin/containerd -a ${BDIR}/c.sock -root ${BDIR}/root -state ${BDIR}/state -log-level debug &> ${BDIR}/containerd-cri.log &"
317317
sudo /usr/local/bin/ctr -a ${BDIR}/c.sock version
318318
sudo /usr/local/sbin/runc --version
319+
sudo mount
319320
320-
sudo -E PATH=$PATH critest --runtime-endpoint=unix:///${BDIR}/c.sock --parallel=8
321-
TEST_RC=$?
321+
TEST_RC=0
322+
sudo -E PATH=$PATH critest --ginkgo.vv --runtime-endpoint=unix:///${BDIR}/c.sock --parallel=8 || TEST_RC=$?
322323
test $TEST_RC -ne 0 && cat ${BDIR}/containerd-cri.log
323324
sudo pkill containerd
324325
echo "CONTD_CRI_DIR=$BDIR" >> $GITHUB_ENV

pkg/framework/util.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,20 @@ func RunPodSandbox(c internalapi.RuntimeService, config *runtimeapi.PodSandboxCo
220220
return podID
221221
}
222222

223+
// RunPodSandboxWithRuntimeHandler runs a PodSandbox with a custom runtime handler.
224+
func RunPodSandboxWithRuntimeHandler(c internalapi.RuntimeService, config *runtimeapi.PodSandboxConfig, runtimeHandler string) string {
225+
podID, err := c.RunPodSandbox(context.TODO(), config, runtimeHandler)
226+
ExpectNoError(err, "failed to create PodSandbox: %v", err)
227+
return podID
228+
}
229+
230+
// RunPodSandboxErrorWithRuntimeHandler runs a PodSandbox with a custom runtime handler and expects an error.
231+
func RunPodSandboxErrorWithRuntimeHandler(c internalapi.RuntimeService, config *runtimeapi.PodSandboxConfig, runtimeHandler string) string {
232+
podID, err := c.RunPodSandbox(context.TODO(), config, runtimeHandler)
233+
Expect(err).To(HaveOccurred())
234+
return podID
235+
}
236+
223237
// CreatePodSandboxForContainer creates a PodSandbox for creating containers.
224238
func CreatePodSandboxForContainer(c internalapi.RuntimeService) (string, *runtimeapi.PodSandboxConfig) {
225239
podSandboxName := "create-PodSandbox-for-container-" + NewUUID()

pkg/validate/container.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"fmt"
2424
"os"
2525
"path/filepath"
26+
"regexp"
2627
"strings"
2728
"time"
2829

@@ -629,6 +630,21 @@ func verifyLogContents(podConfig *runtimeapi.PodSandboxConfig, logPath string, l
629630
Expect(found).To(BeTrue(), "expected log %q (stream=%q) not found in logs %+v", log, stream, msgs)
630631
}
631632

633+
// verifyLogContentsRe verifies the contents of container log using the provided regular expression pattern.
634+
func verifyLogContentsRe(podConfig *runtimeapi.PodSandboxConfig, logPath string, pattern string, stream streamType) {
635+
By("verify log contents using regex pattern")
636+
msgs := parseLogLine(podConfig, logPath)
637+
638+
found := false
639+
for _, msg := range msgs {
640+
if matched, _ := regexp.MatchString(pattern, msg.log); matched && msg.stream == stream {
641+
found = true
642+
break
643+
}
644+
}
645+
Expect(found).To(BeTrue(), "expected log pattern %q (stream=%q) to match logs %+v", pattern, stream, msgs)
646+
}
647+
632648
// listContainerStatsForID lists container for containerID.
633649
func listContainerStatsForID(c internalapi.RuntimeService, containerID string) *runtimeapi.ContainerStats {
634650
By("List container stats for containerID: " + containerID)

pkg/validate/security_context_linux.go

Lines changed: 195 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,125 @@ var _ = framework.KubeDescribe("Security Context", func() {
842842
matchContainerOutput(podConfig, containerName, "Effective uid: 0\n")
843843
})
844844
})
845+
846+
Context("UserNamespaces", func() {
847+
var (
848+
podName string
849+
runtimeHandler string
850+
defaultMapping = []*runtimeapi.IDMapping{{
851+
ContainerId: 0,
852+
HostId: 1000,
853+
Length: 100000,
854+
}}
855+
)
856+
857+
BeforeEach(func() {
858+
podName = "user-namespaces-pod-" + framework.NewUUID()
859+
860+
// Find a working runtime handler if none provided
861+
if framework.TestContext.RuntimeHandler == "" {
862+
By("searching for runtime handler which supports user namespaces")
863+
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
864+
defer cancel()
865+
resp, err := rc.Status(ctx, false)
866+
framework.ExpectNoError(err, "failed to get runtime config: %v", err)
867+
868+
for _, rh := range resp.GetRuntimeHandlers() {
869+
if rh.GetFeatures().GetUserNamespaces() {
870+
runtimeHandler = rh.GetName()
871+
break
872+
}
873+
}
874+
} else {
875+
runtimeHandler = framework.TestContext.RuntimeHandler
876+
}
877+
878+
if runtimeHandler == "" {
879+
Skip("no runtime handler found which supports user namespaces")
880+
}
881+
882+
By(fmt.Sprintf("using runtime handler: %s", runtimeHandler))
883+
})
884+
885+
It("runtime should support NamespaceMode_POD", func() {
886+
namespaceOption := &runtimeapi.NamespaceOption{
887+
UsernsOptions: &runtimeapi.UserNamespace{
888+
Mode: runtimeapi.NamespaceMode_POD,
889+
Uids: defaultMapping,
890+
Gids: defaultMapping,
891+
},
892+
}
893+
894+
hostLogPath, podLogPath := createLogTempDir(podName)
895+
defer os.RemoveAll(hostLogPath)
896+
podID, podConfig = createNamespacePodSandboxWithRuntimeHandler(rc, namespaceOption, podName, podLogPath, runtimeHandler)
897+
containerName := runUserNamespaceContainer(rc, ic, podID, podConfig)
898+
899+
matchContainerOutputRe(podConfig, containerName, `\s+0\s+1000\s+100000\n`)
900+
})
901+
902+
It("runtime should support NamespaceMode_NODE", func() {
903+
namespaceOption := &runtimeapi.NamespaceOption{
904+
UsernsOptions: &runtimeapi.UserNamespace{
905+
Mode: runtimeapi.NamespaceMode_NODE,
906+
},
907+
}
908+
909+
hostLogPath, podLogPath := createLogTempDir(podName)
910+
defer os.RemoveAll(hostLogPath)
911+
podID, podConfig = createNamespacePodSandboxWithRuntimeHandler(rc, namespaceOption, podName, podLogPath, runtimeHandler)
912+
containerName := runUserNamespaceContainer(rc, ic, podID, podConfig)
913+
914+
// 4294967295 means that the entire range is available
915+
matchContainerOutputRe(podConfig, containerName, `\s+0\s+0\s+4294967295\n`)
916+
})
917+
918+
It("runtime should fail if more than one mapping provided", func() {
919+
wrongMapping := []*runtimeapi.IDMapping{{
920+
ContainerId: 0,
921+
HostId: 1000,
922+
Length: 100000,
923+
}, {
924+
ContainerId: 0,
925+
HostId: 2000,
926+
Length: 100000,
927+
}}
928+
usernsOptions := &runtimeapi.UserNamespace{
929+
Mode: runtimeapi.NamespaceMode_POD,
930+
Uids: wrongMapping,
931+
Gids: wrongMapping,
932+
}
933+
934+
runUserNamespacePodWithError(rc, podName, usernsOptions, runtimeHandler)
935+
})
936+
937+
It("runtime should fail if container ID 0 is not mapped", func() {
938+
mapping := []*runtimeapi.IDMapping{{
939+
ContainerId: 1,
940+
HostId: 1000,
941+
Length: 100000,
942+
}}
943+
usernsOptions := &runtimeapi.UserNamespace{
944+
Mode: runtimeapi.NamespaceMode_POD,
945+
Uids: mapping,
946+
Gids: mapping,
947+
}
948+
949+
runUserNamespacePodWithError(rc, podName, usernsOptions, runtimeHandler)
950+
})
951+
952+
It("runtime should fail with NamespaceMode_CONTAINER", func() {
953+
usernsOptions := &runtimeapi.UserNamespace{Mode: runtimeapi.NamespaceMode_CONTAINER}
954+
955+
runUserNamespacePodWithError(rc, podName, usernsOptions, runtimeHandler)
956+
})
957+
958+
It("runtime should fail with NamespaceMode_TARGET", func() {
959+
usernsOptions := &runtimeapi.UserNamespace{Mode: runtimeapi.NamespaceMode_TARGET}
960+
961+
runUserNamespacePodWithError(rc, podName, usernsOptions, runtimeHandler)
962+
})
963+
})
845964
})
846965

847966
// matchContainerOutput matches log line in container logs.
@@ -850,6 +969,12 @@ func matchContainerOutput(podConfig *runtimeapi.PodSandboxConfig, name, output s
850969
verifyLogContents(podConfig, fmt.Sprintf("%s.log", name), output, stdoutType)
851970
}
852971

972+
// matchContainerOutputRe matches log line in container logs using the provided regular expression pattern.
973+
func matchContainerOutputRe(podConfig *runtimeapi.PodSandboxConfig, name, pattern string) {
974+
By("check container output")
975+
verifyLogContentsRe(podConfig, fmt.Sprintf("%s.log", name), pattern, stdoutType)
976+
}
977+
853978
// createRunAsUserContainer creates the container with specified RunAsUser in ContainerConfig.
854979
func createRunAsUserContainer(rc internalapi.RuntimeService, ic internalapi.ImageManagerService, podID string, podConfig *runtimeapi.PodSandboxConfig, prefix string) (string, string) {
855980
By("create RunAsUser container")
@@ -942,11 +1067,23 @@ func createInvalidRunAsGroupContainer(rc internalapi.RuntimeService, ic internal
9421067

9431068
// createNamespacePodSandbox creates a PodSandbox with different NamespaceOption config for creating containers.
9441069
func createNamespacePodSandbox(rc internalapi.RuntimeService, podSandboxNamespace *runtimeapi.NamespaceOption, podSandboxName string, podLogPath string) (string, *runtimeapi.PodSandboxConfig) {
1070+
return createNamespacePodSandboxWithRuntimeHandler(rc, podSandboxNamespace, podSandboxName, podLogPath, framework.TestContext.RuntimeHandler)
1071+
}
1072+
1073+
// createNamespacePodSandboxWithRuntimeHandler creates a PodSandbox with
1074+
// different NamespaceOption config for creating containers by using a custom
1075+
// runtime handler.
1076+
func createNamespacePodSandboxWithRuntimeHandler(
1077+
rc internalapi.RuntimeService,
1078+
podSandboxNamespace *runtimeapi.NamespaceOption,
1079+
podSandboxName, podLogPath, runtimeHandler string,
1080+
) (string, *runtimeapi.PodSandboxConfig) {
9451081
By("create NamespaceOption podSandbox")
9461082
uid := framework.DefaultUIDPrefix + framework.NewUUID()
9471083
namespace := framework.DefaultNamespacePrefix + framework.NewUUID()
9481084
config := &runtimeapi.PodSandboxConfig{
949-
Metadata: framework.BuildPodSandboxMetadata(podSandboxName, uid, namespace, framework.DefaultAttempt),
1085+
Metadata: framework.BuildPodSandboxMetadata(podSandboxName, uid, namespace, framework.DefaultAttempt),
1086+
DnsConfig: &runtimeapi.DNSConfig{},
9501087
Linux: &runtimeapi.LinuxPodSandboxConfig{
9511088
SecurityContext: &runtimeapi.LinuxSandboxSecurityContext{
9521089
NamespaceOptions: podSandboxNamespace,
@@ -957,7 +1094,7 @@ func createNamespacePodSandbox(rc internalapi.RuntimeService, podSandboxNamespac
9571094
Labels: framework.DefaultPodLabels,
9581095
}
9591096

960-
return framework.RunPodSandbox(rc, config), config
1097+
return framework.RunPodSandboxWithRuntimeHandler(rc, config, runtimeHandler), config
9611098
}
9621099

9631100
// createNamespaceContainer creates container with different NamespaceOption config.
@@ -1281,3 +1418,59 @@ func checkSetHostname(rc internalapi.RuntimeService, containerID string, setable
12811418
Expect(err).To(HaveOccurred(), msg)
12821419
}
12831420
}
1421+
1422+
func runUserNamespaceContainer(
1423+
rc internalapi.RuntimeService,
1424+
ic internalapi.ImageManagerService,
1425+
podID string,
1426+
podConfig *runtimeapi.PodSandboxConfig,
1427+
) string {
1428+
By("create user namespaces container")
1429+
containerName := "user-namespaces-container-" + framework.NewUUID()
1430+
containerConfig := &runtimeapi.ContainerConfig{
1431+
Metadata: framework.BuildContainerMetadata(containerName, framework.DefaultAttempt),
1432+
Image: &runtimeapi.ImageSpec{
1433+
Image: framework.TestContext.TestImageList.DefaultTestContainerImage,
1434+
UserSpecifiedImage: framework.TestContext.TestImageList.DefaultTestContainerImage,
1435+
},
1436+
Command: []string{"cat", "/proc/self/uid_map"},
1437+
LogPath: fmt.Sprintf("%s.log", containerName),
1438+
Linux: &runtimeapi.LinuxContainerConfig{
1439+
SecurityContext: &runtimeapi.LinuxContainerSecurityContext{
1440+
NamespaceOptions: podConfig.Linux.SecurityContext.NamespaceOptions,
1441+
},
1442+
},
1443+
}
1444+
1445+
containerID := createContainerWithExpectation(rc, ic, containerConfig, podID, podConfig, true)
1446+
startContainer(rc, containerID)
1447+
1448+
Eventually(func() runtimeapi.ContainerState {
1449+
return getContainerStatus(rc, containerID).State
1450+
}, time.Minute, time.Second*4).Should(Equal(runtimeapi.ContainerState_CONTAINER_EXITED))
1451+
1452+
return containerName
1453+
}
1454+
1455+
func runUserNamespacePodWithError(
1456+
rc internalapi.RuntimeService,
1457+
podName string,
1458+
usernsOptions *runtimeapi.UserNamespace,
1459+
runtimeHandler string,
1460+
) {
1461+
uid := framework.DefaultUIDPrefix + framework.NewUUID()
1462+
namespace := framework.DefaultNamespacePrefix + framework.NewUUID()
1463+
config := &runtimeapi.PodSandboxConfig{
1464+
Metadata: framework.BuildPodSandboxMetadata(podName, uid, namespace, framework.DefaultAttempt),
1465+
Linux: &runtimeapi.LinuxPodSandboxConfig{
1466+
SecurityContext: &runtimeapi.LinuxSandboxSecurityContext{
1467+
NamespaceOptions: &runtimeapi.NamespaceOption{
1468+
UsernsOptions: usernsOptions,
1469+
},
1470+
},
1471+
},
1472+
Labels: framework.DefaultPodLabels,
1473+
}
1474+
1475+
framework.RunPodSandboxErrorWithRuntimeHandler(rc, config, runtimeHandler)
1476+
}

0 commit comments

Comments
 (0)