Skip to content

Commit

Permalink
KEP-3619: Fine-grained SupplementalGroups control (SupplementalGroups…
Browse files Browse the repository at this point in the history
…Policy).
  • Loading branch information
everpeace committed May 30, 2024
1 parent ec712f2 commit 5aac837
Showing 1 changed file with 134 additions and 51 deletions.
185 changes: 134 additions & 51 deletions pkg/validate/security_context_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,57 +300,6 @@ var _ = framework.KubeDescribe("Security Context", func() {
Expect(groups).To(ContainElement("5678"))
})

It("if the container's primary UID belongs to some groups in the image, runtime should add SupplementalGroups to them", func() {
By("create pod")
podID, podConfig, podLogDir = createPodSandboxWithLogDirectory(rc)

By("create container for security context SupplementalGroups")
supplementalGroup := int64(1234)
containerName := "container-with-SupplementalGroups-and-predefined-group-image-test-" + framework.NewUUID()
logPath := containerName + ".log"
containerConfig := &runtimeapi.ContainerConfig{
Metadata: framework.BuildContainerMetadata(containerName, framework.DefaultAttempt),
Image: &runtimeapi.ImageSpec{Image: testImagePreDefinedGroup},
Command: []string{"sh", "-c", "id -G; while :; do sleep 1; done"},
Linux: &runtimeapi.LinuxContainerConfig{
SecurityContext: &runtimeapi.LinuxContainerSecurityContext{
RunAsUser: &runtimeapi.Int64Value{Value: imagePredefinedGroupUID},
SupplementalGroups: []int64{supplementalGroup},
},
},
LogPath: logPath,
}
containerID := framework.CreateContainer(rc, ic, containerConfig, podID, podConfig)

By("start container")
startContainer(rc, containerID)
Eventually(func(g Gomega) {
g.Expect(getContainerStatus(rc, containerID).State).To(Equal(runtimeapi.ContainerState_CONTAINER_RUNNING))
g.Expect(parseLogLine(podConfig, logPath)).NotTo(BeEmpty())
}, time.Minute, time.Second*4).Should(Succeed())

// In testImagePreDefinedGroup,
// - its default user is default-user(uid=1000)
// - default-user belongs to group-defined-in-image(gid=50000)
//
// thus, supplementary group of the container processes should be
// - 1000: self
// - 1234: SupplementalGroups
// - 50000: groups define in the container image
//
// $ id -G
// 1000 1234 5678 50000
expectedOutput := fmt.Sprintf("%d %d %d\n", imagePredefinedGroupUID, supplementalGroup, imagePredefinedGroupGID)

By("verify groups for the first process of the container")
verifyLogContents(podConfig, logPath, expectedOutput, stdoutType)

By("verify groups for 'exec'-ed process of container")
command := []string{"id", "-G"}
o := execSyncContainer(rc, containerID, command)
Expect(o).To(BeEquivalentTo(expectedOutput))
})

It("runtime should support RunAsUser", func() {
By("create pod")
podID, podConfig = framework.CreatePodSandboxForContainer(rc)
Expand Down Expand Up @@ -633,6 +582,140 @@ var _ = framework.KubeDescribe("Security Context", func() {
})
})

Context("SupplementalGroupsPolicy", func() {
When("SupplementalGroupsPolicy=Merge", func() {
It("if the container's primary UID belongs to some groups in the image, runtime should add SupplementalGroups to them", func() {
By("create pod")
podID, podConfig, podLogDir = createPodSandboxWithLogDirectory(rc)

By("create container for security context SupplementalGroups")
supplementalGroup := int64(1234)
containerName := "container-with-SupplementalGroupsPolicyMerge-" + framework.NewUUID()
logPath := containerName + ".log"
containerConfig := &runtimeapi.ContainerConfig{
Metadata: framework.BuildContainerMetadata(containerName, framework.DefaultAttempt),
Image: &runtimeapi.ImageSpec{Image: testImagePreDefinedGroup},
Command: []string{"sh", "-c", "id -G; while :; do sleep 1; done"},
Linux: &runtimeapi.LinuxContainerConfig{
SecurityContext: &runtimeapi.LinuxContainerSecurityContext{
RunAsUser: &runtimeapi.Int64Value{Value: imagePredefinedGroupUID},
SupplementalGroups: []int64{supplementalGroup},
// SupplementalGroupsPolicy_Merge is default(0)
// SupplementalGroupsPolicy: runtimeapi.SupplementalGroupsPolicy_Merge,
},
},
LogPath: logPath,
}
containerID := framework.CreateContainer(rc, ic, containerConfig, podID, podConfig)

By("start container")
startContainer(rc, containerID)

Eventually(func(g Gomega) {
containerStatus := getContainerStatus(rc, containerID)
g.Expect(containerStatus.State).To(Equal(runtimeapi.ContainerState_CONTAINER_RUNNING))
// In testImagePreDefinedGroup,
// - its default user is default-user(uid=1000)
// - default-user belongs to group-defined-in-image(gid=50000) in /etc/group
// And, SupplementalGroupsPolicy is Merge(default)
//
// Thus, firstly attached process identity of the first container processes should be
// - uid: 1000 (RunAsUser)
// - gid: 1000 (default group for uid=1000)
// - supplementary groups
// - 1000: self
// - 1234: SupplementalGroups
// - 50000: groups defined in the container image (/etc/group)
g.Expect(containerStatus.User).To(BeEquivalentTo(&runtimeapi.ContainerUser{
Linux: &runtimeapi.LinuxContainerUser{
Uid: imagePredefinedGroupUID,
Gid: imagePredefinedGroupUID,
SupplementalGroups: []int64{imagePredefinedGroupUID, supplementalGroup, imagePredefinedGroupGID},
},
}))
g.Expect(parseLogLine(podConfig, logPath)).NotTo(BeEmpty())
}, time.Minute, time.Second*4).Should(Succeed())

// $ id -G
// 1000 1234 50000
expectedOutput := fmt.Sprintf("%d %d %d\n", imagePredefinedGroupUID, supplementalGroup, imagePredefinedGroupGID)

By("verify groups for the first process of the container")
verifyLogContents(podConfig, logPath, expectedOutput, stdoutType)

By("verify groups for 'exec'-ed process of container")
command := []string{"id", "-G"}
o := execSyncContainer(rc, containerID, command)
Expect(o).To(BeEquivalentTo(expectedOutput))
})
})
When("SupplementalGroupsPolicy=Strict", func() {
It("even if the container's primary UID belongs to some groups in the image, runtime should not add SupplementalGroups to them", func() {
By("create pod")
podID, podConfig, podLogDir = createPodSandboxWithLogDirectory(rc)

By("create container for security context SupplementalGroups")
supplementalGroup := int64(1234)
containerName := "container-with-SupplementalGroupsPolicyMerge-" + framework.NewUUID()
logPath := containerName + ".log"
containerConfig := &runtimeapi.ContainerConfig{
Metadata: framework.BuildContainerMetadata(containerName, framework.DefaultAttempt),
Image: &runtimeapi.ImageSpec{Image: testImagePreDefinedGroup},
Command: []string{"sh", "-c", "id -G; while :; do sleep 1; done"},
Linux: &runtimeapi.LinuxContainerConfig{
SecurityContext: &runtimeapi.LinuxContainerSecurityContext{
RunAsUser: &runtimeapi.Int64Value{Value: imagePredefinedGroupUID},
SupplementalGroups: []int64{supplementalGroup},
SupplementalGroupsPolicy: runtimeapi.SupplementalGroupsPolicy_Strict,
},
},
LogPath: logPath,
}
containerID := framework.CreateContainer(rc, ic, containerConfig, podID, podConfig)

By("start container")
startContainer(rc, containerID)

Eventually(func(g Gomega) {
containerStatus := getContainerStatus(rc, containerID)
g.Expect(containerStatus.State).To(Equal(runtimeapi.ContainerState_CONTAINER_RUNNING))
// In testImagePreDefinedGroup,
// - its default user is default-user(uid=1000)
// - default-user belongs to group-defined-in-image(gid=50000) in /etc/group
// And, SupplementalGroupsPolicy is Strict
//
// Thus, firstly attached process identity of the first container processes should be
// (5000(defined in /etc/group) is not appended to supplementary groups)
// - uid: 1000 (RunAsUser)
// - gid: 1000 (default group for uid=1000)
// - supplementary groups
// - 1000: self
// - 1234: SupplementalGroups
g.Expect(containerStatus.User).To(BeEquivalentTo(&runtimeapi.ContainerUser{
Linux: &runtimeapi.LinuxContainerUser{
Uid: imagePredefinedGroupUID,
Gid: imagePredefinedGroupUID,
SupplementalGroups: []int64{imagePredefinedGroupUID, supplementalGroup},
},
}))
g.Expect(parseLogLine(podConfig, logPath)).NotTo(BeEmpty())
}, time.Minute, time.Second*4).Should(Succeed())

// $ id -G
// 1000 1234
expectedOutput := fmt.Sprintf("%d %d\n", imagePredefinedGroupUID, supplementalGroup)

By("verify groups for the first process of the container")
verifyLogContents(podConfig, logPath, expectedOutput, stdoutType)

By("verify groups for 'exec'-ed process of container")
command := []string{"id", "-G"}
o := execSyncContainer(rc, containerID, command)
Expect(o).To(BeEquivalentTo(expectedOutput))
})
})
})

// TODO(random-liu): We should set apparmor to unconfined in seccomp test to prevent
// them from interfering with each other.
Context("SeccompProfilePath", func() {
Expand Down

0 comments on commit 5aac837

Please sign in to comment.