Merge pull request #111009 from marosset/runasnonroot-windows-fix

Windows: ensure runAsNonRoot does case-insensitive comparison on username
This commit is contained in:
Kubernetes Prow Robot 2022-07-28 17:55:22 -07:00 committed by GitHub
commit 73b3be3082
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 85 additions and 3 deletions

View File

@ -25,6 +25,7 @@ import (
"k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/kubelet/util/format"
"k8s.io/kubernetes/pkg/securitycontext"
"strings"
)
var (
@ -37,6 +38,7 @@ var (
// According to the discussion of sig-windows, at present, we assume that ContainerAdministrator is the windows container root user,
// and then optimize this logic according to the best time.
// https://docs.google.com/document/d/1Tjxzjjuy4SQsFSUVXZbvqVb64hjNAG5CQX8bK7Yda9w
// note: usernames on Windows are NOT case sensitive!
func verifyRunAsNonRoot(pod *v1.Pod, container *v1.Container, uid *int64, username string) error {
effectiveSc := securitycontext.DetermineEffectiveSecurityContext(pod, container)
// If the option is not set, or if running as root is allowed, return nil.
@ -54,15 +56,17 @@ func verifyRunAsNonRoot(pod *v1.Pod, container *v1.Container, uid *int64, userna
if effectiveSc.RunAsGroup != nil {
klog.InfoS("Windows container does not support SecurityContext.RunAsGroup", "pod", klog.KObj(pod), "containerName", container.Name)
}
// Verify that if runAsUserName is set for the pod and/or container that it is not set to 'ContainerAdministrator'
if effectiveSc.WindowsOptions != nil {
if effectiveSc.WindowsOptions.RunAsUserName != nil {
if *effectiveSc.WindowsOptions.RunAsUserName == windowsRootUserName {
return fmt.Errorf("container's runAsUser (%s) which will be regarded as root identity and will break non-root policy (pod: %q, container: %s)", username, format.Pod(pod), container.Name)
if strings.EqualFold(*effectiveSc.WindowsOptions.RunAsUserName, windowsRootUserName) {
return fmt.Errorf("container's runAsUserName (%s) which will be regarded as root identity and will break non-root policy (pod: %q, container: %s)", *effectiveSc.WindowsOptions.RunAsUserName, format.Pod(pod), container.Name)
}
return nil
}
}
if len(username) > 0 && username == windowsRootUserName {
// Verify that if runAsUserName is NOT set for the pod and/or container that the default user for the container image is not set to 'ContainerAdministrator'
if len(username) > 0 && strings.EqualFold(username, windowsRootUserName) {
return fmt.Errorf("container's runAsUser (%s) which will be regarded as root identity and will break non-root policy (pod: %q, container: %s)", username, format.Pod(pod), container.Name)
}
return nil

View File

@ -47,6 +47,7 @@ func TestVerifyRunAsNonRoot(t *testing.T) {
},
}
rootUser := "ContainerAdministrator"
rootUserUppercase := "CONTAINERADMINISTRATOR"
anyUser := "anyone"
runAsNonRootTrue := true
runAsNonRootFalse := false
@ -101,6 +102,17 @@ func TestVerifyRunAsNonRoot(t *testing.T) {
username: rootUser,
fail: true,
},
{
desc: "Fail if container's RunAsUser is root (case-insensitive) and RunAsNonRoot is true",
sc: &v1.SecurityContext{
RunAsNonRoot: &runAsNonRootTrue,
WindowsOptions: &v1.WindowsSecurityContextOptions{
RunAsUserName: &rootUserUppercase,
},
},
username: anyUser,
fail: true,
},
{
desc: "Fail if image's user is root and RunAsNonRoot is true",
sc: &v1.SecurityContext{
@ -109,6 +121,14 @@ func TestVerifyRunAsNonRoot(t *testing.T) {
username: rootUser,
fail: true,
},
{
desc: "Fail if image's user is root (case-insensitive) and RunAsNonRoot is true",
sc: &v1.SecurityContext{
RunAsNonRoot: &runAsNonRootTrue,
},
username: rootUserUppercase,
fail: true,
},
{
desc: "Pass if image's user is non-root and RunAsNonRoot is true",
sc: &v1.SecurityContext{
@ -124,6 +144,28 @@ func TestVerifyRunAsNonRoot(t *testing.T) {
},
fail: false,
},
{
desc: "Pass if image's user is root, container's RunAsUser is not root and RunAsNonRoot is true",
sc: &v1.SecurityContext{
RunAsNonRoot: &runAsNonRootTrue,
WindowsOptions: &v1.WindowsSecurityContextOptions{
RunAsUserName: &anyUser,
},
},
username: rootUser,
fail: false,
},
{
desc: "Pass if image's user is root (case-insensitive), container's RunAsUser is not root and RunAsNonRoot is true",
sc: &v1.SecurityContext{
RunAsNonRoot: &runAsNonRootTrue,
WindowsOptions: &v1.WindowsSecurityContextOptions{
RunAsUserName: &anyUser,
},
},
username: rootUserUppercase,
fail: false,
},
} {
pod.Spec.Containers[0].SecurityContext = test.sc
err := verifyRunAsNonRoot(pod, &pod.Spec.Containers[0], test.uid, test.username)

View File

@ -153,6 +153,42 @@ var _ = SIGDescribe("[Feature:Windows] SecurityContext", func() {
framework.ExpectNoError(e2epod.WaitForPodNameRunningInNamespace(f.ClientSet, windowsPodWithSELinux.Name,
f.Namespace.Name), "failed to wait for pod %s to be running", windowsPodWithSELinux.Name)
})
ginkgo.It("should not be able to create pods with containers running as ContainerAdministrator when runAsNonRoot is true", func() {
ginkgo.By("Creating a pod")
p := runAsUserNamePod(toPtr("ContainerAdministrator"))
p.Spec.SecurityContext.RunAsNonRoot = &trueVar
podInvalid, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(context.TODO(), p, metav1.CreateOptions{})
framework.ExpectNoError(err, "Error creating pod")
ginkgo.By("Waiting for pod to finish")
event, err := f.PodClient().WaitForErrorEventOrSuccess(podInvalid)
framework.ExpectNoError(err)
framework.ExpectNotEqual(event, nil, "event should not be empty")
framework.Logf("Got event: %v", event)
expectedEventError := "container's runAsUserName (ContainerAdministrator) which will be regarded as root identity and will break non-root policy"
framework.ExpectEqual(true, strings.Contains(event.Message, expectedEventError), "Event error should indicate non-root policy caused container to not start")
})
ginkgo.It("should not be able to create pods with containers running as CONTAINERADMINISTRATOR when runAsNonRoot is true", func() {
ginkgo.By("Creating a pod")
p := runAsUserNamePod(toPtr("CONTAINERADMINISTRATOR"))
p.Spec.SecurityContext.RunAsNonRoot = &trueVar
podInvalid, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(context.TODO(), p, metav1.CreateOptions{})
framework.ExpectNoError(err, "Error creating pod")
ginkgo.By("Waiting for pod to finish")
event, err := f.PodClient().WaitForErrorEventOrSuccess(podInvalid)
framework.ExpectNoError(err)
framework.ExpectNotEqual(event, nil, "event should not be empty")
framework.Logf("Got event: %v", event)
expectedEventError := "container's runAsUserName (CONTAINERADMINISTRATOR) which will be regarded as root identity and will break non-root policy"
framework.ExpectEqual(true, strings.Contains(event.Message, expectedEventError), "Event error should indicate non-root policy caused container to not start")
})
})
func runAsUserNamePod(username *string) *v1.Pod {