diff --git a/staging/src/k8s.io/pod-security-admission/policy/check_hostProcess.go b/staging/src/k8s.io/pod-security-admission/policy/check_windowsHostProcess.go similarity index 63% rename from staging/src/k8s.io/pod-security-admission/policy/check_hostProcess.go rename to staging/src/k8s.io/pod-security-admission/policy/check_windowsHostProcess.go index 061125841b7..d8d189cb35d 100644 --- a/staging/src/k8s.io/pod-security-admission/policy/check_hostProcess.go +++ b/staging/src/k8s.io/pod-security-admission/policy/check_windowsHostProcess.go @@ -17,6 +17,7 @@ limitations under the License. package policy import ( + "fmt" "strings" corev1 "k8s.io/api/core/v1" @@ -26,43 +27,44 @@ import ( ) /* -Containers must not run as hostProcess. +Pod and containers must not set securityContext.windowsOptions.hostProcess to true. **Restricted Fields:** spec.securityContext.windowsOptions.hostProcess spec.containers[*].securityContext.windowsOptions.hostProcess +spec.initContainers[*].securityContext.windowsOptions.hostProcess **Allowed Values:** undefined / false */ func init() { - addCheck(CheckHostProcess) + addCheck(CheckWindowsHostProcess) } -// CheckHostProcess returns a baseline level check +// CheckWindowsHostProcess returns a baseline level check // that forbids hostProcess=true in 1.0+ -func CheckHostProcess() Check { +func CheckWindowsHostProcess() Check { return Check{ - ID: "hostProcess", + ID: "windowsHostProcess", Level: api.LevelBaseline, Versions: []VersionedCheck{ { MinimumVersion: api.MajorMinorVersion(1, 0), - CheckPod: hostProcess_1_0, + CheckPod: windowsHostProcess_1_0, }, }, } } -func hostProcess_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult { - var forbiddenContainers []string +func windowsHostProcess_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult { + var badContainers []string visitContainersWithPath(podSpec, field.NewPath("spec"), func(container *corev1.Container, path *field.Path) { if container.SecurityContext != nil && container.SecurityContext.WindowsOptions != nil && container.SecurityContext.WindowsOptions.HostProcess != nil && *container.SecurityContext.WindowsOptions.HostProcess { - forbiddenContainers = append(forbiddenContainers, container.Name) + badContainers = append(badContainers, container.Name) } }) @@ -75,11 +77,25 @@ func hostProcess_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) Ch } // pod or containers explicitly set hostProcess=true - if len(forbiddenContainers) > 0 || podSpecForbidden { + var forbiddenSetters []string + if podSpecForbidden { + forbiddenSetters = append(forbiddenSetters, "pod") + } + if len(badContainers) > 0 { + forbiddenSetters = append( + forbiddenSetters, + fmt.Sprintf( + "%s %s", + pluralize("container", "containers", len(badContainers)), + joinQuote(badContainers), + ), + ) + } + if len(forbiddenSetters) > 0 { return CheckResult{ Allowed: false, - ForbiddenReason: "hostProcess == true", - ForbiddenDetail: strings.Join(forbiddenContainers, ", "), + ForbiddenReason: "hostProcess", + ForbiddenDetail: fmt.Sprintf("%s must not set securityContext.windowsOptions.hostProcess=true", strings.Join(forbiddenSetters, " and ")), } } diff --git a/staging/src/k8s.io/pod-security-admission/policy/check_windowsHostProcess_test.go b/staging/src/k8s.io/pod-security-admission/policy/check_windowsHostProcess_test.go new file mode 100644 index 00000000000..881ab94c96c --- /dev/null +++ b/staging/src/k8s.io/pod-security-admission/policy/check_windowsHostProcess_test.go @@ -0,0 +1,67 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package policy + +import ( + "testing" + + corev1 "k8s.io/api/core/v1" + utilpointer "k8s.io/utils/pointer" +) + +func TestWindowsHostProcess(t *testing.T) { + tests := []struct { + name string + pod *corev1.Pod + expectReason string + expectDetail string + }{ + { + name: "host process", + pod: &corev1.Pod{Spec: corev1.PodSpec{ + SecurityContext: &corev1.PodSecurityContext{ + WindowsOptions: &corev1.WindowsSecurityContextOptions{HostProcess: utilpointer.Bool(true)}, + }, + Containers: []corev1.Container{ + {Name: "a", SecurityContext: nil}, + {Name: "b", SecurityContext: &corev1.SecurityContext{}}, + {Name: "c", SecurityContext: &corev1.SecurityContext{WindowsOptions: &corev1.WindowsSecurityContextOptions{}}}, + {Name: "d", SecurityContext: &corev1.SecurityContext{WindowsOptions: &corev1.WindowsSecurityContextOptions{HostProcess: utilpointer.Bool(false)}}}, + {Name: "e", SecurityContext: &corev1.SecurityContext{WindowsOptions: &corev1.WindowsSecurityContextOptions{HostProcess: utilpointer.Bool(true)}}}, + {Name: "f", SecurityContext: &corev1.SecurityContext{WindowsOptions: &corev1.WindowsSecurityContextOptions{HostProcess: utilpointer.Bool(true)}}}, + }, + }}, + expectReason: `hostProcess`, + expectDetail: `pod and containers "e", "f" must not set securityContext.windowsOptions.hostProcess=true`, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + result := windowsHostProcess_1_0(&tc.pod.ObjectMeta, &tc.pod.Spec) + if result.Allowed { + t.Fatal("expected disallowed") + } + if e, a := tc.expectReason, result.ForbiddenReason; e != a { + t.Errorf("expected\n%s\ngot\n%s", e, a) + } + if e, a := tc.expectDetail, result.ForbiddenDetail; e != a { + t.Errorf("expected\n%s\ngot\n%s", e, a) + } + }) + } +} diff --git a/staging/src/k8s.io/pod-security-admission/test/fixtures_hostProcess.go b/staging/src/k8s.io/pod-security-admission/test/fixtures_windowsHostProcess.go similarity index 97% rename from staging/src/k8s.io/pod-security-admission/test/fixtures_hostProcess.go rename to staging/src/k8s.io/pod-security-admission/test/fixtures_windowsHostProcess.go index 95066537d7b..f3b166f132d 100644 --- a/staging/src/k8s.io/pod-security-admission/test/fixtures_hostProcess.go +++ b/staging/src/k8s.io/pod-security-admission/test/fixtures_windowsHostProcess.go @@ -42,6 +42,7 @@ func init() { // minimal valid pod already captures all valid combinations return nil }, + expectErrorSubstring: "hostProcess", failRequiresFeatures: []featuregate.Feature{"WindowsHostProcessContainers"}, generateFail: func(p *corev1.Pod) []*corev1.Pod { p = ensureSecurityContext(p) @@ -75,7 +76,7 @@ func init() { } registerFixtureGenerator( - fixtureKey{level: api.LevelBaseline, version: api.MajorMinorVersion(1, 0), check: "hostProcess"}, + fixtureKey{level: api.LevelBaseline, version: api.MajorMinorVersion(1, 0), check: "windowsHostProcess"}, fixtureData_1_0, ) // TODO: register another set of fixtures with passing test cases that explicitly set hostProcess=false at pod and container level once hostProcess is GA