Skip to content

Commit 68415b7

Browse files
committed
KEP-3619: Fine-grained SupplementalGroups control (SupplementalGroupsPolicy).
1 parent 88bb69d commit 68415b7

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
@@ -38,6 +38,7 @@ import (
3838

3939
. "github.com/onsi/ginkgo/v2"
4040
. "github.com/onsi/gomega"
41+
. "github.com/onsi/gomega/gstruct"
4142
)
4243

4344
const (
@@ -307,57 +308,6 @@ var _ = framework.KubeDescribe("Security Context", func() {
307308
Expect(groups).To(ContainElement("5678"))
308309
})
309310

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

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

0 commit comments

Comments
 (0)