PodSecurity: windowsHostProcess: cleanup

Rename to windowsHostProcess
Format reason/details
Add unit tests
This commit is contained in:
Jordan Liggitt 2021-07-07 20:55:36 -04:00
parent 45485bb7ae
commit 9dce1d6a49
3 changed files with 97 additions and 13 deletions

View File

@ -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 ")),
}
}

View File

@ -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)
}
})
}
}

View File

@ -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