Skip to content

Commit 64a7db2

Browse files
committed
KEP-3619: Fine-grained SupplementalGroups control (SupplementalGroupsPolicy).
1 parent 29b04c4 commit 64a7db2

File tree

9 files changed

+854
-51
lines changed

9 files changed

+854
-51
lines changed

Diff for: pkg/validate/security_context_linux.go

+146-51
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232

3333
. "github.com/onsi/ginkgo/v2"
3434
. "github.com/onsi/gomega"
35+
. "github.com/onsi/gomega/gstruct"
3536
"golang.org/x/sys/unix"
3637
internalapi "k8s.io/cri-api/pkg/apis"
3738
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
@@ -306,57 +307,6 @@ var _ = framework.KubeDescribe("Security Context", func() {
306307
Expect(groups).To(ContainElement("5678"))
307308
})
308309

309-
It("if the container's primary UID belongs to some groups in the image, runtime should add SupplementalGroups to them", func() {
310-
By("create pod")
311-
podID, podConfig, podLogDir = createPodSandboxWithLogDirectory(rc)
312-
313-
By("create container for security context SupplementalGroups")
314-
supplementalGroup := int64(1234)
315-
containerName := "container-with-SupplementalGroups-and-predefined-group-image-test-" + framework.NewUUID()
316-
logPath := containerName + ".log"
317-
containerConfig := &runtimeapi.ContainerConfig{
318-
Metadata: framework.BuildContainerMetadata(containerName, framework.DefaultAttempt),
319-
Image: &runtimeapi.ImageSpec{Image: testImagePreDefinedGroup},
320-
Command: []string{"sh", "-c", "id -G; while :; do sleep 1; done"},
321-
Linux: &runtimeapi.LinuxContainerConfig{
322-
SecurityContext: &runtimeapi.LinuxContainerSecurityContext{
323-
RunAsUser: &runtimeapi.Int64Value{Value: imagePredefinedGroupUID},
324-
SupplementalGroups: []int64{supplementalGroup},
325-
},
326-
},
327-
LogPath: logPath,
328-
}
329-
containerID := framework.CreateContainer(rc, ic, containerConfig, podID, podConfig)
330-
331-
By("start container")
332-
startContainer(rc, containerID)
333-
Eventually(func(g Gomega) {
334-
g.Expect(getContainerStatus(rc, containerID).State).To(Equal(runtimeapi.ContainerState_CONTAINER_RUNNING))
335-
g.Expect(parseLogLine(podConfig, logPath)).NotTo(BeEmpty())
336-
}, time.Minute, time.Second*4).Should(Succeed())
337-
338-
// In testImagePreDefinedGroup,
339-
// - its default user is default-user(uid=1000)
340-
// - default-user belongs to group-defined-in-image(gid=50000)
341-
//
342-
// thus, supplementary group of the container processes should be
343-
// - 1000: self
344-
// - 1234: SupplementalGroups
345-
// - 50000: groups define in the container image
346-
//
347-
// $ id -G
348-
// 1000 1234 5678 50000
349-
expectedOutput := fmt.Sprintf("%d %d %d\n", imagePredefinedGroupUID, supplementalGroup, imagePredefinedGroupGID)
350-
351-
By("verify groups for the first process of the container")
352-
verifyLogContents(podConfig, logPath, expectedOutput, stdoutType)
353-
354-
By("verify groups for 'exec'-ed process of container")
355-
command := []string{"id", "-G"}
356-
o := execSyncContainer(rc, containerID, command)
357-
Expect(o).To(BeEquivalentTo(expectedOutput))
358-
})
359-
360310
It("runtime should support RunAsUser", func() {
361311
By("create pod")
362312
podID, podConfig = framework.CreatePodSandboxForContainer(rc)
@@ -639,6 +589,151 @@ var _ = framework.KubeDescribe("Security Context", func() {
639589
})
640590
})
641591

592+
Context("SupplementalGroupsPolicy", func() {
593+
BeforeEach(func(ctx context.Context) {
594+
By("skip if the runtime does not support SupplementalGroupsPolicy")
595+
statusResponse, err := rc.Status(ctx, false)
596+
Expect(err).NotTo(HaveOccurred())
597+
if !(statusResponse.Features != nil && statusResponse.Features.SupplementalGroupsPolicy) {
598+
Skip("The runtime does not support SupplementalGroupsPolicy feature")
599+
}
600+
})
601+
602+
When("SupplementalGroupsPolicy=Merge (Default)", func() {
603+
It("if the container's primary UID belongs to some groups in the image, runtime should add SupplementalGroups to them", func() {
604+
By("create pod")
605+
podID, podConfig, podLogDir = createPodSandboxWithLogDirectory(rc)
606+
607+
By("create container for security context SupplementalGroups")
608+
supplementalGroup := int64(1234)
609+
containerName := "container-with-SupplementalGroupsPolicyMerge-" + framework.NewUUID()
610+
logPath := containerName + ".log"
611+
containerConfig := &runtimeapi.ContainerConfig{
612+
Metadata: framework.BuildContainerMetadata(containerName, framework.DefaultAttempt),
613+
Image: &runtimeapi.ImageSpec{Image: testImagePreDefinedGroup},
614+
Command: []string{"sh", "-c", "id -G; while :; do sleep 1; done"},
615+
Linux: &runtimeapi.LinuxContainerConfig{
616+
SecurityContext: &runtimeapi.LinuxContainerSecurityContext{
617+
RunAsUser: &runtimeapi.Int64Value{Value: imagePredefinedGroupUID},
618+
SupplementalGroups: []int64{supplementalGroup},
619+
// SupplementalGroupsPolicy_Merge is default(0)
620+
// SupplementalGroupsPolicy: runtimeapi.SupplementalGroupsPolicy_Merge,
621+
},
622+
},
623+
LogPath: logPath,
624+
}
625+
containerID := framework.CreateContainer(rc, ic, containerConfig, podID, podConfig)
626+
627+
By("start container")
628+
startContainer(rc, containerID)
629+
630+
Eventually(func(g Gomega) {
631+
containerStatus := getContainerStatus(rc, containerID)
632+
g.Expect(containerStatus.State).To(Equal(runtimeapi.ContainerState_CONTAINER_RUNNING))
633+
// In testImagePreDefinedGroup,
634+
// - its default user is default-user(uid=1000)
635+
// - default-user belongs to group-defined-in-image(gid=50000) in /etc/group
636+
// And, SupplementalGroupsPolicy is Merge(default)
637+
//
638+
// Thus, firstly attached process identity of the first container processes should be
639+
// - uid: 1000 (RunAsUser)
640+
// - gid: 1000 (default group for uid=1000)
641+
// - supplementary groups
642+
// - 1000: self
643+
// - 1234: SupplementalGroups
644+
// - 50000: groups defined in the container image (/etc/group)
645+
g.Expect(containerStatus.User).To(PointTo(MatchFields(IgnoreExtras, Fields{
646+
"Linux": PointTo(MatchFields(IgnoreExtras, Fields{
647+
"Uid": Equal(imagePredefinedGroupUID),
648+
"Gid": Equal(imagePredefinedGroupUID),
649+
// we can not assume the order of gids
650+
"SupplementalGroups": And(ContainElements(imagePredefinedGroupUID, supplementalGroup, imagePredefinedGroupGID), HaveLen(3)),
651+
})),
652+
})))
653+
g.Expect(parseLogLine(podConfig, logPath)).NotTo(BeEmpty())
654+
}, time.Minute, time.Second*4).Should(Succeed())
655+
656+
// $ id -G
657+
// 1000 1234 50000
658+
expectedOutput := fmt.Sprintf("%d %d %d\n", imagePredefinedGroupUID, supplementalGroup, imagePredefinedGroupGID)
659+
660+
By("verify groups for the first process of the container")
661+
verifyLogContents(podConfig, logPath, expectedOutput, stdoutType)
662+
663+
By("verify groups for 'exec'-ed process of container")
664+
command := []string{"id", "-G"}
665+
o := execSyncContainer(rc, containerID, command)
666+
Expect(o).To(BeEquivalentTo(expectedOutput))
667+
})
668+
})
669+
When("SupplementalGroupsPolicy=Strict", func() {
670+
It("even if the container's primary UID belongs to some groups in the image, runtime should not add SupplementalGroups to them", func() {
671+
By("create pod")
672+
podID, podConfig, podLogDir = createPodSandboxWithLogDirectory(rc)
673+
674+
By("create container for security context SupplementalGroups")
675+
supplementalGroup := int64(1234)
676+
containerName := "container-with-SupplementalGroupsPolicyMerge-" + framework.NewUUID()
677+
logPath := containerName + ".log"
678+
containerConfig := &runtimeapi.ContainerConfig{
679+
Metadata: framework.BuildContainerMetadata(containerName, framework.DefaultAttempt),
680+
Image: &runtimeapi.ImageSpec{Image: testImagePreDefinedGroup},
681+
Command: []string{"sh", "-c", "id -G; while :; do sleep 1; done"},
682+
Linux: &runtimeapi.LinuxContainerConfig{
683+
SecurityContext: &runtimeapi.LinuxContainerSecurityContext{
684+
RunAsUser: &runtimeapi.Int64Value{Value: imagePredefinedGroupUID},
685+
SupplementalGroups: []int64{supplementalGroup},
686+
SupplementalGroupsPolicy: runtimeapi.SupplementalGroupsPolicy_Strict,
687+
},
688+
},
689+
LogPath: logPath,
690+
}
691+
containerID := framework.CreateContainer(rc, ic, containerConfig, podID, podConfig)
692+
693+
By("start container")
694+
startContainer(rc, containerID)
695+
696+
Eventually(func(g Gomega) {
697+
containerStatus := getContainerStatus(rc, containerID)
698+
g.Expect(containerStatus.State).To(Equal(runtimeapi.ContainerState_CONTAINER_RUNNING))
699+
// In testImagePreDefinedGroup,
700+
// - its default user is default-user(uid=1000)
701+
// - default-user belongs to group-defined-in-image(gid=50000) in /etc/group
702+
// And, SupplementalGroupsPolicy is Strict
703+
//
704+
// Thus, firstly attached process identity of the first container processes should be
705+
// (5000(defined in /etc/group) is not appended to supplementary groups)
706+
// - uid: 1000 (RunAsUser)
707+
// - gid: 1000 (default group for uid=1000)
708+
// - supplementary groups
709+
// - 1000: self
710+
// - 1234: SupplementalGroups
711+
g.Expect(containerStatus.User).To(PointTo(MatchFields(IgnoreExtras, Fields{
712+
"Linux": PointTo(MatchFields(IgnoreExtras, Fields{
713+
"Uid": Equal(imagePredefinedGroupUID),
714+
"Gid": Equal(imagePredefinedGroupUID),
715+
// we can not assume the order of gids
716+
"SupplementalGroups": And(ContainElements(imagePredefinedGroupUID, supplementalGroup), HaveLen(2)),
717+
})),
718+
})))
719+
g.Expect(parseLogLine(podConfig, logPath)).NotTo(BeEmpty())
720+
}, time.Minute, time.Second*4).Should(Succeed())
721+
722+
// $ id -G
723+
// 1000 1234
724+
expectedOutput := fmt.Sprintf("%d %d\n", imagePredefinedGroupUID, supplementalGroup)
725+
726+
By("verify groups for the first process of the container")
727+
verifyLogContents(podConfig, logPath, expectedOutput, stdoutType)
728+
729+
By("verify groups for 'exec'-ed process of container")
730+
command := []string{"id", "-G"}
731+
o := execSyncContainer(rc, containerID, command)
732+
Expect(o).To(BeEquivalentTo(expectedOutput))
733+
})
734+
})
735+
})
736+
642737
// TODO(random-liu): We should set apparmor to unconfined in seccomp test to prevent
643738
// them from interfering with each other.
644739
Context("SeccompProfilePath", func() {

0 commit comments

Comments
 (0)