From 3733e209c91c1d25ab200d6abfccb6a021625aa8 Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Tue, 22 Jun 2021 14:12:12 -0400 Subject: [PATCH] PodSecurity: check: allowPrivilegeEscalation --- .../policy/check_allowPrivilegeEscalation.go | 76 +++++++++++++++++++ .../test/fixtures_allowPrivilegeEscalation.go | 65 ++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 staging/src/k8s.io/pod-security-admission/policy/check_allowPrivilegeEscalation.go create mode 100644 staging/src/k8s.io/pod-security-admission/test/fixtures_allowPrivilegeEscalation.go diff --git a/staging/src/k8s.io/pod-security-admission/policy/check_allowPrivilegeEscalation.go b/staging/src/k8s.io/pod-security-admission/policy/check_allowPrivilegeEscalation.go new file mode 100644 index 00000000000..c9f7c39a330 --- /dev/null +++ b/staging/src/k8s.io/pod-security-admission/policy/check_allowPrivilegeEscalation.go @@ -0,0 +1,76 @@ +/* +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" +) + +/* +Privilege escalation (such as via set-user-ID or set-group-ID file mode) should not be allowed. + +**Restricted Fields:** + +spec.containers[*].securityContext.allowPrivilegeEscalation +spec.initContainers[*].securityContext.allowPrivilegeEscalation + +**Allowed Values:** false +*/ + +func init() { + addCheck(CheckAllowPrivilegeEscalation) +} + +// CheckAllowPrivilegeEscalation returns a restricted level check +// that requires allowPrivilegeEscalation=false in 1.8+ +func CheckAllowPrivilegeEscalation() Check { + return Check{ + ID: "allowPrivilegeEscalation", + Level: api.LevelRestricted, + Versions: []VersionedCheck{ + { + // Field added in 1.8: + // https://github.com/kubernetes/kubernetes/blob/v1.8.0/staging/src/k8s.io/api/core/v1/types.go#L4797-L4804 + MinimumVersion: api.MajorMinorVersion(1, 8), + CheckPod: allowPrivilegeEscalation_1_8, + }, + }, + } +} + +func allowPrivilegeEscalation_1_8(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult { + var forbiddenPaths []string + visitContainersWithPath(podSpec, field.NewPath("spec"), func(container *corev1.Container, path *field.Path) { + if container.SecurityContext == nil || container.SecurityContext.AllowPrivilegeEscalation == nil || *container.SecurityContext.AllowPrivilegeEscalation { + forbiddenPaths = append(forbiddenPaths, path.Child("securityContext", "allowPrivilegeEscalation").String()) + } + }) + + if len(forbiddenPaths) > 0 { + return CheckResult{ + Allowed: false, + ForbiddenReason: "allowPrivilegeEscalation != false", + ForbiddenDetail: strings.Join(forbiddenPaths, ", "), + } + } + return CheckResult{Allowed: true} +} diff --git a/staging/src/k8s.io/pod-security-admission/test/fixtures_allowPrivilegeEscalation.go b/staging/src/k8s.io/pod-security-admission/test/fixtures_allowPrivilegeEscalation.go new file mode 100644 index 00000000000..7d2a0670b54 --- /dev/null +++ b/staging/src/k8s.io/pod-security-admission/test/fixtures_allowPrivilegeEscalation.go @@ -0,0 +1,65 @@ +/* +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/pod-security-admission/api" + "k8s.io/utils/pointer" +) + +/* +TODO: include field paths in reflect-based unit test + +containerFields: []string{ + `securityContext.allowPrivilegeEscalation`, +}, + +*/ + +func init() { + fixtureData_1_8 := fixtureGenerator{ + generatePass: func(p *corev1.Pod) []*corev1.Pod { + return []*corev1.Pod{ + // only valid pod is to explicitly set allowPrivilegeEscalation to false in all containers + p, + } + }, + generateFail: func(p *corev1.Pod) []*corev1.Pod { + return []*corev1.Pod{ + // explicit true + tweak(p, func(p *corev1.Pod) { + p.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = pointer.BoolPtr(true) + }), + tweak(p, func(p *corev1.Pod) { + p.Spec.InitContainers[0].SecurityContext.AllowPrivilegeEscalation = pointer.BoolPtr(true) + }), + // nil AllowPrivilegeEscalation + tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = nil }), + tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.AllowPrivilegeEscalation = nil }), + // nil security context + tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext = nil }), + tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext = nil }), + } + }, + } + + registerFixtureGenerator( + fixtureKey{level: api.LevelRestricted, version: api.MajorMinorVersion(1, 8), check: "allowPrivilegeEscalation"}, + fixtureData_1_8, + ) +}