diff --git a/auditors/nonroot/fix.go b/auditors/nonroot/fix.go index 9a2360f2..34922d2f 100644 --- a/auditors/nonroot/fix.go +++ b/auditors/nonroot/fix.go @@ -19,6 +19,11 @@ func (f *fixRunAsNonRoot) Apply(resource k8stypes.Resource) []k8stypes.Resource if f.container.SecurityContext == nil { f.container.SecurityContext = &k8stypes.SecurityContextV1{} } + + if f.container.SecurityContext.RunAsUser != nil && *f.container.SecurityContext.RunAsUser == 0 { + f.container.SecurityContext.RunAsUser = nil + } + f.container.SecurityContext.RunAsNonRoot = k8s.NewTrue() return nil } diff --git a/auditors/nonroot/fix_test.go b/auditors/nonroot/fix_test.go index 784f9f13..394b3069 100644 --- a/auditors/nonroot/fix_test.go +++ b/auditors/nonroot/fix_test.go @@ -27,6 +27,27 @@ func TestFixRunAsNonRoot(t *testing.T) { {"run-as-non-root-psc-false-csc-nil-multiple-cont.yml", fixtureDir, k8s.NewTrue()}, {"run-as-non-root-psc-false-csc-true-multiple-cont.yml", fixtureDir, k8s.NewTrue()}, {"run-as-non-root-psc-false-allowed-multi-containers-single-label.yml", fixtureDir, k8s.NewTrue()}, + {"run-as-user-0.yml", fixtureDir, k8s.NewTrue()}, + {"run-as-user-0-allowed.yml", fixtureDir, nil}, + {"run-as-user-psc-0.yml", fixtureDir, k8s.NewTrue()}, + {"run-as-user-psc-0-allowed.yml", fixtureDir, nil}, + {"run-as-user-psc-1.yml", fixtureDir, nil}, + {"run-as-user-psc-1-csc-0.yml", fixtureDir, k8s.NewTrue()}, + {"run-as-user-psc-0-csc-0.yml", fixtureDir, k8s.NewTrue()}, + {"run-as-user-psc-0-csc-1.yml", fixtureDir, nil}, + {"run-as-user-redundant-override-container.yml", fixtureDir, nil}, + {"run-as-user-redundant-override-pod.yml", fixtureDir, nil}, + {"run-as-user-psc-0-csc-1-multiple-cont.yml", fixtureDir, nil}, + {"run-as-user-psc-0-allowed-multi-containers-multi-labels.yml", fixtureDir, nil}, + {"run-as-user-psc-0-allowed-multi-containers-single-label.yml", fixtureDir, nil}, + {"run-as-user-0-run-as-non-root-true.yml", fixtureDir, k8s.NewTrue()}, + {"run-as-user-0-run-as-non-root-false.yml", fixtureDir, k8s.NewTrue()}, + {"run-as-user-psc-0-run-as-non-root-psc-true.yml", fixtureDir, k8s.NewTrue()}, + {"run-as-user-psc-0-run-as-non-root-psc-false.yml", fixtureDir, k8s.NewTrue()}, + {"run-as-user-1-run-as-non-root-true.yml", fixtureDir, k8s.NewTrue()}, + {"run-as-user-1-run-as-non-root-false.yml", fixtureDir, k8s.NewFalse()}, + {"run-as-user-psc-1-run-as-non-root-psc-true.yml", fixtureDir, nil}, + {"run-as-user-psc-1-run-as-non-root-psc-false.yml", fixtureDir, nil}, } for _, tc := range cases { @@ -45,19 +66,24 @@ func TestFixRunAsNonRoot(t *testing.T) { }) } - file := "run-as-non-root-psc-false-allowed-multi-containers-multi-labels.yml" - t.Run(file, func(t *testing.T) { - resources, _ := test.FixSetup(t, fixtureDir, file, New()) - for _, resource := range resources { - containers := k8s.GetContainers(resource) - for _, container := range containers { - switch container.Name { - case "fakeContainerRANR": - assert.True(t, (container.SecurityContext == nil || container.SecurityContext.RunAsNonRoot == nil)) - case "fakeContainerRANR2": - assert.True(t, *container.SecurityContext.RunAsNonRoot) + files := []string { + "run-as-non-root-psc-false-allowed-multi-containers-multi-labels.yml", + "run-as-user-psc-0-csc-nil-multiple-cont.yml", + } + for _, file := range files { + t.Run(file, func(t *testing.T) { + resources, _ := test.FixSetup(t, fixtureDir, file, New()) + for _, resource := range resources { + containers := k8s.GetContainers(resource) + for _, container := range containers { + switch container.Name { + case "container1": + assert.True(t, (container.SecurityContext == nil || container.SecurityContext.RunAsNonRoot == nil)) + case "container2": + assert.True(t, *container.SecurityContext.RunAsNonRoot) + } } } - } - }) + }) + } } diff --git a/auditors/nonroot/fixtures/run-as-user-0-allowed.yml b/auditors/nonroot/fixtures/run-as-user-0-allowed.yml new file mode 100644 index 00000000..5a8121c8 --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-0-allowed.yml @@ -0,0 +1,21 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deployment + namespace: run-as-user-0-allowed +spec: + selector: + matchLabels: + name: deployment + template: + metadata: + labels: + name: deployment + audit.kubernetes.io/pod.allow-run-as-root: "SuperuserPrivilegesNeeded" + spec: + containers: + - name: container + image: scratch + securityContext: + runAsUser: 0 diff --git a/auditors/nonroot/fixtures/run-as-user-0-run-as-non-root-false.yml b/auditors/nonroot/fixtures/run-as-user-0-run-as-non-root-false.yml new file mode 100644 index 00000000..68755c76 --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-0-run-as-non-root-false.yml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deployment + namespace: run-as-user-0-run-as-non-root-false +spec: + selector: + matchLabels: + name: deployment + template: + metadata: + labels: + name: deployment + spec: + containers: + - name: container + image: scratch + securityContext: + runAsUser: 0 + runAsNonRoot: false diff --git a/auditors/nonroot/fixtures/run-as-user-0-run-as-non-root-true.yml b/auditors/nonroot/fixtures/run-as-user-0-run-as-non-root-true.yml new file mode 100644 index 00000000..313a5f3d --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-0-run-as-non-root-true.yml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deployment + namespace: run-as-user-0-run-as-non-root-true +spec: + selector: + matchLabels: + name: deployment + template: + metadata: + labels: + name: deployment + spec: + containers: + - name: container + image: scratch + securityContext: + runAsUser: 0 + runAsNonRoot: true diff --git a/auditors/nonroot/fixtures/run-as-user-0.yml b/auditors/nonroot/fixtures/run-as-user-0.yml new file mode 100644 index 00000000..53d86500 --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-0.yml @@ -0,0 +1,19 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deployment + namespace: run-as-user-0 +spec: + selector: + matchLabels: + name: deployment + template: + metadata: + labels: + name: deployment + spec: + containers: + - name: container + image: scratch + securityContext: + runAsUser: 0 diff --git a/auditors/nonroot/fixtures/run-as-user-1-run-as-non-root-false.yml b/auditors/nonroot/fixtures/run-as-user-1-run-as-non-root-false.yml new file mode 100644 index 00000000..fd9c9caa --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-1-run-as-non-root-false.yml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deployment + namespace: run-as-user-1-run-as-non-root-false +spec: + selector: + matchLabels: + name: deployment + template: + metadata: + labels: + name: deployment + spec: + containers: + - name: container + image: scratch + securityContext: + runAsUser: 1 + runAsNonRoot: false diff --git a/auditors/nonroot/fixtures/run-as-user-1-run-as-non-root-true.yml b/auditors/nonroot/fixtures/run-as-user-1-run-as-non-root-true.yml new file mode 100644 index 00000000..eb888e23 --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-1-run-as-non-root-true.yml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deployment + namespace: run-as-user-1-run-as-non-root-true +spec: + selector: + matchLabels: + name: deployment + template: + metadata: + labels: + name: deployment + spec: + containers: + - name: container + image: scratch + securityContext: + runAsUser: 1 + runAsNonRoot: true diff --git a/auditors/nonroot/fixtures/run-as-user-psc-0-allowed-multi-containers-multi-labels.yml b/auditors/nonroot/fixtures/run-as-user-psc-0-allowed-multi-containers-multi-labels.yml new file mode 100644 index 00000000..77611261 --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-psc-0-allowed-multi-containers-multi-labels.yml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Pod +metadata: + name: pod + labels: + name: pod + container.audit.kubernetes.io/container1.allow-run-as-root: "SuperuserPrivilegesNeeded" + container.audit.kubernetes.io/container2.allow-run-as-root: "SuperuserPrivilegesNeeded" + namespace: run-as-user-psc-0-allowed-multi-containers-multi-labels +spec: + securityContext: + runAsUser: 0 + containers: + - name: container1 + image: scratch + - name: container2 + image: scratch + securityContext: + runAsUser: 1 diff --git a/auditors/nonroot/fixtures/run-as-user-psc-0-allowed-multi-containers-single-label.yml b/auditors/nonroot/fixtures/run-as-user-psc-0-allowed-multi-containers-single-label.yml new file mode 100644 index 00000000..8464388f --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-psc-0-allowed-multi-containers-single-label.yml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Pod +metadata: + name: pod + labels: + name: pod + container.audit.kubernetes.io/container2.allow-run-as-root: "SuperuserPrivilegesNeeded" + namespace: run-as-user-psc-0-allowed-multi-containers-single-label +spec: + securityContext: + runAsUser: 0 + containers: + - name: container1 + image: scratch + securityContext: + runAsUser: 1 + - name: container2 + image: scratch + securityContext: + runAsUser: 0 diff --git a/auditors/nonroot/fixtures/run-as-user-psc-0-allowed.yml b/auditors/nonroot/fixtures/run-as-user-psc-0-allowed.yml new file mode 100644 index 00000000..7e2fbb4c --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-psc-0-allowed.yml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Pod +metadata: + name: pod + labels: + name: pod + audit.kubernetes.io/pod.allow-run-as-root: "SuperuserPrivilegesNeeded" + namespace: run-as-user-psc-0-allowed +spec: + securityContext: + runAsUser: 0 + containers: + - name: container + image: scratch diff --git a/auditors/nonroot/fixtures/run-as-user-psc-0-csc-0.yml b/auditors/nonroot/fixtures/run-as-user-psc-0-csc-0.yml new file mode 100644 index 00000000..de0604ee --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-psc-0-csc-0.yml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Pod +metadata: + name: pod + namespace: run-as-user-psc-0-csc-0 +spec: + securityContext: + runAsUser: 0 + containers: + - name: container + image: scratch + securityContext: + runAsUser: 0 diff --git a/auditors/nonroot/fixtures/run-as-user-psc-0-csc-1-multiple-cont.yml b/auditors/nonroot/fixtures/run-as-user-psc-0-csc-1-multiple-cont.yml new file mode 100644 index 00000000..10569477 --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-psc-0-csc-1-multiple-cont.yml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Pod +metadata: + name: pod + namespace: run-as-user-psc-0-csc-1-multiple-cont +spec: + securityContext: + runAsUser: 0 + containers: + - name: container1 + image: scratch + securityContext: + runAsUser: 1 + - name: container2 + image: scratch + securityContext: + runAsUser: 1 diff --git a/auditors/nonroot/fixtures/run-as-user-psc-0-csc-1.yml b/auditors/nonroot/fixtures/run-as-user-psc-0-csc-1.yml new file mode 100644 index 00000000..762322ac --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-psc-0-csc-1.yml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Pod +metadata: + name: pod + namespace: run-as-user-psc-0-csc-1 +spec: + securityContext: + runAsUser: 0 + containers: + - name: container + image: scratch + securityContext: + runAsUser: 1 diff --git a/auditors/nonroot/fixtures/run-as-user-psc-0-csc-nil-multiple-cont.yml b/auditors/nonroot/fixtures/run-as-user-psc-0-csc-nil-multiple-cont.yml new file mode 100644 index 00000000..1625bdd5 --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-psc-0-csc-nil-multiple-cont.yml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: pod + namespace: run-as-user-psc-0-csc-nil-multiple-cont +spec: + securityContext: + runAsUser: 0 + containers: + - name: container1 + image: scratch + securityContext: + runAsUser: 1 + - name: container2 + image: scratch diff --git a/auditors/nonroot/fixtures/run-as-user-psc-0-run-as-non-root-psc-false.yml b/auditors/nonroot/fixtures/run-as-user-psc-0-run-as-non-root-psc-false.yml new file mode 100644 index 00000000..d008df80 --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-psc-0-run-as-non-root-psc-false.yml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Pod +metadata: + name: pod + labels: + name: pod + namespace: run-as-user-psc-0-run-as-non-root-psc-false +spec: + securityContext: + runAsUser: 0 + runAsNonRoot: false + containers: + - name: container + image: scratch diff --git a/auditors/nonroot/fixtures/run-as-user-psc-0-run-as-non-root-psc-true.yml b/auditors/nonroot/fixtures/run-as-user-psc-0-run-as-non-root-psc-true.yml new file mode 100644 index 00000000..47aee13b --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-psc-0-run-as-non-root-psc-true.yml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Pod +metadata: + name: pod + labels: + name: pod + namespace: run-as-user-psc-0-run-as-non-root-psc-true +spec: + securityContext: + runAsUser: 0 + runAsNonRoot: true + containers: + - name: container + image: scratch diff --git a/auditors/nonroot/fixtures/run-as-user-psc-0.yml b/auditors/nonroot/fixtures/run-as-user-psc-0.yml new file mode 100644 index 00000000..245476f7 --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-psc-0.yml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Pod +metadata: + name: pod + labels: + name: pod + namespace: run-as-user-psc-0 +spec: + securityContext: + runAsUser: 0 + containers: + - name: container + image: scratch diff --git a/auditors/nonroot/fixtures/run-as-user-psc-1-csc-0.yml b/auditors/nonroot/fixtures/run-as-user-psc-1-csc-0.yml new file mode 100644 index 00000000..3b0ff043 --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-psc-1-csc-0.yml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Pod +metadata: + name: pod + namespace: run-as-user-psc-1-csc-0 +spec: + securityContext: + runAsUser: 1 + containers: + - name: container + image: scratch + securityContext: + runAsUser: 0 diff --git a/auditors/nonroot/fixtures/run-as-user-psc-1-run-as-non-root-psc-false.yml b/auditors/nonroot/fixtures/run-as-user-psc-1-run-as-non-root-psc-false.yml new file mode 100644 index 00000000..2a8d8d31 --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-psc-1-run-as-non-root-psc-false.yml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Pod +metadata: + name: pod + labels: + name: pod + namespace: run-as-user-psc-1-run-as-non-root-psc-false +spec: + securityContext: + runAsUser: 1 + runAsNonRoot: false + containers: + - name: container + image: scratch diff --git a/auditors/nonroot/fixtures/run-as-user-psc-1-run-as-non-root-psc-true.yml b/auditors/nonroot/fixtures/run-as-user-psc-1-run-as-non-root-psc-true.yml new file mode 100644 index 00000000..de141324 --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-psc-1-run-as-non-root-psc-true.yml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Pod +metadata: + name: pod + labels: + name: pod + namespace: run-as-user-psc-1-run-as-non-root-psc-true +spec: + securityContext: + runAsUser: 1 + runAsNonRoot: true + containers: + - name: container + image: scratch diff --git a/auditors/nonroot/fixtures/run-as-user-psc-1.yml b/auditors/nonroot/fixtures/run-as-user-psc-1.yml new file mode 100644 index 00000000..bbb107a4 --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-psc-1.yml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Pod +metadata: + name: pod + labels: + name: pod + namespace: run-as-user-psc-1 +spec: + securityContext: + runAsUser: 1 + containers: + - name: container + image: scratch diff --git a/auditors/nonroot/fixtures/run-as-user-redundant-override-container.yml b/auditors/nonroot/fixtures/run-as-user-redundant-override-container.yml new file mode 100644 index 00000000..6687be4b --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-redundant-override-container.yml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deployment + namespace: run-as-user-redundant-override-container +spec: + selector: + matchLabels: + name: deployment + template: + metadata: + labels: + name: deployment + audit.kubernetes.io/pod.allow-run-as-root: "SuperuserPrivilegesNeeded" + spec: + containers: + - name: container + image: scratch + securityContext: + runAsUser: 1 diff --git a/auditors/nonroot/fixtures/run-as-user-redundant-override-pod.yml b/auditors/nonroot/fixtures/run-as-user-redundant-override-pod.yml new file mode 100644 index 00000000..0ac7a73a --- /dev/null +++ b/auditors/nonroot/fixtures/run-as-user-redundant-override-pod.yml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Pod +metadata: + name: pod + labels: + name: pod + audit.kubernetes.io/pod.allow-run-as-root: "SuperuserPrivilegesNeeded" + namespace: run-as-user-redundant-override-pod +spec: + securityContext: + runAsUser: 1 + containers: + - name: container + image: scratch diff --git a/auditors/nonroot/nonroot.go b/auditors/nonroot/nonroot.go index 0595d4df..7ae937e6 100644 --- a/auditors/nonroot/nonroot.go +++ b/auditors/nonroot/nonroot.go @@ -10,6 +10,10 @@ import ( const Name = "nonroot" const ( + // RunAsUserCSCRoot occurs when runAsUser is set to 0 in the container SecurityContext + RunAsUserCSCRoot = "RunAsUserCSCRoot" + // RunAsUserPSCRoot occurs when runAsUser is set to 0 in the pod SecurityContext + RunAsUserPSCRoot = "RunAsUserPSCRoot" // RunAsNonRootCSCFalse occurs when runAsNonRoot is set to false in the container SecurityContext RunAsNonRootCSCFalse = "RunAsNonRootCSCFalse" // RunAsNonRootPSCNilCSCNil occurs when runAsNonRoot is not set in the container SecurityContext nor the pod @@ -45,11 +49,65 @@ func (a *RunAsNonRoot) Audit(resource k8stypes.Resource, _ []k8stypes.Resource) } func auditContainer(container *k8stypes.ContainerV1, resource k8stypes.Resource) *kubeaudit.AuditResult { + podSpec := k8s.GetPodSpec(resource) + if podSpec == nil { + return nil + } + + if !isContainerRunAsUserNil(container) { + if *container.SecurityContext.RunAsUser == 0 { + return &kubeaudit.AuditResult{ + Name: RunAsUserCSCRoot, + Severity: kubeaudit.Error, + Message: "runAsUser is set to UID 0 (root user) in the container SecurityContext. Either set it to a value > 0 or remove it and set runAsNonRoot to true.", + PendingFix: &fixRunAsNonRoot{ + container: container, + }, + Metadata: kubeaudit.Metadata{ + "Container": container.Name, + }, + } + } + + if !isPodRunAsUserNil(podSpec) { + if *podSpec.SecurityContext.RunAsUser == 0 { + return &kubeaudit.AuditResult{ + Name: RunAsUserPSCRoot, + Severity: kubeaudit.Warn, + Message: "runAsUser is set to UID 0 (root user) in the PodSecurityContext. Either set it to a value > 0 or remove it and set runAsNonRoot to true.", + Metadata: kubeaudit.Metadata{ + "Container": container.Name, + }, + } + } + } + + return nil + } + + if !isPodRunAsUserNil(podSpec) { + if *podSpec.SecurityContext.RunAsUser == 0 { + return &kubeaudit.AuditResult{ + Name: RunAsUserPSCRoot, + Severity: kubeaudit.Error, + Message: "runAsUser is set to UID 0 (root user) in the PodSecurityContext. Either set it to a value > 0 or remove it and set runAsNonRoot to true.", + PendingFix: &fixRunAsNonRoot{ + container: container, + }, + Metadata: kubeaudit.Metadata{ + "Container": container.Name, + }, + } + } + + return nil + } + if isContainerRunAsNonRootCSCFalse(container) { return &kubeaudit.AuditResult{ Name: RunAsNonRootCSCFalse, Severity: kubeaudit.Error, - Message: "runAsNonRoot is set to false in container SecurityContext. It should be set to true.", + Message: "runAsNonRoot is set to false in the container SecurityContext. Either set it to true or set runAsUser to a value > 0.", PendingFix: &fixRunAsNonRoot{ container: container, }, @@ -59,16 +117,12 @@ func auditContainer(container *k8stypes.ContainerV1, resource k8stypes.Resource) } } - podSpec := k8s.GetPodSpec(resource) - if podSpec == nil { - return nil - } if isContainerRunAsNonRootNil(container) { if isPodRunAsNonRootNil(podSpec) { return &kubeaudit.AuditResult{ Name: RunAsNonRootPSCNilCSCNil, Severity: kubeaudit.Error, - Message: "runAsNonRoot is not set in container SecurityContext nor the PodSecurityContext. It should be set to 'true' in at least one of the two.", + Message: "runAsNonRoot should be set to true or runAsUser should be set to a value > 0 either in the container SecurityContext or PodSecurityContext.", PendingFix: &fixRunAsNonRoot{ container: container, }, @@ -82,7 +136,7 @@ func auditContainer(container *k8stypes.ContainerV1, resource k8stypes.Resource) return &kubeaudit.AuditResult{ Name: RunAsNonRootPSCFalseCSCNil, Severity: kubeaudit.Error, - Message: "runAsNonRoot is not set in container SecurityContext and is set to false in the PodSecurityContext. It should be set to 'true' in at least one of the two.", + Message: "runAsNonRoot is set to false in the PodSecurityContext. Either set it to true or set runAsUser to a value > 0.", PendingFix: &fixRunAsNonRoot{ container: container, }, @@ -96,7 +150,7 @@ func auditContainer(container *k8stypes.ContainerV1, resource k8stypes.Resource) return nil } -// returns true if runAsNonRoot is explicilty set to false in the pod's security context. Returns true if the +// returns true if runAsNonRoot is explicitly set to false in the pod's security context. Returns true if the // security context is nil even though the default value for runAsNonRoot is false func isPodRunAsNonRootFalse(podSpec *k8stypes.PodSpecV1) bool { if isPodRunAsNonRootNil(podSpec) { @@ -107,14 +161,10 @@ func isPodRunAsNonRootFalse(podSpec *k8stypes.PodSpecV1) bool { } func isPodRunAsNonRootNil(podSpec *k8stypes.PodSpecV1) bool { - if podSpec.SecurityContext == nil || podSpec.SecurityContext.RunAsNonRoot == nil { - return true - } - - return false + return podSpec.SecurityContext == nil || podSpec.SecurityContext.RunAsNonRoot == nil } -// returns true if runAsNonRoot is explicilty set to false in the containers's security context. Returns true if the +// returns true if runAsNonRoot is explicitly set to false in the container's security context. Returns true if the // security context is nil even though the default value for runAsNonRoot is false func isContainerRunAsNonRootCSCFalse(container *k8stypes.ContainerV1) bool { if isContainerRunAsNonRootNil(container) { @@ -127,3 +177,11 @@ func isContainerRunAsNonRootCSCFalse(container *k8stypes.ContainerV1) bool { func isContainerRunAsNonRootNil(container *k8stypes.ContainerV1) bool { return container.SecurityContext == nil || container.SecurityContext.RunAsNonRoot == nil } + +func isContainerRunAsUserNil(container *k8stypes.ContainerV1) bool { + return container.SecurityContext == nil || container.SecurityContext.RunAsUser == nil +} + +func isPodRunAsUserNil(podSpec *k8stypes.PodSpecV1) bool { + return podSpec.SecurityContext == nil || podSpec.SecurityContext.RunAsUser == nil +} diff --git a/auditors/nonroot/nonroot_test.go b/auditors/nonroot/nonroot_test.go index d25cab12..b8293ed0 100644 --- a/auditors/nonroot/nonroot_test.go +++ b/auditors/nonroot/nonroot_test.go @@ -37,6 +37,32 @@ func TestAuditRunAsNonRoot(t *testing.T) { {"run-as-non-root-psc-false-allowed-multi-containers-single-label.yml", fixtureDir, []string{ kubeaudit.RedundantAuditorOverride, RunAsNonRootCSCFalse, }}, + {"run-as-user-0.yml", fixtureDir, []string{RunAsUserCSCRoot}}, + {"run-as-user-0-allowed.yml", fixtureDir, []string{override.GetOverriddenResultName(RunAsUserCSCRoot)}}, + {"run-as-user-psc-0.yml", fixtureDir, []string{RunAsUserPSCRoot}}, + {"run-as-user-psc-0-allowed.yml", fixtureDir, []string{override.GetOverriddenResultName(RunAsUserPSCRoot)}}, + {"run-as-user-psc-1.yml", fixtureDir, []string{}}, + {"run-as-user-psc-1-csc-0.yml", fixtureDir, []string{RunAsUserCSCRoot}}, + {"run-as-user-psc-0-csc-0.yml", fixtureDir, []string{RunAsUserCSCRoot}}, + {"run-as-user-psc-0-csc-1.yml", fixtureDir, []string{RunAsUserPSCRoot}}, + {"run-as-user-redundant-override-container.yml", fixtureDir, []string{kubeaudit.RedundantAuditorOverride}}, + {"run-as-user-redundant-override-pod.yml", fixtureDir, []string{kubeaudit.RedundantAuditorOverride}}, + {"run-as-user-psc-0-csc-nil-multiple-cont.yml", fixtureDir, []string{RunAsUserPSCRoot}}, + {"run-as-user-psc-0-csc-1-multiple-cont.yml", fixtureDir, []string{RunAsUserPSCRoot}}, + {"run-as-user-psc-0-allowed-multi-containers-multi-labels.yml", fixtureDir, []string{ + override.GetOverriddenResultName(RunAsUserPSCRoot), + }}, + {"run-as-user-psc-0-allowed-multi-containers-single-label.yml", fixtureDir, []string{ + override.GetOverriddenResultName(RunAsUserCSCRoot), RunAsUserPSCRoot, + }}, + {"run-as-user-0-run-as-non-root-true.yml", fixtureDir, []string{RunAsUserCSCRoot}}, + {"run-as-user-0-run-as-non-root-false.yml", fixtureDir, []string{RunAsUserCSCRoot}}, + {"run-as-user-psc-0-run-as-non-root-psc-true.yml", fixtureDir, []string{RunAsUserPSCRoot}}, + {"run-as-user-psc-0-run-as-non-root-psc-false.yml", fixtureDir, []string{RunAsUserPSCRoot}}, + {"run-as-user-1-run-as-non-root-true.yml", fixtureDir, []string{}}, + {"run-as-user-1-run-as-non-root-false.yml", fixtureDir, []string{}}, + {"run-as-user-psc-1-run-as-non-root-psc-true.yml", fixtureDir, []string{}}, + {"run-as-user-psc-1-run-as-non-root-psc-false.yml", fixtureDir, []string{}}, } for _, tc := range cases { diff --git a/cmd/commands/nonroot.go b/cmd/commands/nonroot.go index 3adf9952..0e1df494 100644 --- a/cmd/commands/nonroot.go +++ b/cmd/commands/nonroot.go @@ -7,11 +7,11 @@ import ( var runAsNonRootCmd = &cobra.Command{ Use: "nonroot", - Short: "Audit containers running as root", - Long: `This command determines which containers are running as root (uid=0). + Short: "Audit containers allowing for root user", + Long: `This command determines which containers are allowed to run as root (uid=0). -An ERROR result is generated when container does not have 'runAsNonRoot = true' in either its container - SecurityContext or its pod SecurityContext. +An ERROR result is generated when container does not have 'runAsNonRoot = true' or if a root user (UID 0) is explicitly + set using 'runAsUser' in either its container SecurityContext or its pod SecurityContext. Example usage: kubeaudit nonroot`, diff --git a/docs/auditors/nonroot.md b/docs/auditors/nonroot.md index 770a2de2..f75eb6ac 100644 --- a/docs/auditors/nonroot.md +++ b/docs/auditors/nonroot.md @@ -1,6 +1,6 @@ # runAsNonRoot Auditor (nonroot) -Finds containers running as root. +Finds containers allowed to run as root. ## General Usage @@ -52,6 +52,23 @@ spec: - name: myContainer ``` +Alternatively it's possible to enforce non-root containers by setting `runAsUser` to a non-root UID (>0) in either the PodSecurityContext or container SecurityContext. Conversely, if `runAsUser` is set to `0` in either the PodSecurityContext or container SecurityContext then the container will always run as root and so the audit will fail. + +If `runAsUser` is set to a non-root UID (either in PodSecurityContext or container SecurityContext) it won't matter if `runAsNonRoot` is set to `false` or `nil` and so the audit will always pass. + +As for `runAsNonRoot`, ideally, `runAsUser` should be set to a non-root UID in the PodSecurityContext: +```yaml +apiVersion: apps/v1 +kind: Deployment +spec: + template: #PodTemplateSpec + spec: #PodSpec + securityContext: #PodSecurityContext + runAsUser: 1000 + containers: + - name: myContainer +``` + If a container needs to run as root, it should be enabled for that container only in the container's SecurityContext. This will require an override label so kubeaudit knows it is intentional. See [Override Errors](#override-errors). For more information on pod and container security contexts see https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ @@ -111,3 +128,22 @@ spec: securityContext: #SecurityContext runAsNonRoot: false ``` + +Example of resource with `nonroot` overridden for a specific container using `runAsUser`: +```yaml +apiVersion: apps/v1 +kind: Deployment +spec: + template: #PodTemplateSpec + metadata: + labels: + container.audit.kubernetes.io/myContainer.allow-run-as-root: "" + spec: #PodSpec + securityContext: #PodSecurityContext + runAsUser: 1000 + containers: + - name: myContainer + securityContext: #SecurityContext + runAsUser: 0 + - name: myContainer2 +```