diff --git a/staging/src/k8s.io/pod-security-admission/admission/admission_test.go b/staging/src/k8s.io/pod-security-admission/admission/admission_test.go index 6d20569b07c..c0412b6d930 100644 --- a/staging/src/k8s.io/pod-security-admission/admission/admission_test.go +++ b/staging/src/k8s.io/pod-security-admission/admission/admission_test.go @@ -278,7 +278,7 @@ func TestValidateNamespace(t *testing.T) { expectAllowed: true, expectListPods: false, expectWarnings: []string{ - `namespace "test" is exempt from Pod Security, and the policy (enforce=restricted:latest) will be ignored`, + `namespace "test" is exempt from Pod Security, and the policy (enforce=restricted:latest, warn=restricted:latest) will be ignored`, }, }, { @@ -1180,7 +1180,7 @@ func TestExemptNamespaceWarning(t *testing.T) { api.EnforceLevelLabel: string(api.LevelBaseline), }, expectWarning: true, - expectWarningContains: "(enforce=baseline:latest)", + expectWarningContains: "(enforce=baseline:latest, warn=baseline:latest)", }, { name: "warn-on-warn", labels: map[string]string{ diff --git a/staging/src/k8s.io/pod-security-admission/api/helpers.go b/staging/src/k8s.io/pod-security-admission/api/helpers.go index 67aa83e5f0f..200a82cdab0 100644 --- a/staging/src/k8s.io/pod-security-admission/api/helpers.go +++ b/staging/src/k8s.io/pod-security-admission/api/helpers.go @@ -213,12 +213,16 @@ func PolicyToEvaluate(labels map[string]string, defaults Policy) (Policy, field. errs field.ErrorList p = defaults + + hasEnforceLevel bool + hasWarnLevel, hasWarnVersion bool ) if len(labels) == 0 { return p, nil } if level, ok := labels[EnforceLevelLabel]; ok { p.Enforce.Level, err = ParseLevel(level) + hasEnforceLevel = (err == nil) // Don't default warn in case of error errs = appendErr(errs, err, EnforceLevelLabel, level) } if version, ok := labels[EnforceVersionLabel]; ok { @@ -237,6 +241,7 @@ func PolicyToEvaluate(labels map[string]string, defaults Policy) (Policy, field. errs = appendErr(errs, err, AuditVersionLabel, version) } if level, ok := labels[WarnLevelLabel]; ok { + hasWarnLevel = true p.Warn.Level, err = ParseLevel(level) errs = appendErr(errs, err, WarnLevelLabel, level) if err != nil { @@ -244,9 +249,19 @@ func PolicyToEvaluate(labels map[string]string, defaults Policy) (Policy, field. } } if version, ok := labels[WarnVersionLabel]; ok { + hasWarnVersion = true p.Warn.Version, err = ParseVersion(version) errs = appendErr(errs, err, WarnVersionLabel, version) } + + // Default warn to the enforce level when explicitly set to a more restrictive level. + if !hasWarnLevel && hasEnforceLevel && CompareLevels(p.Enforce.Level, p.Warn.Level) > 0 { + p.Warn.Level = p.Enforce.Level + if !hasWarnVersion { + p.Warn.Version = p.Enforce.Version + } + } + return p, errs } diff --git a/staging/src/k8s.io/pod-security-admission/api/helpers_test.go b/staging/src/k8s.io/pod-security-admission/api/helpers_test.go index 09a0b2a9028..7c22d2438bf 100644 --- a/staging/src/k8s.io/pod-security-admission/api/helpers_test.go +++ b/staging/src/k8s.io/pod-security-admission/api/helpers_test.go @@ -117,3 +117,151 @@ func TestPolicyEquals(t *testing.T) { assert.True(t, baseline.Equivalent(&baseline), "baseline policy equals itself") assert.False(t, privileged.Equivalent(&baseline), "privileged != baseline") } + +func TestPolicyToEvaluate(t *testing.T) { + privilegedLV := LevelVersion{ + Level: LevelPrivileged, + Version: LatestVersion(), + } + privilegedPolicy := Policy{ + Enforce: privilegedLV, + Warn: privilegedLV, + Audit: privilegedLV, + } + + type testcase struct { + desc string + labels map[string]string + defaults Policy + expect Policy + expectErr bool + } + + tests := []testcase{{ + desc: "simple enforce", + labels: makeLabels("enforce", "baseline"), + expect: Policy{ + Enforce: LevelVersion{LevelBaseline, LatestVersion()}, + Warn: LevelVersion{LevelBaseline, LatestVersion()}, + Audit: privilegedLV, + }, + }, { + desc: "simple warn", + labels: makeLabels("warn", "restricted"), + expect: Policy{ + Enforce: privilegedLV, + Warn: LevelVersion{LevelRestricted, LatestVersion()}, + Audit: privilegedLV, + }, + }, { + desc: "simple audit", + labels: makeLabels("audit", "baseline"), + expect: Policy{ + Enforce: privilegedLV, + Warn: privilegedLV, + Audit: LevelVersion{LevelBaseline, LatestVersion()}, + }, + }, { + desc: "enforce & warn", + labels: makeLabels("enforce", "baseline", "warn", "restricted"), + expect: Policy{ + Enforce: LevelVersion{LevelBaseline, LatestVersion()}, + Warn: LevelVersion{LevelRestricted, LatestVersion()}, + Audit: privilegedLV, + }, + }, { + desc: "enforce version", + labels: makeLabels("enforce", "baseline", "enforce-version", "v1.22"), + expect: Policy{ + Enforce: LevelVersion{LevelBaseline, MajorMinorVersion(1, 22)}, + Warn: LevelVersion{LevelBaseline, MajorMinorVersion(1, 22)}, + Audit: privilegedLV, + }, + }, { + desc: "enforce version & warn-version", + labels: makeLabels("enforce", "baseline", "enforce-version", "v1.22", "warn-version", "latest"), + expect: Policy{ + Enforce: LevelVersion{LevelBaseline, MajorMinorVersion(1, 22)}, + Warn: LevelVersion{LevelBaseline, LatestVersion()}, + Audit: privilegedLV, + }, + }, { + desc: "enforce & warn-version", + labels: makeLabels("enforce", "baseline", "warn-version", "v1.23"), + expect: Policy{ + Enforce: LevelVersion{LevelBaseline, LatestVersion()}, + Warn: LevelVersion{LevelBaseline, MajorMinorVersion(1, 23)}, + Audit: privilegedLV, + }, + }, { + desc: "fully specd", + labels: makeLabels( + "enforce", "baseline", "enforce-version", "v1.20", + "warn", "restricted", "warn-version", "v1.21", + "audit", "restricted", "audit-version", "v1.22"), + expect: Policy{ + Enforce: LevelVersion{LevelBaseline, MajorMinorVersion(1, 20)}, + Warn: LevelVersion{LevelRestricted, MajorMinorVersion(1, 21)}, + Audit: LevelVersion{LevelRestricted, MajorMinorVersion(1, 22)}, + }, + }, { + desc: "enforce no warn", + labels: makeLabels("enforce", "baseline", "warn", "privileged"), + expect: Policy{ + Enforce: LevelVersion{LevelBaseline, LatestVersion()}, + Warn: privilegedLV, + Audit: privilegedLV, + }, + }, { + desc: "enforce warn error", + labels: makeLabels("enforce", "baseline", "warn", "foo"), + expect: Policy{ + Enforce: LevelVersion{LevelBaseline, LatestVersion()}, + Warn: privilegedLV, + Audit: privilegedLV, + }, + expectErr: true, + }, { + desc: "enforce error", + labels: makeLabels("enforce", "foo"), + expect: Policy{ + Enforce: LevelVersion{LevelRestricted, LatestVersion()}, + Warn: privilegedLV, + Audit: privilegedLV, + }, + expectErr: true, + }} + + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + if test.defaults == (Policy{}) { + test.defaults = privilegedPolicy + } + + actual, errs := PolicyToEvaluate(test.labels, test.defaults) + if test.expectErr { + assert.Error(t, errs.ToAggregate()) + } else { + assert.NoError(t, errs.ToAggregate()) + } + + assert.Equal(t, test.expect, actual) + }) + } +} + +// makeLabels turns the kev-value pairs into a labels map[string]string. +func makeLabels(kvs ...string) map[string]string { + if len(kvs)%2 != 0 { + panic("makeLabels called with mismatched key-values") + } + labels := map[string]string{ + "other-label": "foo-bar", + } + for i := 0; i < len(kvs); i += 2 { + key, value := kvs[i], kvs[i+1] + key = labelPrefix + key + labels[key] = value + } + return labels +}