mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
PodSecurity: policy: registry
Co-authored-by: Jordan Liggitt <liggitt@google.com>
This commit is contained in:
parent
5183ea0bf0
commit
1436d35779
178
staging/src/k8s.io/pod-security-admission/policy/checks.go
Normal file
178
staging/src/k8s.io/pod-security-admission/policy/checks.go
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
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 string
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
type CheckPodFn func(*metav1.ObjectMeta, *corev1.PodSpec) CheckResult
|
||||||
|
|
||||||
|
// 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
staging/src/k8s.io/pod-security-admission/policy/doc.go
Normal file
18
staging/src/k8s.io/pod-security-admission/policy/doc.go
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"
|
146
staging/src/k8s.io/pod-security-admission/policy/registry.go
Normal file
146
staging/src/k8s.io/pod-security-admission/policy/registry.go
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 of check_ID -> sorted slice of versioned checks, newest first
|
||||||
|
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
|
||||||
|
}
|
||||||
|
results := []CheckResult{}
|
||||||
|
for _, check := range r.baselineChecks[lv.Version] {
|
||||||
|
results = append(results, check(podMetadata, podSpec))
|
||||||
|
}
|
||||||
|
if lv.Level == api.LevelBaseline {
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
for _, check := range r.restrictedChecks[lv.Version] {
|
||||||
|
results = append(results, check(podMetadata, podSpec))
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateChecks(checks []Check) error {
|
||||||
|
ids := map[string]bool{}
|
||||||
|
for _, check := range checks {
|
||||||
|
if ids[check.ID] {
|
||||||
|
return fmt.Errorf("multiple checks registered for ID %s", check.ID)
|
||||||
|
}
|
||||||
|
ids[check.ID] = true
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range validChecks {
|
||||||
|
if c.Level == api.LevelRestricted {
|
||||||
|
inflateVersions(c, r.restrictedChecks, r.maxVersion)
|
||||||
|
} else {
|
||||||
|
inflateVersions(c, r.baselineChecks, r.maxVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func inflateVersions(check Check, versions map[api.Version][]CheckPodFn, 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 = api.MajorMinorVersion(1, maxVersion.Minor()+1)
|
||||||
|
}
|
||||||
|
// 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 = api.MajorMinorVersion(1, v.Minor()+1) {
|
||||||
|
versions[v] = append(versions[v], check.Versions[i].CheckPod)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/pod-security-admission/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCheckRegistry(t *testing.T) {
|
||||||
|
checks := []Check{
|
||||||
|
generateCheck("a", api.LevelBaseline, []string{"v1.0"}),
|
||||||
|
generateCheck("b", api.LevelBaseline, []string{"v1.10"}),
|
||||||
|
generateCheck("c", api.LevelBaseline, []string{"v1.0", "v1.5", "v1.10"}),
|
||||||
|
generateCheck("d", api.LevelBaseline, []string{"v1.11", "v1.15", "v1.20"}),
|
||||||
|
generateCheck("e", api.LevelRestricted, []string{"v1.0"}),
|
||||||
|
generateCheck("f", api.LevelRestricted, []string{"v1.12", "v1.16", "v1.21"}),
|
||||||
|
}
|
||||||
|
|
||||||
|
reg, err := NewEvaluator(checks)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
levelCases := []struct {
|
||||||
|
level api.Level
|
||||||
|
version string
|
||||||
|
expectedReasons []string
|
||||||
|
}{
|
||||||
|
{api.LevelPrivileged, "v1.0", nil},
|
||||||
|
{api.LevelPrivileged, "latest", nil},
|
||||||
|
{api.LevelBaseline, "v1.0", []string{"a:v1.0", "c:v1.0"}},
|
||||||
|
{api.LevelBaseline, "v1.4", []string{"a:v1.0", "c:v1.0"}},
|
||||||
|
{api.LevelBaseline, "v1.5", []string{"a:v1.0", "c:v1.5"}},
|
||||||
|
{api.LevelBaseline, "v1.10", []string{"a:v1.0", "b:v1.10", "c:v1.10"}},
|
||||||
|
{api.LevelBaseline, "v1.11", []string{"a:v1.0", "b:v1.10", "c:v1.10", "d:v1.11"}},
|
||||||
|
{api.LevelBaseline, "latest", []string{"a:v1.0", "b:v1.10", "c:v1.10", "d:v1.20"}},
|
||||||
|
{api.LevelRestricted, "v1.0", []string{"a:v1.0", "c:v1.0", "e:v1.0"}},
|
||||||
|
{api.LevelRestricted, "v1.4", []string{"a:v1.0", "c:v1.0", "e:v1.0"}},
|
||||||
|
{api.LevelRestricted, "v1.5", []string{"a:v1.0", "c:v1.5", "e:v1.0"}},
|
||||||
|
{api.LevelRestricted, "v1.10", []string{"a:v1.0", "b:v1.10", "c:v1.10", "e:v1.0"}},
|
||||||
|
{api.LevelRestricted, "v1.11", []string{"a:v1.0", "b:v1.10", "c:v1.10", "d:v1.11", "e:v1.0"}},
|
||||||
|
{api.LevelRestricted, "latest", []string{"a:v1.0", "b:v1.10", "c:v1.10", "d:v1.20", "e:v1.0", "f:v1.21"}},
|
||||||
|
{api.LevelRestricted, "v1.10000", []string{"a:v1.0", "b:v1.10", "c:v1.10", "d:v1.20", "e:v1.0", "f:v1.21"}},
|
||||||
|
}
|
||||||
|
for _, test := range levelCases {
|
||||||
|
t.Run(fmt.Sprintf("%s:%s", test.level, test.version), func(t *testing.T) {
|
||||||
|
results := reg.EvaluatePod(api.LevelVersion{test.level, versionOrPanic(test.version)}, nil, nil)
|
||||||
|
|
||||||
|
// Set extract the ForbiddenReasons from the results.
|
||||||
|
var actualReasons []string
|
||||||
|
for _, result := range results {
|
||||||
|
actualReasons = append(actualReasons, result.ForbiddenReason)
|
||||||
|
}
|
||||||
|
assert.ElementsMatch(t, test.expectedReasons, actualReasons)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateCheck(id string, level api.Level, versions []string) Check {
|
||||||
|
c := Check{
|
||||||
|
ID: id,
|
||||||
|
Level: level,
|
||||||
|
}
|
||||||
|
for _, ver := range versions {
|
||||||
|
v := versionOrPanic(ver) // Copy ver so it can be used in the CheckPod closure.
|
||||||
|
c.Versions = append(c.Versions, VersionedCheck{
|
||||||
|
MinimumVersion: v,
|
||||||
|
CheckPod: func(_ *metav1.ObjectMeta, _ *corev1.PodSpec) CheckResult {
|
||||||
|
return CheckResult{
|
||||||
|
ForbiddenReason: fmt.Sprintf("%s:%s", id, v),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func versionOrPanic(v string) api.Version {
|
||||||
|
ver, err := api.ParseVersion(v)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return ver
|
||||||
|
}
|
42
staging/src/k8s.io/pod-security-admission/policy/visitor.go
Normal file
42
staging/src/k8s.io/pod-security-admission/policy/visitor.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContainerVisitorWithPath is called with each container and the field.Path to that container
|
||||||
|
type ContainerVisitorWithPath func(container *corev1.Container, path *field.Path)
|
||||||
|
|
||||||
|
// visitContainersWithPath invokes the visitor function with a pointer to the spec
|
||||||
|
// of every container in the given pod spec and the field.Path to that container.
|
||||||
|
func visitContainersWithPath(podSpec *corev1.PodSpec, specPath *field.Path, visitor ContainerVisitorWithPath) {
|
||||||
|
fldPath := specPath.Child("initContainers")
|
||||||
|
for i := range podSpec.InitContainers {
|
||||||
|
visitor(&podSpec.InitContainers[i], fldPath.Index(i))
|
||||||
|
}
|
||||||
|
fldPath = specPath.Child("containers")
|
||||||
|
for i := range podSpec.Containers {
|
||||||
|
visitor(&podSpec.Containers[i], fldPath.Index(i))
|
||||||
|
}
|
||||||
|
fldPath = specPath.Child("ephemeralContainers")
|
||||||
|
for i := range podSpec.EphemeralContainers {
|
||||||
|
visitor((*corev1.Container)(&podSpec.EphemeralContainers[i].EphemeralContainerCommon), fldPath.Index(i))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user