mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 10:53:34 +00:00
rebase: update kubernetes to v1.25.0
update kubernetes to latest v1.25.0 release. Signed-off-by: Madhu Rajanna <madhupr007@gmail.com>
This commit is contained in:
committed by
mergify[bot]
parent
f47839d73d
commit
e3bf375035
53
vendor/k8s.io/pod-security-admission/api/helpers.go
generated
vendored
53
vendor/k8s.io/pod-security-admission/api/helpers.go
generated
vendored
@ -144,12 +144,65 @@ func (lv LevelVersion) String() string {
|
||||
return fmt.Sprintf("%s:%s", lv.Level, lv.Version)
|
||||
}
|
||||
|
||||
// Equivalent determines whether two LevelVersions are functionally equivalent. LevelVersions are
|
||||
// considered equivalent if both are privileged, or both levels & versions are equal.
|
||||
func (lv *LevelVersion) Equivalent(other *LevelVersion) bool {
|
||||
return (lv.Level == LevelPrivileged && other.Level == LevelPrivileged) ||
|
||||
(lv.Level == other.Level && lv.Version == other.Version)
|
||||
}
|
||||
|
||||
type Policy struct {
|
||||
Enforce LevelVersion
|
||||
Audit LevelVersion
|
||||
Warn LevelVersion
|
||||
}
|
||||
|
||||
func (p *Policy) String() string {
|
||||
return fmt.Sprintf("enforce=%#v, audit=%#v, warn=%#v", p.Enforce, p.Audit, p.Warn)
|
||||
}
|
||||
|
||||
// CompactString prints a minimalist representation of the policy that excludes any privileged
|
||||
// levels.
|
||||
func (p *Policy) CompactString() string {
|
||||
sb := strings.Builder{}
|
||||
if p.Enforce.Level != LevelPrivileged {
|
||||
sb.WriteString("enforce=")
|
||||
sb.WriteString(p.Enforce.String())
|
||||
}
|
||||
if p.Audit.Level != LevelPrivileged {
|
||||
if sb.Len() > 0 {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
sb.WriteString("audit=")
|
||||
sb.WriteString(p.Audit.String())
|
||||
}
|
||||
if p.Warn.Level != LevelPrivileged {
|
||||
if sb.Len() > 0 {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
sb.WriteString("warn=")
|
||||
sb.WriteString(p.Warn.String())
|
||||
}
|
||||
if sb.Len() == 0 {
|
||||
// All modes were privileged, just output "privileged".
|
||||
return string(LevelPrivileged)
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// Equivalent determines whether two policies are functionally equivalent. Policies are considered
|
||||
// equivalent if all 3 modes are considered equivalent.
|
||||
func (p *Policy) Equivalent(other *Policy) bool {
|
||||
return p.Enforce.Equivalent(&other.Enforce) && p.Audit.Equivalent(&other.Audit) && p.Warn.Equivalent(&other.Warn)
|
||||
}
|
||||
|
||||
// FullyPrivileged returns true if all 3 policy modes are privileged.
|
||||
func (p *Policy) FullyPrivileged() bool {
|
||||
return p.Enforce.Level == LevelPrivileged &&
|
||||
p.Audit.Level == LevelPrivileged &&
|
||||
p.Warn.Level == LevelPrivileged
|
||||
}
|
||||
|
||||
// PolicyToEvaluate resolves the PodSecurity namespace labels to the policy for that namespace,
|
||||
// falling back to the provided defaults when a label is unspecified. A valid policy is always
|
||||
// returned, even when an error is returned. If labels cannot be parsed correctly, the values of
|
||||
|
93
vendor/k8s.io/pod-security-admission/policy/check_allowPrivilegeEscalation.go
generated
vendored
Normal file
93
vendor/k8s.io/pod-security-admission/policy/check_allowPrivilegeEscalation.go
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"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,
|
||||
},
|
||||
{
|
||||
// Starting 1.25, windows pods would be exempted from this check using pod.spec.os field when set to windows.
|
||||
MinimumVersion: api.MajorMinorVersion(1, 25),
|
||||
CheckPod: allowPrivilegeEscalation_1_25,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func allowPrivilegeEscalation_1_8(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
var badContainers []string
|
||||
visitContainers(podSpec, func(container *corev1.Container) {
|
||||
if container.SecurityContext == nil || container.SecurityContext.AllowPrivilegeEscalation == nil || *container.SecurityContext.AllowPrivilegeEscalation {
|
||||
badContainers = append(badContainers, container.Name)
|
||||
}
|
||||
})
|
||||
|
||||
if len(badContainers) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "allowPrivilegeEscalation != false",
|
||||
ForbiddenDetail: fmt.Sprintf(
|
||||
"%s %s must set securityContext.allowPrivilegeEscalation=false",
|
||||
pluralize("container", "containers", len(badContainers)),
|
||||
joinQuote(badContainers),
|
||||
),
|
||||
}
|
||||
}
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
||||
|
||||
func allowPrivilegeEscalation_1_25(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
// Pod API validation would have failed if podOS == Windows and if privilegeEscalation has been set.
|
||||
// We can admit the Windows pod even if privilegeEscalation has not been set.
|
||||
if podSpec.OS != nil && podSpec.OS.Name == corev1.Windows {
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
||||
return allowPrivilegeEscalation_1_8(podMetadata, podSpec)
|
||||
}
|
81
vendor/k8s.io/pod-security-admission/policy/check_appArmorProfile.go
generated
vendored
Normal file
81
vendor/k8s.io/pod-security-admission/policy/check_appArmorProfile.go
generated
vendored
Normal 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 policy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
On supported hosts, the 'runtime/default' AppArmor profile is applied by default.
|
||||
The baseline policy should prevent overriding or disabling the default AppArmor
|
||||
profile, or restrict overrides to an allowed set of profiles.
|
||||
|
||||
**Restricted Fields:**
|
||||
metadata.annotations['container.apparmor.security.beta.kubernetes.io/*']
|
||||
|
||||
**Allowed Values:** 'runtime/default', 'localhost/*', empty, undefined
|
||||
*/
|
||||
func init() {
|
||||
addCheck(CheckAppArmorProfile)
|
||||
}
|
||||
|
||||
// CheckAppArmorProfile returns a baseline level check
|
||||
// that limits the value of AppArmor profiles in 1.0+
|
||||
func CheckAppArmorProfile() Check {
|
||||
return Check{
|
||||
ID: "appArmorProfile",
|
||||
Level: api.LevelBaseline,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 0),
|
||||
CheckPod: appArmorProfile_1_0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func allowedProfile(profile string) bool {
|
||||
return len(profile) == 0 ||
|
||||
profile == corev1.AppArmorBetaProfileRuntimeDefault ||
|
||||
strings.HasPrefix(profile, corev1.AppArmorBetaProfileNamePrefix)
|
||||
}
|
||||
|
||||
func appArmorProfile_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
var forbiddenValues []string
|
||||
for k, v := range podMetadata.Annotations {
|
||||
if strings.HasPrefix(k, corev1.AppArmorBetaContainerAnnotationKeyPrefix) && !allowedProfile(v) {
|
||||
forbiddenValues = append(forbiddenValues, fmt.Sprintf("%s=%q", k, v))
|
||||
}
|
||||
}
|
||||
if len(forbiddenValues) > 0 {
|
||||
sort.Strings(forbiddenValues)
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: pluralize("forbidden AppArmor profile", "forbidden AppArmor profiles", len(forbiddenValues)),
|
||||
ForbiddenDetail: strings.Join(forbiddenValues, ", "),
|
||||
}
|
||||
}
|
||||
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
110
vendor/k8s.io/pod-security-admission/policy/check_capabilities_baseline.go
generated
vendored
Normal file
110
vendor/k8s.io/pod-security-admission/policy/check_capabilities_baseline.go
generated
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
Adding NET_RAW or capabilities beyond the default set must be disallowed.
|
||||
|
||||
**Restricted Fields:**
|
||||
spec.containers[*].securityContext.capabilities.add
|
||||
spec.initContainers[*].securityContext.capabilities.add
|
||||
|
||||
**Allowed Values:**
|
||||
undefined / empty
|
||||
values from the default set "AUDIT_WRITE", "CHOWN", "DAC_OVERRIDE","FOWNER", "FSETID", "KILL", "MKNOD", "NET_BIND_SERVICE", "SETFCAP", "SETGID", "SETPCAP", "SETUID", "SYS_CHROOT"
|
||||
*/
|
||||
|
||||
func init() {
|
||||
addCheck(CheckCapabilitiesBaseline)
|
||||
}
|
||||
|
||||
const checkCapabilitiesBaselineID CheckID = "capabilities_baseline"
|
||||
|
||||
// CheckCapabilitiesBaseline returns a baseline level check
|
||||
// that limits the capabilities that can be added in 1.0+
|
||||
func CheckCapabilitiesBaseline() Check {
|
||||
return Check{
|
||||
ID: checkCapabilitiesBaselineID,
|
||||
Level: api.LevelBaseline,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 0),
|
||||
CheckPod: capabilitiesBaseline_1_0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
capabilities_allowed_1_0 = sets.NewString(
|
||||
"AUDIT_WRITE",
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FOWNER",
|
||||
"FSETID",
|
||||
"KILL",
|
||||
"MKNOD",
|
||||
"NET_BIND_SERVICE",
|
||||
"SETFCAP",
|
||||
"SETGID",
|
||||
"SETPCAP",
|
||||
"SETUID",
|
||||
"SYS_CHROOT",
|
||||
)
|
||||
)
|
||||
|
||||
func capabilitiesBaseline_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
var badContainers []string
|
||||
nonDefaultCapabilities := sets.NewString()
|
||||
visitContainers(podSpec, func(container *corev1.Container) {
|
||||
if container.SecurityContext != nil && container.SecurityContext.Capabilities != nil {
|
||||
valid := true
|
||||
for _, c := range container.SecurityContext.Capabilities.Add {
|
||||
if !capabilities_allowed_1_0.Has(string(c)) {
|
||||
valid = false
|
||||
nonDefaultCapabilities.Insert(string(c))
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
badContainers = append(badContainers, container.Name)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if len(badContainers) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "non-default capabilities",
|
||||
ForbiddenDetail: fmt.Sprintf(
|
||||
"%s %s must not include %s in securityContext.capabilities.add",
|
||||
pluralize("container", "containers", len(badContainers)),
|
||||
joinQuote(badContainers),
|
||||
joinQuote(nonDefaultCapabilities.List()),
|
||||
),
|
||||
}
|
||||
}
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
145
vendor/k8s.io/pod-security-admission/policy/check_capabilities_restricted.go
generated
vendored
Normal file
145
vendor/k8s.io/pod-security-admission/policy/check_capabilities_restricted.go
generated
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
const (
|
||||
capabilityAll = "ALL"
|
||||
capabilityNetBindService = "NET_BIND_SERVICE"
|
||||
)
|
||||
|
||||
/*
|
||||
Containers must drop ALL, and may only add NET_BIND_SERVICE.
|
||||
|
||||
**Restricted Fields:**
|
||||
spec.containers[*].securityContext.capabilities.drop
|
||||
spec.initContainers[*].securityContext.capabilities.drop
|
||||
|
||||
**Allowed Values:**
|
||||
Must include "ALL"
|
||||
|
||||
**Restricted Fields:**
|
||||
spec.containers[*].securityContext.capabilities.add
|
||||
spec.initContainers[*].securityContext.capabilities.add
|
||||
|
||||
**Allowed Values:**
|
||||
undefined / empty
|
||||
"NET_BIND_SERVICE"
|
||||
*/
|
||||
|
||||
func init() {
|
||||
addCheck(CheckCapabilitiesRestricted)
|
||||
}
|
||||
|
||||
// CheckCapabilitiesRestricted returns a restricted level check
|
||||
// that ensures ALL capabilities are dropped in 1.22+
|
||||
func CheckCapabilitiesRestricted() Check {
|
||||
return Check{
|
||||
ID: "capabilities_restricted",
|
||||
Level: api.LevelRestricted,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 22),
|
||||
CheckPod: capabilitiesRestricted_1_22,
|
||||
OverrideCheckIDs: []CheckID{checkCapabilitiesBaselineID},
|
||||
},
|
||||
// Starting 1.25, windows pods would be exempted from this check using pod.spec.os field when set to windows.
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 25),
|
||||
CheckPod: capabilitiesRestricted_1_25,
|
||||
OverrideCheckIDs: []CheckID{checkCapabilitiesBaselineID},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func capabilitiesRestricted_1_22(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
var (
|
||||
containersMissingDropAll []string
|
||||
containersAddingForbidden []string
|
||||
forbiddenCapabilities = sets.NewString()
|
||||
)
|
||||
|
||||
visitContainers(podSpec, func(container *corev1.Container) {
|
||||
if container.SecurityContext == nil || container.SecurityContext.Capabilities == nil {
|
||||
containersMissingDropAll = append(containersMissingDropAll, container.Name)
|
||||
return
|
||||
}
|
||||
|
||||
droppedAll := false
|
||||
for _, c := range container.SecurityContext.Capabilities.Drop {
|
||||
if c == capabilityAll {
|
||||
droppedAll = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !droppedAll {
|
||||
containersMissingDropAll = append(containersMissingDropAll, container.Name)
|
||||
}
|
||||
|
||||
addedForbidden := false
|
||||
for _, c := range container.SecurityContext.Capabilities.Add {
|
||||
if c != capabilityNetBindService {
|
||||
addedForbidden = true
|
||||
forbiddenCapabilities.Insert(string(c))
|
||||
}
|
||||
}
|
||||
if addedForbidden {
|
||||
containersAddingForbidden = append(containersAddingForbidden, container.Name)
|
||||
}
|
||||
})
|
||||
var forbiddenDetails []string
|
||||
if len(containersMissingDropAll) > 0 {
|
||||
forbiddenDetails = append(forbiddenDetails, fmt.Sprintf(
|
||||
`%s %s must set securityContext.capabilities.drop=["ALL"]`,
|
||||
pluralize("container", "containers", len(containersMissingDropAll)),
|
||||
joinQuote(containersMissingDropAll)))
|
||||
}
|
||||
if len(containersAddingForbidden) > 0 {
|
||||
forbiddenDetails = append(forbiddenDetails, fmt.Sprintf(
|
||||
`%s %s must not include %s in securityContext.capabilities.add`,
|
||||
pluralize("container", "containers", len(containersAddingForbidden)),
|
||||
joinQuote(containersAddingForbidden),
|
||||
joinQuote(forbiddenCapabilities.List())))
|
||||
}
|
||||
if len(forbiddenDetails) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "unrestricted capabilities",
|
||||
ForbiddenDetail: strings.Join(forbiddenDetails, "; "),
|
||||
}
|
||||
}
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
||||
|
||||
func capabilitiesRestricted_1_25(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
// Pod API validation would have failed if podOS == Windows and if capabilities have been set.
|
||||
// We can admit the Windows pod even if capabilities has not been set.
|
||||
if podSpec.OS != nil && podSpec.OS.Name == corev1.Windows {
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
||||
return capabilitiesRestricted_1_22(podMetadata, podSpec)
|
||||
}
|
82
vendor/k8s.io/pod-security-admission/policy/check_hostNamespaces.go
generated
vendored
Normal file
82
vendor/k8s.io/pod-security-admission/policy/check_hostNamespaces.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
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/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
Sharing the host network, PID, and IPC namespaces must be disallowed.
|
||||
|
||||
**Restricted Fields:**
|
||||
|
||||
spec.hostNetwork
|
||||
spec.hostPID
|
||||
spec.hostIPC
|
||||
|
||||
**Allowed Values:** undefined, false
|
||||
*/
|
||||
|
||||
func init() {
|
||||
addCheck(CheckHostNamespaces)
|
||||
}
|
||||
|
||||
// CheckHostNamespaces returns a baseline level check
|
||||
// that prohibits host namespaces in 1.0+
|
||||
func CheckHostNamespaces() Check {
|
||||
return Check{
|
||||
ID: "hostNamespaces",
|
||||
Level: api.LevelBaseline,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 0),
|
||||
CheckPod: hostNamespaces_1_0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func hostNamespaces_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
var hostNamespaces []string
|
||||
|
||||
if podSpec.HostNetwork {
|
||||
hostNamespaces = append(hostNamespaces, "hostNetwork=true")
|
||||
}
|
||||
|
||||
if podSpec.HostPID {
|
||||
hostNamespaces = append(hostNamespaces, "hostPID=true")
|
||||
}
|
||||
|
||||
if podSpec.HostIPC {
|
||||
hostNamespaces = append(hostNamespaces, "hostIPC=true")
|
||||
}
|
||||
|
||||
if len(hostNamespaces) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "host namespaces",
|
||||
ForbiddenDetail: strings.Join(hostNamespaces, ", "),
|
||||
}
|
||||
}
|
||||
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
76
vendor/k8s.io/pod-security-admission/policy/check_hostPathVolumes.go
generated
vendored
Normal file
76
vendor/k8s.io/pod-security-admission/policy/check_hostPathVolumes.go
generated
vendored
Normal file
@ -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 (
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
HostPath volumes must be forbidden.
|
||||
|
||||
**Restricted Fields:**
|
||||
|
||||
spec.volumes[*].hostPath
|
||||
|
||||
**Allowed Values:** undefined/null
|
||||
*/
|
||||
|
||||
func init() {
|
||||
addCheck(CheckHostPathVolumes)
|
||||
}
|
||||
|
||||
const checkHostPathVolumesID CheckID = "hostPathVolumes"
|
||||
|
||||
// CheckHostPathVolumes returns a baseline level check
|
||||
// that requires hostPath=undefined/null in 1.0+
|
||||
func CheckHostPathVolumes() Check {
|
||||
return Check{
|
||||
ID: checkHostPathVolumesID,
|
||||
Level: api.LevelBaseline,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 0),
|
||||
CheckPod: hostPathVolumes_1_0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func hostPathVolumes_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
var hostVolumes []string
|
||||
|
||||
for _, volume := range podSpec.Volumes {
|
||||
if volume.HostPath != nil {
|
||||
hostVolumes = append(hostVolumes, volume.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if len(hostVolumes) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "hostPath volumes",
|
||||
ForbiddenDetail: fmt.Sprintf("%s %s", pluralize("volume", "volumes", len(hostVolumes)), joinQuote(hostVolumes)),
|
||||
}
|
||||
}
|
||||
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
91
vendor/k8s.io/pod-security-admission/policy/check_hostPorts.go
generated
vendored
Normal file
91
vendor/k8s.io/pod-security-admission/policy/check_hostPorts.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
HostPort ports must be forbidden.
|
||||
|
||||
**Restricted Fields:**
|
||||
|
||||
spec.containers[*].ports[*].hostPort
|
||||
spec.initContainers[*].ports[*].hostPort
|
||||
|
||||
**Allowed Values:** undefined/0
|
||||
*/
|
||||
|
||||
func init() {
|
||||
addCheck(CheckHostPorts)
|
||||
}
|
||||
|
||||
// CheckHostPorts returns a baseline level check
|
||||
// that forbids any host ports in 1.0+
|
||||
func CheckHostPorts() Check {
|
||||
return Check{
|
||||
ID: "hostPorts",
|
||||
Level: api.LevelBaseline,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 0),
|
||||
CheckPod: hostPorts_1_0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func hostPorts_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
var badContainers []string
|
||||
forbiddenHostPorts := sets.NewString()
|
||||
visitContainers(podSpec, func(container *corev1.Container) {
|
||||
valid := true
|
||||
for _, c := range container.Ports {
|
||||
if c.HostPort != 0 {
|
||||
valid = false
|
||||
forbiddenHostPorts.Insert(strconv.Itoa(int(c.HostPort)))
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
badContainers = append(badContainers, container.Name)
|
||||
}
|
||||
})
|
||||
|
||||
if len(badContainers) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "hostPort",
|
||||
ForbiddenDetail: fmt.Sprintf(
|
||||
"%s %s %s %s %s",
|
||||
pluralize("container", "containers", len(badContainers)),
|
||||
joinQuote(badContainers),
|
||||
pluralize("uses", "use", len(badContainers)),
|
||||
pluralize("hostPort", "hostPorts", len(forbiddenHostPorts)),
|
||||
strings.Join(forbiddenHostPorts.List(), ", "),
|
||||
),
|
||||
}
|
||||
}
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
75
vendor/k8s.io/pod-security-admission/policy/check_privileged.go
generated
vendored
Normal file
75
vendor/k8s.io/pod-security-admission/policy/check_privileged.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
Privileged Pods disable most security mechanisms and must be disallowed.
|
||||
|
||||
Restricted Fields:
|
||||
spec.containers[*].securityContext.privileged
|
||||
spec.initContainers[*].securityContext.privileged
|
||||
|
||||
Allowed Values: false, undefined/null
|
||||
*/
|
||||
|
||||
func init() {
|
||||
addCheck(CheckPrivileged)
|
||||
}
|
||||
|
||||
// CheckPrivileged returns a baseline level check
|
||||
// that forbids privileged=true in 1.0+
|
||||
func CheckPrivileged() Check {
|
||||
return Check{
|
||||
ID: "privileged",
|
||||
Level: api.LevelBaseline,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 0),
|
||||
CheckPod: privileged_1_0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func privileged_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
var badContainers []string
|
||||
visitContainers(podSpec, func(container *corev1.Container) {
|
||||
if container.SecurityContext != nil && container.SecurityContext.Privileged != nil && *container.SecurityContext.Privileged {
|
||||
badContainers = append(badContainers, container.Name)
|
||||
}
|
||||
})
|
||||
if len(badContainers) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "privileged",
|
||||
ForbiddenDetail: fmt.Sprintf(
|
||||
`%s %s must not set securityContext.privileged=true`,
|
||||
pluralize("container", "containers", len(badContainers)),
|
||||
joinQuote(badContainers),
|
||||
),
|
||||
}
|
||||
}
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
91
vendor/k8s.io/pod-security-admission/policy/check_procMount.go
generated
vendored
Normal file
91
vendor/k8s.io/pod-security-admission/policy/check_procMount.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
The default /proc masks are set up to reduce attack surface, and should be required.
|
||||
|
||||
**Restricted Fields:**
|
||||
spec.containers[*].securityContext.procMount
|
||||
spec.initContainers[*].securityContext.procMount
|
||||
|
||||
**Allowed Values:** undefined/null, "Default"
|
||||
|
||||
*/
|
||||
|
||||
func init() {
|
||||
addCheck(CheckProcMount)
|
||||
}
|
||||
|
||||
// CheckProcMount returns a baseline level check that restricts
|
||||
// setting the value of securityContext.procMount to DefaultProcMount
|
||||
// in 1.0+
|
||||
func CheckProcMount() Check {
|
||||
return Check{
|
||||
ID: "procMount",
|
||||
Level: api.LevelBaseline,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 0),
|
||||
CheckPod: procMount_1_0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func procMount_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
var badContainers []string
|
||||
forbiddenProcMountTypes := sets.NewString()
|
||||
visitContainers(podSpec, func(container *corev1.Container) {
|
||||
// allow if the security context is nil.
|
||||
if container.SecurityContext == nil {
|
||||
return
|
||||
}
|
||||
// allow if proc mount is not set.
|
||||
if container.SecurityContext.ProcMount == nil {
|
||||
return
|
||||
}
|
||||
// check if the value of the proc mount type is valid.
|
||||
if *container.SecurityContext.ProcMount != corev1.DefaultProcMount {
|
||||
badContainers = append(badContainers, container.Name)
|
||||
forbiddenProcMountTypes.Insert(string(*container.SecurityContext.ProcMount))
|
||||
}
|
||||
})
|
||||
if len(badContainers) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "procMount",
|
||||
ForbiddenDetail: fmt.Sprintf(
|
||||
"%s %s must not set securityContext.procMount to %s",
|
||||
pluralize("container", "containers", len(badContainers)),
|
||||
joinQuote(badContainers),
|
||||
joinQuote(forbiddenProcMountTypes.List()),
|
||||
),
|
||||
}
|
||||
}
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
171
vendor/k8s.io/pod-security-admission/policy/check_restrictedVolumes.go
generated
vendored
Normal file
171
vendor/k8s.io/pod-security-admission/policy/check_restrictedVolumes.go
generated
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
In addition to restricting HostPath volumes, the restricted profile
|
||||
limits usage of inline pod volume sources to:
|
||||
* configMap
|
||||
* downwardAPI
|
||||
* emptyDir
|
||||
* projected
|
||||
* secret
|
||||
* csi
|
||||
* persistentVolumeClaim
|
||||
* ephemeral
|
||||
|
||||
**Restricted Fields:**
|
||||
|
||||
spec.volumes[*].hostPath
|
||||
spec.volumes[*].gcePersistentDisk
|
||||
spec.volumes[*].awsElasticBlockStore
|
||||
spec.volumes[*].gitRepo
|
||||
spec.volumes[*].nfs
|
||||
spec.volumes[*].iscsi
|
||||
spec.volumes[*].glusterfs
|
||||
spec.volumes[*].rbd
|
||||
spec.volumes[*].flexVolume
|
||||
spec.volumes[*].cinder
|
||||
spec.volumes[*].cephfs
|
||||
spec.volumes[*].flocker
|
||||
spec.volumes[*].fc
|
||||
spec.volumes[*].azureFile
|
||||
spec.volumes[*].vsphereVolume
|
||||
spec.volumes[*].quobyte
|
||||
spec.volumes[*].azureDisk
|
||||
spec.volumes[*].portworxVolume
|
||||
spec.volumes[*].photonPersistentDisk
|
||||
spec.volumes[*].scaleIO
|
||||
spec.volumes[*].storageos
|
||||
|
||||
**Allowed Values:** undefined/null
|
||||
*/
|
||||
|
||||
func init() {
|
||||
addCheck(CheckRestrictedVolumes)
|
||||
}
|
||||
|
||||
// CheckRestrictedVolumes returns a restricted level check
|
||||
// that limits usage of specific volume types in 1.0+
|
||||
func CheckRestrictedVolumes() Check {
|
||||
return Check{
|
||||
ID: "restrictedVolumes",
|
||||
Level: api.LevelRestricted,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 0),
|
||||
CheckPod: restrictedVolumes_1_0,
|
||||
OverrideCheckIDs: []CheckID{checkHostPathVolumesID},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func restrictedVolumes_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
var badVolumes []string
|
||||
badVolumeTypes := sets.NewString()
|
||||
|
||||
for _, volume := range podSpec.Volumes {
|
||||
switch {
|
||||
case volume.ConfigMap != nil,
|
||||
volume.CSI != nil,
|
||||
volume.DownwardAPI != nil,
|
||||
volume.EmptyDir != nil,
|
||||
volume.Ephemeral != nil,
|
||||
volume.PersistentVolumeClaim != nil,
|
||||
volume.Projected != nil,
|
||||
volume.Secret != nil:
|
||||
continue
|
||||
|
||||
default:
|
||||
badVolumes = append(badVolumes, volume.Name)
|
||||
|
||||
switch {
|
||||
case volume.HostPath != nil:
|
||||
badVolumeTypes.Insert("hostPath")
|
||||
case volume.GCEPersistentDisk != nil:
|
||||
badVolumeTypes.Insert("gcePersistentDisk")
|
||||
case volume.AWSElasticBlockStore != nil:
|
||||
badVolumeTypes.Insert("awsElasticBlockStore")
|
||||
case volume.GitRepo != nil:
|
||||
badVolumeTypes.Insert("gitRepo")
|
||||
case volume.NFS != nil:
|
||||
badVolumeTypes.Insert("nfs")
|
||||
case volume.ISCSI != nil:
|
||||
badVolumeTypes.Insert("iscsi")
|
||||
case volume.Glusterfs != nil:
|
||||
badVolumeTypes.Insert("glusterfs")
|
||||
case volume.RBD != nil:
|
||||
badVolumeTypes.Insert("rbd")
|
||||
case volume.FlexVolume != nil:
|
||||
badVolumeTypes.Insert("flexVolume")
|
||||
case volume.Cinder != nil:
|
||||
badVolumeTypes.Insert("cinder")
|
||||
case volume.CephFS != nil:
|
||||
badVolumeTypes.Insert("cephfs")
|
||||
case volume.Flocker != nil:
|
||||
badVolumeTypes.Insert("flocker")
|
||||
case volume.FC != nil:
|
||||
badVolumeTypes.Insert("fc")
|
||||
case volume.AzureFile != nil:
|
||||
badVolumeTypes.Insert("azureFile")
|
||||
case volume.VsphereVolume != nil:
|
||||
badVolumeTypes.Insert("vsphereVolume")
|
||||
case volume.Quobyte != nil:
|
||||
badVolumeTypes.Insert("quobyte")
|
||||
case volume.AzureDisk != nil:
|
||||
badVolumeTypes.Insert("azureDisk")
|
||||
case volume.PhotonPersistentDisk != nil:
|
||||
badVolumeTypes.Insert("photonPersistentDisk")
|
||||
case volume.PortworxVolume != nil:
|
||||
badVolumeTypes.Insert("portworxVolume")
|
||||
case volume.ScaleIO != nil:
|
||||
badVolumeTypes.Insert("scaleIO")
|
||||
case volume.StorageOS != nil:
|
||||
badVolumeTypes.Insert("storageos")
|
||||
default:
|
||||
badVolumeTypes.Insert("unknown")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(badVolumes) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "restricted volume types",
|
||||
ForbiddenDetail: fmt.Sprintf(
|
||||
"%s %s %s %s %s",
|
||||
pluralize("volume", "volumes", len(badVolumes)),
|
||||
joinQuote(badVolumes),
|
||||
pluralize("uses", "use", len(badVolumes)),
|
||||
pluralize("restricted volume type", "restricted volume types", len(badVolumeTypes)),
|
||||
joinQuote(badVolumeTypes.List()),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
128
vendor/k8s.io/pod-security-admission/policy/check_runAsNonRoot.go
generated
vendored
Normal file
128
vendor/k8s.io/pod-security-admission/policy/check_runAsNonRoot.go
generated
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
Containers must be required to run as non-root users.
|
||||
|
||||
**Restricted Fields:**
|
||||
|
||||
spec.securityContext.runAsNonRoot
|
||||
spec.containers[*].securityContext.runAsNonRoot
|
||||
spec.initContainers[*].securityContext.runAsNonRoot
|
||||
|
||||
**Allowed Values:**
|
||||
true
|
||||
undefined/null at container-level if pod-level is set to true
|
||||
*/
|
||||
|
||||
func init() {
|
||||
addCheck(CheckRunAsNonRoot)
|
||||
}
|
||||
|
||||
// CheckRunAsNonRoot returns a restricted level check
|
||||
// that requires runAsNonRoot=true in 1.0+
|
||||
func CheckRunAsNonRoot() Check {
|
||||
return Check{
|
||||
ID: "runAsNonRoot",
|
||||
Level: api.LevelRestricted,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 0),
|
||||
CheckPod: runAsNonRoot_1_0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func runAsNonRoot_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
// things that explicitly set runAsNonRoot=false
|
||||
var badSetters []string
|
||||
|
||||
podRunAsNonRoot := false
|
||||
if podSpec.SecurityContext != nil && podSpec.SecurityContext.RunAsNonRoot != nil {
|
||||
if !*podSpec.SecurityContext.RunAsNonRoot {
|
||||
badSetters = append(badSetters, "pod")
|
||||
} else {
|
||||
podRunAsNonRoot = true
|
||||
}
|
||||
}
|
||||
|
||||
// containers that explicitly set runAsNonRoot=false
|
||||
var explicitlyBadContainers []string
|
||||
// containers that didn't set runAsNonRoot and aren't caught by a pod-level runAsNonRoot=true
|
||||
var implicitlyBadContainers []string
|
||||
|
||||
visitContainers(podSpec, func(container *corev1.Container) {
|
||||
if container.SecurityContext != nil && container.SecurityContext.RunAsNonRoot != nil {
|
||||
// container explicitly set runAsNonRoot
|
||||
if !*container.SecurityContext.RunAsNonRoot {
|
||||
// container explicitly set runAsNonRoot to a bad value
|
||||
explicitlyBadContainers = append(explicitlyBadContainers, container.Name)
|
||||
}
|
||||
} else {
|
||||
// container did not explicitly set runAsNonRoot
|
||||
if !podRunAsNonRoot {
|
||||
// no pod-level runAsNonRoot=true, so this container implicitly has a bad value
|
||||
implicitlyBadContainers = append(implicitlyBadContainers, container.Name)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if len(explicitlyBadContainers) > 0 {
|
||||
badSetters = append(
|
||||
badSetters,
|
||||
fmt.Sprintf(
|
||||
"%s %s",
|
||||
pluralize("container", "containers", len(explicitlyBadContainers)),
|
||||
joinQuote(explicitlyBadContainers),
|
||||
),
|
||||
)
|
||||
}
|
||||
// pod or containers explicitly set runAsNonRoot=false
|
||||
if len(badSetters) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "runAsNonRoot != true",
|
||||
ForbiddenDetail: fmt.Sprintf("%s must not set securityContext.runAsNonRoot=false", strings.Join(badSetters, " and ")),
|
||||
}
|
||||
}
|
||||
|
||||
// pod didn't set runAsNonRoot and not all containers opted into runAsNonRoot
|
||||
if len(implicitlyBadContainers) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "runAsNonRoot != true",
|
||||
ForbiddenDetail: fmt.Sprintf(
|
||||
"pod or %s %s must set securityContext.runAsNonRoot=true",
|
||||
pluralize("container", "containers", len(implicitlyBadContainers)),
|
||||
joinQuote(implicitlyBadContainers),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
99
vendor/k8s.io/pod-security-admission/policy/check_runAsUser.go
generated
vendored
Normal file
99
vendor/k8s.io/pod-security-admission/policy/check_runAsUser.go
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
Containers must not set runAsUser: 0
|
||||
|
||||
**Restricted Fields:**
|
||||
|
||||
spec.securityContext.runAsUser
|
||||
spec.containers[*].securityContext.runAsUser
|
||||
spec.initContainers[*].securityContext.runAsUser
|
||||
|
||||
**Allowed Values:**
|
||||
non-zero values
|
||||
undefined/null
|
||||
|
||||
*/
|
||||
|
||||
func init() {
|
||||
addCheck(CheckRunAsUser)
|
||||
}
|
||||
|
||||
// CheckRunAsUser returns a restricted level check
|
||||
// that forbides runAsUser=0 in 1.23+
|
||||
func CheckRunAsUser() Check {
|
||||
return Check{
|
||||
ID: "runAsUser",
|
||||
Level: api.LevelRestricted,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 23),
|
||||
CheckPod: runAsUser_1_23,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func runAsUser_1_23(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
// things that explicitly set runAsUser=0
|
||||
var badSetters []string
|
||||
|
||||
if podSpec.SecurityContext != nil && podSpec.SecurityContext.RunAsUser != nil && *podSpec.SecurityContext.RunAsUser == 0 {
|
||||
badSetters = append(badSetters, "pod")
|
||||
}
|
||||
|
||||
// containers that explicitly set runAsUser=0
|
||||
var explicitlyBadContainers []string
|
||||
|
||||
visitContainers(podSpec, func(container *corev1.Container) {
|
||||
if container.SecurityContext != nil && container.SecurityContext.RunAsUser != nil && *container.SecurityContext.RunAsUser == 0 {
|
||||
explicitlyBadContainers = append(explicitlyBadContainers, container.Name)
|
||||
}
|
||||
})
|
||||
|
||||
if len(explicitlyBadContainers) > 0 {
|
||||
badSetters = append(
|
||||
badSetters,
|
||||
fmt.Sprintf(
|
||||
"%s %s",
|
||||
pluralize("container", "containers", len(explicitlyBadContainers)),
|
||||
joinQuote(explicitlyBadContainers),
|
||||
),
|
||||
)
|
||||
}
|
||||
// pod or containers explicitly set runAsUser=0
|
||||
if len(badSetters) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "runAsUser=0",
|
||||
ForbiddenDetail: fmt.Sprintf("%s must not set runAsUser=0", strings.Join(badSetters, " and ")),
|
||||
}
|
||||
}
|
||||
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
159
vendor/k8s.io/pod-security-admission/policy/check_seLinuxOptions.go
generated
vendored
Normal file
159
vendor/k8s.io/pod-security-admission/policy/check_seLinuxOptions.go
generated
vendored
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
Setting the SELinux type is restricted, and setting a custom SELinux user or role option is forbidden.
|
||||
|
||||
**Restricted Fields:**
|
||||
spec.securityContext.seLinuxOptions.type
|
||||
spec.containers[*].securityContext.seLinuxOptions.type
|
||||
spec.initContainers[*].securityContext.seLinuxOptions.type
|
||||
|
||||
**Allowed Values:**
|
||||
undefined/empty
|
||||
container_t
|
||||
container_init_t
|
||||
container_kvm_t
|
||||
|
||||
**Restricted Fields:**
|
||||
spec.securityContext.seLinuxOptions.user
|
||||
spec.containers[*].securityContext.seLinuxOptions.user
|
||||
spec.initContainers[*].securityContext.seLinuxOptions.user
|
||||
spec.securityContext.seLinuxOptions.role
|
||||
spec.containers[*].securityContext.seLinuxOptions.role
|
||||
spec.initContainers[*].securityContext.seLinuxOptions.role
|
||||
|
||||
**Allowed Values:** undefined/empty
|
||||
*/
|
||||
|
||||
func init() {
|
||||
addCheck(CheckSELinuxOptions)
|
||||
}
|
||||
|
||||
// CheckSELinuxOptions returns a baseline level check
|
||||
// that limits seLinuxOptions type, user, and role values in 1.0+
|
||||
func CheckSELinuxOptions() Check {
|
||||
return Check{
|
||||
ID: "seLinuxOptions",
|
||||
Level: api.LevelBaseline,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 0),
|
||||
CheckPod: seLinuxOptions_1_0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
selinux_allowed_types_1_0 = sets.NewString("", "container_t", "container_init_t", "container_kvm_t")
|
||||
)
|
||||
|
||||
func seLinuxOptions_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
var (
|
||||
// sources that set bad seLinuxOptions
|
||||
badSetters []string
|
||||
|
||||
// invalid type values set
|
||||
badTypes = sets.NewString()
|
||||
// was user set?
|
||||
setUser = false
|
||||
// was role set?
|
||||
setRole = false
|
||||
)
|
||||
|
||||
validSELinuxOptions := func(opts *corev1.SELinuxOptions) bool {
|
||||
valid := true
|
||||
if !selinux_allowed_types_1_0.Has(opts.Type) {
|
||||
valid = false
|
||||
badTypes.Insert(opts.Type)
|
||||
}
|
||||
if len(opts.User) > 0 {
|
||||
valid = false
|
||||
setUser = true
|
||||
}
|
||||
if len(opts.Role) > 0 {
|
||||
valid = false
|
||||
setRole = true
|
||||
}
|
||||
return valid
|
||||
}
|
||||
|
||||
if podSpec.SecurityContext != nil && podSpec.SecurityContext.SELinuxOptions != nil {
|
||||
if !validSELinuxOptions(podSpec.SecurityContext.SELinuxOptions) {
|
||||
badSetters = append(badSetters, "pod")
|
||||
}
|
||||
}
|
||||
|
||||
var badContainers []string
|
||||
visitContainers(podSpec, func(container *corev1.Container) {
|
||||
if container.SecurityContext != nil && container.SecurityContext.SELinuxOptions != nil {
|
||||
if !validSELinuxOptions(container.SecurityContext.SELinuxOptions) {
|
||||
badContainers = append(badContainers, container.Name)
|
||||
}
|
||||
}
|
||||
})
|
||||
if len(badContainers) > 0 {
|
||||
badSetters = append(
|
||||
badSetters,
|
||||
fmt.Sprintf(
|
||||
"%s %s",
|
||||
pluralize("container", "containers", len(badContainers)),
|
||||
joinQuote(badContainers),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
if len(badSetters) > 0 {
|
||||
var badData []string
|
||||
if len(badTypes) > 0 {
|
||||
badData = append(badData, fmt.Sprintf(
|
||||
"%s %s",
|
||||
pluralize("type", "types", len(badTypes)),
|
||||
joinQuote(badTypes.List()),
|
||||
))
|
||||
if setUser {
|
||||
badData = append(badData, "user may not be set")
|
||||
}
|
||||
if setRole {
|
||||
badData = append(badData, "role may not be set")
|
||||
}
|
||||
}
|
||||
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "seLinuxOptions",
|
||||
ForbiddenDetail: fmt.Sprintf(
|
||||
`%s set forbidden securityContext.seLinuxOptions: %s`,
|
||||
strings.Join(badSetters, " and "),
|
||||
strings.Join(badData, "; "),
|
||||
),
|
||||
}
|
||||
}
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
171
vendor/k8s.io/pod-security-admission/policy/check_seccompProfile_baseline.go
generated
vendored
Normal file
171
vendor/k8s.io/pod-security-admission/policy/check_seccompProfile_baseline.go
generated
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
If seccomp profiles are specified, only runtime default and localhost profiles are allowed.
|
||||
|
||||
v1.0 - v1.18:
|
||||
**Restricted Fields:**
|
||||
metadata.annotations['seccomp.security.alpha.kubernetes.io/pod']
|
||||
metadata.annotations['container.seccomp.security.alpha.kubernetes.io/*']
|
||||
|
||||
**Allowed Values:** 'runtime/default', 'docker/default', 'localhost/*', undefined
|
||||
|
||||
v1.19+:
|
||||
**Restricted Fields:**
|
||||
spec.securityContext.seccompProfile.type
|
||||
spec.containers[*].securityContext.seccompProfile.type
|
||||
spec.initContainers[*].securityContext.seccompProfile.type
|
||||
|
||||
**Allowed Values:** 'RuntimeDefault', 'Localhost', undefined
|
||||
*/
|
||||
const (
|
||||
annotationKeyPod = "seccomp.security.alpha.kubernetes.io/pod"
|
||||
annotationKeyContainerPrefix = "container.seccomp.security.alpha.kubernetes.io/"
|
||||
|
||||
checkSeccompBaselineID CheckID = "seccompProfile_baseline"
|
||||
)
|
||||
|
||||
func init() {
|
||||
addCheck(CheckSeccompBaseline)
|
||||
}
|
||||
|
||||
func CheckSeccompBaseline() Check {
|
||||
return Check{
|
||||
ID: checkSeccompBaselineID,
|
||||
Level: api.LevelBaseline,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 0),
|
||||
CheckPod: seccompProfileBaseline_1_0,
|
||||
},
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 19),
|
||||
CheckPod: seccompProfileBaseline_1_19,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func validSeccomp(t corev1.SeccompProfileType) bool {
|
||||
return t == corev1.SeccompProfileTypeLocalhost ||
|
||||
t == corev1.SeccompProfileTypeRuntimeDefault
|
||||
}
|
||||
|
||||
func validSeccompAnnotationValue(v string) bool {
|
||||
return v == corev1.SeccompProfileRuntimeDefault ||
|
||||
v == corev1.DeprecatedSeccompProfileDockerDefault ||
|
||||
strings.HasPrefix(v, corev1.SeccompLocalhostProfileNamePrefix)
|
||||
}
|
||||
|
||||
// seccompProfileBaseline_1_0 checks baseline policy on seccomp alpha annotation
|
||||
func seccompProfileBaseline_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
forbidden := sets.NewString()
|
||||
|
||||
if val, ok := podMetadata.Annotations[annotationKeyPod]; ok {
|
||||
if !validSeccompAnnotationValue(val) {
|
||||
forbidden.Insert(fmt.Sprintf("%s=%q", annotationKeyPod, val))
|
||||
}
|
||||
}
|
||||
|
||||
visitContainers(podSpec, func(c *corev1.Container) {
|
||||
annotation := annotationKeyContainerPrefix + c.Name
|
||||
if val, ok := podMetadata.Annotations[annotation]; ok {
|
||||
if !validSeccompAnnotationValue(val) {
|
||||
forbidden.Insert(fmt.Sprintf("%s=%q", annotation, val))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if len(forbidden) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "seccompProfile",
|
||||
ForbiddenDetail: fmt.Sprintf(
|
||||
"forbidden %s %s",
|
||||
pluralize("annotation", "annotations", len(forbidden)),
|
||||
strings.Join(forbidden.List(), ", "),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
||||
|
||||
// seccompProfileBaseline_1_19 checks baseline policy on securityContext.seccompProfile field
|
||||
func seccompProfileBaseline_1_19(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
// things that explicitly set seccompProfile.type to a bad value
|
||||
var badSetters []string
|
||||
badValues := sets.NewString()
|
||||
|
||||
if podSpec.SecurityContext != nil && podSpec.SecurityContext.SeccompProfile != nil {
|
||||
if !validSeccomp(podSpec.SecurityContext.SeccompProfile.Type) {
|
||||
badSetters = append(badSetters, "pod")
|
||||
badValues.Insert(string(podSpec.SecurityContext.SeccompProfile.Type))
|
||||
}
|
||||
}
|
||||
|
||||
// containers that explicitly set seccompProfile.type to a bad value
|
||||
var explicitlyBadContainers []string
|
||||
|
||||
visitContainers(podSpec, func(c *corev1.Container) {
|
||||
if c.SecurityContext != nil && c.SecurityContext.SeccompProfile != nil {
|
||||
// container explicitly set seccompProfile
|
||||
if !validSeccomp(c.SecurityContext.SeccompProfile.Type) {
|
||||
// container explicitly set seccompProfile to a bad value
|
||||
explicitlyBadContainers = append(explicitlyBadContainers, c.Name)
|
||||
badValues.Insert(string(c.SecurityContext.SeccompProfile.Type))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if len(explicitlyBadContainers) > 0 {
|
||||
badSetters = append(
|
||||
badSetters,
|
||||
fmt.Sprintf(
|
||||
"%s %s",
|
||||
pluralize("container", "containers", len(explicitlyBadContainers)),
|
||||
joinQuote(explicitlyBadContainers),
|
||||
),
|
||||
)
|
||||
}
|
||||
// pod or containers explicitly set bad seccompProfiles
|
||||
if len(badSetters) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "seccompProfile",
|
||||
ForbiddenDetail: fmt.Sprintf(
|
||||
"%s must not set securityContext.seccompProfile.type to %s",
|
||||
strings.Join(badSetters, " and "),
|
||||
joinQuote(badValues.List()),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
155
vendor/k8s.io/pod-security-admission/policy/check_seccompProfile_restricted.go
generated
vendored
Normal file
155
vendor/k8s.io/pod-security-admission/policy/check_seccompProfile_restricted.go
generated
vendored
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
Seccomp profiles must be specified, and only runtime default and localhost profiles are allowed.
|
||||
|
||||
v1.19+:
|
||||
**Restricted Fields:**
|
||||
spec.securityContext.seccompProfile.type
|
||||
spec.containers[*].securityContext.seccompProfile.type
|
||||
spec.initContainers[*].securityContext.seccompProfile.type
|
||||
|
||||
**Allowed Values:** 'RuntimeDefault', 'Localhost'
|
||||
Note: container-level fields may be undefined if pod-level field is specified.
|
||||
|
||||
*/
|
||||
|
||||
func init() {
|
||||
addCheck(CheckSeccompProfileRestricted)
|
||||
}
|
||||
|
||||
func CheckSeccompProfileRestricted() Check {
|
||||
return Check{
|
||||
ID: "seccompProfile_restricted",
|
||||
Level: api.LevelRestricted,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 19),
|
||||
CheckPod: seccompProfileRestricted_1_19,
|
||||
OverrideCheckIDs: []CheckID{checkSeccompBaselineID},
|
||||
},
|
||||
// Starting 1.25, windows pods would be exempted from this check using pod.spec.os field when set to windows.
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 25),
|
||||
CheckPod: seccompProfileRestricted_1_25,
|
||||
OverrideCheckIDs: []CheckID{checkSeccompBaselineID},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// seccompProfileRestricted_1_19 checks restricted policy on securityContext.seccompProfile field
|
||||
func seccompProfileRestricted_1_19(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
// things that explicitly set seccompProfile.type to a bad value
|
||||
var badSetters []string
|
||||
badValues := sets.NewString()
|
||||
|
||||
podSeccompSet := false
|
||||
|
||||
if podSpec.SecurityContext != nil && podSpec.SecurityContext.SeccompProfile != nil {
|
||||
if !validSeccomp(podSpec.SecurityContext.SeccompProfile.Type) {
|
||||
badSetters = append(badSetters, "pod")
|
||||
badValues.Insert(string(podSpec.SecurityContext.SeccompProfile.Type))
|
||||
} else {
|
||||
podSeccompSet = true
|
||||
}
|
||||
}
|
||||
|
||||
// containers that explicitly set seccompProfile.type to a bad value
|
||||
var explicitlyBadContainers []string
|
||||
// containers that didn't set seccompProfile and aren't caught by a pod-level seccompProfile
|
||||
var implicitlyBadContainers []string
|
||||
|
||||
visitContainers(podSpec, func(c *corev1.Container) {
|
||||
if c.SecurityContext != nil && c.SecurityContext.SeccompProfile != nil {
|
||||
// container explicitly set seccompProfile
|
||||
if !validSeccomp(c.SecurityContext.SeccompProfile.Type) {
|
||||
// container explicitly set seccompProfile to a bad value
|
||||
explicitlyBadContainers = append(explicitlyBadContainers, c.Name)
|
||||
badValues.Insert(string(c.SecurityContext.SeccompProfile.Type))
|
||||
}
|
||||
} else {
|
||||
// container did not explicitly set seccompProfile
|
||||
if !podSeccompSet {
|
||||
// no valid pod-level seccompProfile, so this container implicitly has a bad value
|
||||
implicitlyBadContainers = append(implicitlyBadContainers, c.Name)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if len(explicitlyBadContainers) > 0 {
|
||||
badSetters = append(
|
||||
badSetters,
|
||||
fmt.Sprintf(
|
||||
"%s %s",
|
||||
pluralize("container", "containers", len(explicitlyBadContainers)),
|
||||
joinQuote(explicitlyBadContainers),
|
||||
),
|
||||
)
|
||||
}
|
||||
// pod or containers explicitly set bad seccompProfiles
|
||||
if len(badSetters) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "seccompProfile",
|
||||
ForbiddenDetail: fmt.Sprintf(
|
||||
"%s must not set securityContext.seccompProfile.type to %s",
|
||||
strings.Join(badSetters, " and "),
|
||||
joinQuote(badValues.List()),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// pod didn't set seccompProfile and not all containers opted into seccompProfile
|
||||
if len(implicitlyBadContainers) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "seccompProfile",
|
||||
ForbiddenDetail: fmt.Sprintf(
|
||||
`pod or %s %s must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost"`,
|
||||
pluralize("container", "containers", len(implicitlyBadContainers)),
|
||||
joinQuote(implicitlyBadContainers),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
||||
|
||||
// seccompProfileRestricted_1_25 checks restricted policy on securityContext.seccompProfile field for kubernetes
|
||||
// version 1.25 and above
|
||||
func seccompProfileRestricted_1_25(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
// Pod API validation would have failed if podOS == Windows and if secCompProfile has been set.
|
||||
// We can admit the Windows pod even if seccompProfile has not been set.
|
||||
if podSpec.OS != nil && podSpec.OS.Name == corev1.Windows {
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
||||
return seccompProfileRestricted_1_19(podMetadata, podSpec)
|
||||
}
|
96
vendor/k8s.io/pod-security-admission/policy/check_sysctls.go
generated
vendored
Normal file
96
vendor/k8s.io/pod-security-admission/policy/check_sysctls.go
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
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/sets"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
Sysctls can disable security mechanisms or affect all containers on a host,
|
||||
and should be disallowed except for an allowed "safe" subset.
|
||||
|
||||
A sysctl is considered safe if it is namespaced in the container or the Pod,
|
||||
and it is isolated from other Pods or processes on the same Node.
|
||||
|
||||
**Restricted Fields:**
|
||||
spec.securityContext.sysctls[*].name
|
||||
|
||||
**Allowed Values:**
|
||||
'kernel.shm_rmid_forced'
|
||||
'net.ipv4.ip_local_port_range'
|
||||
'net.ipv4.tcp_syncookies'
|
||||
'net.ipv4.ping_group_range'
|
||||
'net.ipv4.ip_unprivileged_port_start'
|
||||
|
||||
*/
|
||||
|
||||
func init() {
|
||||
addCheck(CheckSysctls)
|
||||
}
|
||||
|
||||
// CheckSysctls returns a baseline level check
|
||||
// that limits the value of sysctls in 1.0+
|
||||
func CheckSysctls() Check {
|
||||
return Check{
|
||||
ID: "sysctls",
|
||||
Level: api.LevelBaseline,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 0),
|
||||
CheckPod: sysctls_1_0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
sysctls_allowed_1_0 = sets.NewString(
|
||||
"kernel.shm_rmid_forced",
|
||||
"net.ipv4.ip_local_port_range",
|
||||
"net.ipv4.tcp_syncookies",
|
||||
"net.ipv4.ping_group_range",
|
||||
"net.ipv4.ip_unprivileged_port_start",
|
||||
)
|
||||
)
|
||||
|
||||
func sysctls_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
var forbiddenSysctls []string
|
||||
|
||||
if podSpec.SecurityContext != nil {
|
||||
for _, sysctl := range podSpec.SecurityContext.Sysctls {
|
||||
if !sysctls_allowed_1_0.Has(sysctl.Name) {
|
||||
forbiddenSysctls = append(forbiddenSysctls, sysctl.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(forbiddenSysctls) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "forbidden sysctls",
|
||||
ForbiddenDetail: strings.Join(forbiddenSysctls, ", "),
|
||||
}
|
||||
}
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
102
vendor/k8s.io/pod-security-admission/policy/check_windowsHostProcess.go
generated
vendored
Normal file
102
vendor/k8s.io/pod-security-admission/policy/check_windowsHostProcess.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
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(CheckWindowsHostProcess)
|
||||
}
|
||||
|
||||
// CheckWindowsHostProcess returns a baseline level check
|
||||
// that forbids hostProcess=true in 1.0+
|
||||
func CheckWindowsHostProcess() Check {
|
||||
return Check{
|
||||
ID: "windowsHostProcess",
|
||||
Level: api.LevelBaseline,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 0),
|
||||
CheckPod: windowsHostProcess_1_0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func windowsHostProcess_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
var badContainers []string
|
||||
visitContainers(podSpec, func(container *corev1.Container) {
|
||||
if container.SecurityContext != nil &&
|
||||
container.SecurityContext.WindowsOptions != nil &&
|
||||
container.SecurityContext.WindowsOptions.HostProcess != nil &&
|
||||
*container.SecurityContext.WindowsOptions.HostProcess {
|
||||
badContainers = append(badContainers, 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
|
||||
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",
|
||||
ForbiddenDetail: fmt.Sprintf("%s must not set securityContext.windowsOptions.hostProcess=true", strings.Join(forbiddenSetters, " and ")),
|
||||
}
|
||||
}
|
||||
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
184
vendor/k8s.io/pod-security-admission/policy/checks.go
generated
vendored
Normal file
184
vendor/k8s.io/pod-security-admission/policy/checks.go
generated
vendored
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
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/pod-security-admission/api"
|
||||
)
|
||||
|
||||
type Check struct {
|
||||
// ID is the unique ID of the check.
|
||||
ID CheckID
|
||||
// Level is the policy level this check belongs to.
|
||||
// Must be Baseline or Restricted.
|
||||
// Baseline checks are evaluated for baseline and restricted namespaces.
|
||||
// Restricted checks are only evaluated for restricted namespaces.
|
||||
Level api.Level
|
||||
// Versions contains one or more revisions of the check that apply to different versions.
|
||||
// If the check is not yet assigned to a version, this must be a single-item list with a MinimumVersion of "".
|
||||
// Otherwise, MinimumVersion of items must represent strictly increasing versions.
|
||||
Versions []VersionedCheck
|
||||
}
|
||||
|
||||
type VersionedCheck struct {
|
||||
// MinimumVersion is the first policy version this check applies to.
|
||||
// If unset, this check is not yet assigned to a policy version.
|
||||
// If set, must not be "latest".
|
||||
MinimumVersion api.Version
|
||||
// CheckPod determines if the pod is allowed.
|
||||
CheckPod CheckPodFn
|
||||
// OverrideCheckIDs is an optional list of checks that should be skipped when this check is run.
|
||||
// Overrides may only be set on restricted checks, and may only override baseline checks.
|
||||
OverrideCheckIDs []CheckID
|
||||
}
|
||||
|
||||
type CheckPodFn func(*metav1.ObjectMeta, *corev1.PodSpec) CheckResult
|
||||
|
||||
type CheckID string
|
||||
|
||||
// CheckResult contains the result of checking a pod and indicates whether the pod is allowed,
|
||||
// and if not, why it was forbidden.
|
||||
//
|
||||
// Example output for (false, "host ports", "8080, 9090"):
|
||||
//
|
||||
// When checking all pods in a namespace:
|
||||
// disallowed by policy "baseline": host ports, privileged containers, non-default capabilities
|
||||
// When checking an individual pod:
|
||||
// disallowed by policy "baseline": host ports (8080, 9090), privileged containers, non-default capabilities (CAP_NET_RAW)
|
||||
type CheckResult struct {
|
||||
// Allowed indicates if the check allowed the pod.
|
||||
Allowed bool
|
||||
// ForbiddenReason must be set if Allowed is false.
|
||||
// ForbiddenReason should be as succinct as possible and is always output.
|
||||
// Examples:
|
||||
// - "host ports"
|
||||
// - "privileged containers"
|
||||
// - "non-default capabilities"
|
||||
ForbiddenReason string
|
||||
// ForbiddenDetail should only be set if Allowed is false, and is optional.
|
||||
// ForbiddenDetail can include specific values that were disallowed and is used when checking an individual object.
|
||||
// Examples:
|
||||
// - list specific invalid host ports: "8080, 9090"
|
||||
// - list specific invalid containers: "container1, container2"
|
||||
// - list specific non-default capabilities: "CAP_NET_RAW"
|
||||
ForbiddenDetail string
|
||||
}
|
||||
|
||||
// AggergateCheckResult holds the aggregate result of running CheckPod across multiple checks.
|
||||
type AggregateCheckResult struct {
|
||||
// Allowed indicates if all checks allowed the pod.
|
||||
Allowed bool
|
||||
// ForbiddenReasons is a slice of the forbidden reasons from all the forbidden checks. It should not include empty strings.
|
||||
// ForbiddenReasons and ForbiddenDetails must have the same number of elements, and the indexes are for the same check.
|
||||
ForbiddenReasons []string
|
||||
// ForbiddenDetails is a slice of the forbidden details from all the forbidden checks. It may include empty strings.
|
||||
// ForbiddenReasons and ForbiddenDetails must have the same number of elements, and the indexes are for the same check.
|
||||
ForbiddenDetails []string
|
||||
}
|
||||
|
||||
// ForbiddenReason returns a comma-separated string of of the forbidden reasons.
|
||||
// Example: host ports, privileged containers, non-default capabilities
|
||||
func (a *AggregateCheckResult) ForbiddenReason() string {
|
||||
return strings.Join(a.ForbiddenReasons, ", ")
|
||||
}
|
||||
|
||||
// ForbiddenDetail returns a detailed forbidden message, with non-empty details formatted in
|
||||
// parentheses with the associated reason.
|
||||
// Example: host ports (8080, 9090), privileged containers, non-default capabilities (NET_RAW)
|
||||
func (a *AggregateCheckResult) ForbiddenDetail() string {
|
||||
var b strings.Builder
|
||||
for i := 0; i < len(a.ForbiddenReasons); i++ {
|
||||
b.WriteString(a.ForbiddenReasons[i])
|
||||
if a.ForbiddenDetails[i] != "" {
|
||||
b.WriteString(" (")
|
||||
b.WriteString(a.ForbiddenDetails[i])
|
||||
b.WriteString(")")
|
||||
}
|
||||
if i != len(a.ForbiddenReasons)-1 {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// UnknownForbiddenReason is used as the placeholder forbidden reason for checks that incorrectly disallow without providing a reason.
|
||||
const UnknownForbiddenReason = "unknown forbidden reason"
|
||||
|
||||
// AggregateCheckPod runs all the checks and aggregates the forbidden results into a single CheckResult.
|
||||
// The aggregated reason is a comma-separated
|
||||
func AggregateCheckResults(results []CheckResult) AggregateCheckResult {
|
||||
var (
|
||||
reasons []string
|
||||
details []string
|
||||
)
|
||||
for _, result := range results {
|
||||
if !result.Allowed {
|
||||
if len(result.ForbiddenReason) == 0 {
|
||||
reasons = append(reasons, UnknownForbiddenReason)
|
||||
} else {
|
||||
reasons = append(reasons, result.ForbiddenReason)
|
||||
}
|
||||
details = append(details, result.ForbiddenDetail)
|
||||
}
|
||||
}
|
||||
return AggregateCheckResult{
|
||||
Allowed: len(reasons) == 0,
|
||||
ForbiddenReasons: reasons,
|
||||
ForbiddenDetails: details,
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
defaultChecks []func() Check
|
||||
experimentalChecks []func() Check
|
||||
)
|
||||
|
||||
func addCheck(f func() Check) {
|
||||
// add to experimental or versioned list
|
||||
c := f()
|
||||
if len(c.Versions) == 1 && c.Versions[0].MinimumVersion == (api.Version{}) {
|
||||
experimentalChecks = append(experimentalChecks, f)
|
||||
} else {
|
||||
defaultChecks = append(defaultChecks, f)
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultChecks returns checks that are expected to be enabled by default.
|
||||
// The results are mutually exclusive with ExperimentalChecks.
|
||||
// It returns a new copy of checks on each invocation and is expected to be called once at setup time.
|
||||
func DefaultChecks() []Check {
|
||||
retval := make([]Check, 0, len(defaultChecks))
|
||||
for _, f := range defaultChecks {
|
||||
retval = append(retval, f())
|
||||
}
|
||||
return retval
|
||||
}
|
||||
|
||||
// ExperimentalChecks returns checks that have not yet been assigned to policy versions.
|
||||
// The results are mutually exclusive with DefaultChecks.
|
||||
// It returns a new copy of checks on each invocation and is expected to be called once at setup time.
|
||||
func ExperimentalChecks() []Check {
|
||||
retval := make([]Check, 0, len(experimentalChecks))
|
||||
for _, f := range experimentalChecks {
|
||||
retval = append(retval, f())
|
||||
}
|
||||
return retval
|
||||
}
|
18
vendor/k8s.io/pod-security-admission/policy/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/pod-security-admission/policy/doc.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
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 contains implementations of Pod Security Standards checks
|
||||
package policy // import "k8s.io/pod-security-admission/policy"
|
33
vendor/k8s.io/pod-security-admission/policy/helpers.go
generated
vendored
Normal file
33
vendor/k8s.io/pod-security-admission/policy/helpers.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
func joinQuote(items []string) string {
|
||||
if len(items) == 0 {
|
||||
return ""
|
||||
}
|
||||
return `"` + strings.Join(items, `", "`) + `"`
|
||||
}
|
||||
|
||||
func pluralize(singular, plural string, count int) string {
|
||||
if count == 1 {
|
||||
return singular
|
||||
}
|
||||
return plural
|
||||
}
|
226
vendor/k8s.io/pod-security-admission/policy/registry.go
generated
vendored
Normal file
226
vendor/k8s.io/pod-security-admission/policy/registry.go
generated
vendored
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
// Evaluator holds the Checks that are used to validate a policy.
|
||||
type Evaluator interface {
|
||||
// EvaluatePod evaluates the pod against the policy for the given level & version.
|
||||
EvaluatePod(lv api.LevelVersion, podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) []CheckResult
|
||||
}
|
||||
|
||||
// checkRegistry provides a default implementation of an Evaluator.
|
||||
type checkRegistry struct {
|
||||
// The checks are a map policy version to a slice of checks registered for that version.
|
||||
baselineChecks, restrictedChecks map[api.Version][]CheckPodFn
|
||||
// maxVersion is the maximum version that is cached, guaranteed to be at least
|
||||
// the max MinimumVersion of all registered checks.
|
||||
maxVersion api.Version
|
||||
}
|
||||
|
||||
// NewEvaluator constructs a new Evaluator instance from the list of checks. If the provided checks are invalid,
|
||||
// an error is returned. A valid list of checks must meet the following requirements:
|
||||
// 1. Check.ID is unique in the list
|
||||
// 2. Check.Level must be either Baseline or Restricted
|
||||
// 3. Checks must have a non-empty set of versions, sorted in a strictly increasing order
|
||||
// 4. Check.Versions cannot include 'latest'
|
||||
func NewEvaluator(checks []Check) (Evaluator, error) {
|
||||
if err := validateChecks(checks); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := &checkRegistry{
|
||||
baselineChecks: map[api.Version][]CheckPodFn{},
|
||||
restrictedChecks: map[api.Version][]CheckPodFn{},
|
||||
}
|
||||
populate(r, checks)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (r *checkRegistry) EvaluatePod(lv api.LevelVersion, podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) []CheckResult {
|
||||
if lv.Level == api.LevelPrivileged {
|
||||
return nil
|
||||
}
|
||||
if r.maxVersion.Older(lv.Version) {
|
||||
lv.Version = r.maxVersion
|
||||
}
|
||||
|
||||
var checks []CheckPodFn
|
||||
if lv.Level == api.LevelBaseline {
|
||||
checks = r.baselineChecks[lv.Version]
|
||||
} else {
|
||||
// includes non-overridden baseline checks
|
||||
checks = r.restrictedChecks[lv.Version]
|
||||
}
|
||||
|
||||
var results []CheckResult
|
||||
for _, check := range checks {
|
||||
results = append(results, check(podMetadata, podSpec))
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func validateChecks(checks []Check) error {
|
||||
ids := map[CheckID]api.Level{}
|
||||
for _, check := range checks {
|
||||
if _, ok := ids[check.ID]; ok {
|
||||
return fmt.Errorf("multiple checks registered for ID %s", check.ID)
|
||||
}
|
||||
ids[check.ID] = check.Level
|
||||
if check.Level != api.LevelBaseline && check.Level != api.LevelRestricted {
|
||||
return fmt.Errorf("check %s: invalid level %s", check.ID, check.Level)
|
||||
}
|
||||
if len(check.Versions) == 0 {
|
||||
return fmt.Errorf("check %s: empty", check.ID)
|
||||
}
|
||||
maxVersion := api.Version{}
|
||||
for _, c := range check.Versions {
|
||||
if c.MinimumVersion == (api.Version{}) {
|
||||
return fmt.Errorf("check %s: undefined version found", check.ID)
|
||||
}
|
||||
if c.MinimumVersion.Latest() {
|
||||
return fmt.Errorf("check %s: version cannot be 'latest'", check.ID)
|
||||
}
|
||||
if maxVersion == c.MinimumVersion {
|
||||
return fmt.Errorf("check %s: duplicate version %s", check.ID, c.MinimumVersion)
|
||||
}
|
||||
if !maxVersion.Older(c.MinimumVersion) {
|
||||
return fmt.Errorf("check %s: versions must be strictly increasing", check.ID)
|
||||
}
|
||||
maxVersion = c.MinimumVersion
|
||||
}
|
||||
}
|
||||
// Second pass to validate overrides.
|
||||
for _, check := range checks {
|
||||
for _, c := range check.Versions {
|
||||
if len(c.OverrideCheckIDs) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if check.Level != api.LevelRestricted {
|
||||
return fmt.Errorf("check %s: only restricted checks may set overrides", check.ID)
|
||||
}
|
||||
for _, override := range c.OverrideCheckIDs {
|
||||
if overriddenLevel, ok := ids[override]; ok && overriddenLevel != api.LevelBaseline {
|
||||
return fmt.Errorf("check %s: overrides %s check %s", check.ID, overriddenLevel, override)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func populate(r *checkRegistry, validChecks []Check) {
|
||||
// Find the max(MinimumVersion) across all checks.
|
||||
for _, c := range validChecks {
|
||||
lastVersion := c.Versions[len(c.Versions)-1].MinimumVersion
|
||||
if r.maxVersion.Older(lastVersion) {
|
||||
r.maxVersion = lastVersion
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
restrictedVersionedChecks = map[api.Version]map[CheckID]VersionedCheck{}
|
||||
baselineVersionedChecks = map[api.Version]map[CheckID]VersionedCheck{}
|
||||
|
||||
baselineIDs, restrictedIDs []CheckID
|
||||
)
|
||||
for _, c := range validChecks {
|
||||
if c.Level == api.LevelRestricted {
|
||||
restrictedIDs = append(restrictedIDs, c.ID)
|
||||
inflateVersions(c, restrictedVersionedChecks, r.maxVersion)
|
||||
} else {
|
||||
baselineIDs = append(baselineIDs, c.ID)
|
||||
inflateVersions(c, baselineVersionedChecks, r.maxVersion)
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the IDs to maintain consistent error messages.
|
||||
sort.Slice(restrictedIDs, func(i, j int) bool { return restrictedIDs[i] < restrictedIDs[j] })
|
||||
sort.Slice(baselineIDs, func(i, j int) bool { return baselineIDs[i] < baselineIDs[j] })
|
||||
orderedIDs := append(baselineIDs, restrictedIDs...) // Baseline checks first, then restricted.
|
||||
|
||||
for v := api.MajorMinorVersion(1, 0); v.Older(nextMinor(r.maxVersion)); v = nextMinor(v) {
|
||||
// Aggregate all the overridden baseline check ids.
|
||||
overrides := map[CheckID]bool{}
|
||||
for _, c := range restrictedVersionedChecks[v] {
|
||||
for _, override := range c.OverrideCheckIDs {
|
||||
overrides[override] = true
|
||||
}
|
||||
}
|
||||
// Add the filtered baseline checks to restricted.
|
||||
for id, c := range baselineVersionedChecks[v] {
|
||||
if overrides[id] {
|
||||
continue // Overridden check: skip it.
|
||||
}
|
||||
if restrictedVersionedChecks[v] == nil {
|
||||
restrictedVersionedChecks[v] = map[CheckID]VersionedCheck{}
|
||||
}
|
||||
restrictedVersionedChecks[v][id] = c
|
||||
}
|
||||
|
||||
r.restrictedChecks[v] = mapCheckPodFns(restrictedVersionedChecks[v], orderedIDs)
|
||||
r.baselineChecks[v] = mapCheckPodFns(baselineVersionedChecks[v], orderedIDs)
|
||||
}
|
||||
}
|
||||
|
||||
func inflateVersions(check Check, versions map[api.Version]map[CheckID]VersionedCheck, maxVersion api.Version) {
|
||||
for i, c := range check.Versions {
|
||||
var nextVersion api.Version
|
||||
if i+1 < len(check.Versions) {
|
||||
nextVersion = check.Versions[i+1].MinimumVersion
|
||||
} else {
|
||||
// Assumes only 1 Major version.
|
||||
nextVersion = nextMinor(maxVersion)
|
||||
}
|
||||
// Iterate over all versions from the minimum of the current check, to the minimum of the
|
||||
// next check, or the maxVersion++.
|
||||
for v := c.MinimumVersion; v.Older(nextVersion); v = nextMinor(v) {
|
||||
if versions[v] == nil {
|
||||
versions[v] = map[CheckID]VersionedCheck{}
|
||||
}
|
||||
versions[v][check.ID] = check.Versions[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mapCheckPodFns converts the versioned check map to an ordered slice of CheckPodFn,
|
||||
// using the order specified by orderedIDs. All checks must have a corresponding ID in orderedIDs.
|
||||
func mapCheckPodFns(checks map[CheckID]VersionedCheck, orderedIDs []CheckID) []CheckPodFn {
|
||||
fns := make([]CheckPodFn, 0, len(checks))
|
||||
for _, id := range orderedIDs {
|
||||
if check, ok := checks[id]; ok {
|
||||
fns = append(fns, check.CheckPod)
|
||||
}
|
||||
}
|
||||
return fns
|
||||
}
|
||||
|
||||
// nextMinor increments the minor version
|
||||
func nextMinor(v api.Version) api.Version {
|
||||
if v.Latest() {
|
||||
return v
|
||||
}
|
||||
return api.MajorMinorVersion(v.Major(), v.Minor()+1)
|
||||
}
|
37
vendor/k8s.io/pod-security-admission/policy/visitor.go
generated
vendored
Normal file
37
vendor/k8s.io/pod-security-admission/policy/visitor.go
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
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 (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// ContainerVisitor is called with each container and the field.Path to that container
|
||||
type ContainerVisitor func(container *corev1.Container)
|
||||
|
||||
// visitContainers invokes the visitor function for every container in the given pod spec
|
||||
func visitContainers(podSpec *corev1.PodSpec, visitor ContainerVisitor) {
|
||||
for i := range podSpec.InitContainers {
|
||||
visitor(&podSpec.InitContainers[i])
|
||||
}
|
||||
for i := range podSpec.Containers {
|
||||
visitor(&podSpec.Containers[i])
|
||||
}
|
||||
for i := range podSpec.EphemeralContainers {
|
||||
visitor((*corev1.Container)(&podSpec.EphemeralContainers[i].EphemeralContainerCommon))
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user