Skip to content

Commit ee58080

Browse files
authored
Merge pull request #1438 from everpeace/kep-3619-SupplementalGroupsPolicy
KEP-3619: Fine-grained SupplementalGroups control
2 parents ac17e9b + c9e3de6 commit ee58080

File tree

6 files changed

+887
-538
lines changed

6 files changed

+887
-538
lines changed

Diff for: go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ require (
3131
k8s.io/api v0.31.0-beta.0
3232
k8s.io/apimachinery v0.31.0-beta.0
3333
k8s.io/client-go v0.31.0-beta.0
34-
k8s.io/cri-api v0.31.0-beta.0
34+
k8s.io/cri-api v0.31.0-beta.0.0.20240716205706-865479a3e1b3
3535
k8s.io/cri-client v0.31.0-beta.0
3636
k8s.io/klog/v2 v2.130.1
3737
k8s.io/kubectl v0.30.3

Diff for: go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,8 @@ k8s.io/client-go v0.31.0-beta.0 h1:op39m8L2YX8cUVTj8Fj+SD09axcxwPbFlipM3x+11fc=
238238
k8s.io/client-go v0.31.0-beta.0/go.mod h1:ZTCtLpZyZDJBji9GGwrzKjR60/lVXvm8WLdgTEqPRw4=
239239
k8s.io/component-base v0.31.0-beta.0 h1:Pw4OEeykCxfs8fTCslHEWggbNe/24W4TYVIn1VEdkjE=
240240
k8s.io/component-base v0.31.0-beta.0/go.mod h1:mjY7SWAP/NhFiwUkdT+gzhGDI/cBqB/Pi5tF443aikE=
241-
k8s.io/cri-api v0.31.0-beta.0 h1:U+E5RaGMtR1TLsJvaxb0RCVRU9ZcDPrC5EiCxb1uN1s=
242-
k8s.io/cri-api v0.31.0-beta.0/go.mod h1:Po3TMAYH/+KrZabi7QiwQI4a692oZcUOUThd/rqwxrI=
241+
k8s.io/cri-api v0.31.0-beta.0.0.20240716205706-865479a3e1b3 h1:Ajq0xvhs7HDrZVLgqUenylY8i4BkaazJzfeM+fh63AU=
242+
k8s.io/cri-api v0.31.0-beta.0.0.20240716205706-865479a3e1b3/go.mod h1:Po3TMAYH/+KrZabi7QiwQI4a692oZcUOUThd/rqwxrI=
243243
k8s.io/cri-client v0.31.0-beta.0 h1:VIq3WTdpshrNkbM/zXmOedEValrOsSseqrLRXeK/s/k=
244244
k8s.io/cri-client v0.31.0-beta.0/go.mod h1:qtduCKkjfx6kUepnUL6vyROfA43bVpmnIpD4pSw4JZo=
245245
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=

Diff for: pkg/validate/security_context_linux.go

+147-51
Original file line numberDiff line numberDiff line change
@@ -306,57 +306,6 @@ var _ = framework.KubeDescribe("Security Context", func() {
306306
Expect(groups).To(ContainElement("5678"))
307307
})
308308

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-
360309
It("runtime should support RunAsUser", func() {
361310
By("create pod")
362311
podID, podConfig = framework.CreatePodSandboxForContainer(rc)
@@ -639,6 +588,153 @@ var _ = framework.KubeDescribe("Security Context", func() {
639588
})
640589
})
641590

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

0 commit comments

Comments
 (0)