PodSecurity: baseline hostProcess check

This commit is contained in:
Jordan Liggitt 2021-06-30 15:33:24 -04:00
parent e5135985fa
commit 49d31c45b1
3 changed files with 169 additions and 0 deletions

View File

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

View File

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

View File

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