diff --git a/staging/src/k8s.io/pod-security-admission/policy/check_hostProcess.go b/staging/src/k8s.io/pod-security-admission/policy/check_hostProcess.go new file mode 100644 index 00000000000..061125841b7 --- /dev/null +++ b/staging/src/k8s.io/pod-security-admission/policy/check_hostProcess.go @@ -0,0 +1,87 @@ +/* +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 ( + "strings" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/pod-security-admission/api" +) + +/* +Containers must not run as hostProcess. + +**Restricted Fields:** + +spec.securityContext.windowsOptions.hostProcess +spec.containers[*].securityContext.windowsOptions.hostProcess + +**Allowed Values:** undefined / false +*/ + +func init() { + addCheck(CheckHostProcess) +} + +// CheckHostProcess returns a baseline level check +// that forbids hostProcess=true in 1.0+ +func CheckHostProcess() Check { + return Check{ + ID: "hostProcess", + Level: api.LevelBaseline, + Versions: []VersionedCheck{ + { + MinimumVersion: api.MajorMinorVersion(1, 0), + CheckPod: hostProcess_1_0, + }, + }, + } +} + +func hostProcess_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult { + var forbiddenContainers []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) + } + }) + + podSpecForbidden := false + if podSpec.SecurityContext != nil && + podSpec.SecurityContext.WindowsOptions != nil && + podSpec.SecurityContext.WindowsOptions.HostProcess != nil && + *podSpec.SecurityContext.WindowsOptions.HostProcess { + podSpecForbidden = true + } + + // pod or containers explicitly set hostProcess=true + if len(forbiddenContainers) > 0 || podSpecForbidden { + return CheckResult{ + Allowed: false, + ForbiddenReason: "hostProcess == true", + ForbiddenDetail: strings.Join(forbiddenContainers, ", "), + } + } + + return CheckResult{Allowed: true} +} diff --git a/staging/src/k8s.io/pod-security-admission/test/fixtures_hostProcess.go b/staging/src/k8s.io/pod-security-admission/test/fixtures_hostProcess.go new file mode 100644 index 00000000000..6f004a33c7c --- /dev/null +++ b/staging/src/k8s.io/pod-security-admission/test/fixtures_hostProcess.go @@ -0,0 +1,81 @@ +/* +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 test + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/component-base/featuregate" + "k8s.io/pod-security-admission/api" + "k8s.io/utils/pointer" +) + +/* +TODO: include field paths in reflect-based unit test + +podFields: []string{ + `securityContext.windowsOptions.hostProcess`, +}, +containerFields: []string{ + `securityContext.windowsOptions.hostProcess`, +}, + +*/ + +func init() { + + fixtureData_1_0 := fixtureGenerator{ + generatePass: func(p *corev1.Pod) []*corev1.Pod { + return []*corev1.Pod{p} + }, + failRequiresFeatures: []featuregate.Feature{"WindowsHostProcessContainers"}, + generateFail: func(p *corev1.Pod) []*corev1.Pod { + p = ensureSecurityContext(p) + if p.Spec.SecurityContext.WindowsOptions == nil { + p.Spec.SecurityContext.WindowsOptions = &corev1.WindowsSecurityContextOptions{} + } + if p.Spec.Containers[0].SecurityContext.WindowsOptions == nil { + p.Spec.Containers[0].SecurityContext.WindowsOptions = &corev1.WindowsSecurityContextOptions{} + } + if p.Spec.InitContainers[0].SecurityContext.WindowsOptions == nil { + p.Spec.InitContainers[0].SecurityContext.WindowsOptions = &corev1.WindowsSecurityContextOptions{} + } + return []*corev1.Pod{ + // true for pod + tweak(p, func(p *corev1.Pod) { + // HostNetwork is required to be true for HostProcess pods. + // Set to true here so we pass API validation and get to admission checks. + p.Spec.HostNetwork = true + p.Spec.SecurityContext.WindowsOptions.HostProcess = pointer.BoolPtr(true) + }), + // true for containers + tweak(p, func(p *corev1.Pod) { + // HostNetwork is required to be true for HostProcess pods. + // Set to true here so we pass API validation and get to admission checks. + p.Spec.HostNetwork = true + p.Spec.Containers[0].SecurityContext.WindowsOptions.HostProcess = pointer.BoolPtr(true) + p.Spec.InitContainers[0].SecurityContext.WindowsOptions.HostProcess = pointer.BoolPtr(true) + }), + } + }, + } + + registerFixtureGenerator( + fixtureKey{level: api.LevelBaseline, version: api.MajorMinorVersion(1, 0), check: "hostProcess"}, + 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 +} diff --git a/test/integration/auth/podsecurity_test.go b/test/integration/auth/podsecurity_test.go index baea7c03a63..c78d20d215b 100644 --- a/test/integration/auth/podsecurity_test.go +++ b/test/integration/auth/podsecurity_test.go @@ -32,6 +32,7 @@ import ( func TestPodSecurity(t *testing.T) { // Enable all feature gates needed to allow all fields to be exercised defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ProcMountType, true)() + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WindowsHostProcessContainers, true)() // Ensure the PodSecurity feature is enabled defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodSecurity, true)() // Start server