mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 19:01:49 +00:00
Merge pull request #103560 from sejr/podsecurity-validate-configuration
[PodSecurity] Add ValidatePodSecurityConfiguration
This commit is contained in:
commit
388c2d901d
@ -17,12 +17,111 @@ limitations under the License.
|
||||
package validation
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
machinery "k8s.io/apimachinery/pkg/api/validation"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/pod-security-admission/admission/api"
|
||||
admissionapi "k8s.io/pod-security-admission/admission/api"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
func ValidatePodSecurityConfiguration(configuration *api.PodSecurityConfiguration) field.ErrorList {
|
||||
// ValidatePodSecurityConfiguration validates a given PodSecurityConfiguration.
|
||||
func ValidatePodSecurityConfiguration(configuration *admissionapi.PodSecurityConfiguration) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
// TODO: validate default levels and versions
|
||||
|
||||
// validate defaults
|
||||
allErrs = append(allErrs, validateLevel(field.NewPath("defaults", "enforce"), configuration.Defaults.Enforce)...)
|
||||
allErrs = append(allErrs, validateVersion(field.NewPath("defaults", "enforce-version"), configuration.Defaults.EnforceVersion)...)
|
||||
allErrs = append(allErrs, validateLevel(field.NewPath("defaults", "warn"), configuration.Defaults.Warn)...)
|
||||
allErrs = append(allErrs, validateVersion(field.NewPath("defaults", "warn-version"), configuration.Defaults.WarnVersion)...)
|
||||
allErrs = append(allErrs, validateLevel(field.NewPath("defaults", "audit"), configuration.Defaults.Audit)...)
|
||||
allErrs = append(allErrs, validateVersion(field.NewPath("defaults", "audit-version"), configuration.Defaults.AuditVersion)...)
|
||||
|
||||
// validate exemptions
|
||||
allErrs = append(allErrs, validateNamespaces(configuration)...)
|
||||
allErrs = append(allErrs, validateRuntimeClasses(configuration)...)
|
||||
allErrs = append(allErrs, validateUsernames(configuration)...)
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validateLevel validates a level
|
||||
func validateLevel(p *field.Path, value string) field.ErrorList {
|
||||
errs := field.ErrorList{}
|
||||
_, err := api.ParseLevel(value)
|
||||
if err != nil {
|
||||
errs = append(errs, field.Invalid(p, value, err.Error()))
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
// validateVersion validates a version
|
||||
func validateVersion(p *field.Path, value string) field.ErrorList {
|
||||
errs := field.ErrorList{}
|
||||
_, err := api.ParseVersion(value)
|
||||
if err != nil {
|
||||
errs = append(errs, field.Invalid(p, value, err.Error()))
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func validateNamespaces(configuration *admissionapi.PodSecurityConfiguration) field.ErrorList {
|
||||
errs := field.ErrorList{}
|
||||
validSet := sets.NewString()
|
||||
for i, ns := range configuration.Exemptions.Namespaces {
|
||||
err := machinery.ValidateNamespaceName(ns, false)
|
||||
if len(err) > 0 {
|
||||
path := field.NewPath("exemptions", "namespaces").Index(i)
|
||||
errs = append(errs, field.Invalid(path, ns, strings.Join(err, ", ")))
|
||||
continue
|
||||
}
|
||||
if validSet.Has(ns) {
|
||||
path := field.NewPath("exemptions", "namespaces").Index(i)
|
||||
errs = append(errs, field.Duplicate(path, ns))
|
||||
continue
|
||||
}
|
||||
validSet.Insert(ns)
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func validateRuntimeClasses(configuration *admissionapi.PodSecurityConfiguration) field.ErrorList {
|
||||
errs := field.ErrorList{}
|
||||
validSet := sets.NewString()
|
||||
for i, rc := range configuration.Exemptions.RuntimeClasses {
|
||||
err := machinery.NameIsDNSSubdomain(rc, false)
|
||||
if len(err) > 0 {
|
||||
path := field.NewPath("exemptions", "runtimeClasses").Index(i)
|
||||
errs = append(errs, field.Invalid(path, rc, strings.Join(err, ", ")))
|
||||
continue
|
||||
}
|
||||
if validSet.Has(rc) {
|
||||
path := field.NewPath("exemptions", "runtimeClasses").Index(i)
|
||||
errs = append(errs, field.Duplicate(path, rc))
|
||||
continue
|
||||
}
|
||||
validSet.Insert(rc)
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func validateUsernames(configuration *admissionapi.PodSecurityConfiguration) field.ErrorList {
|
||||
errs := field.ErrorList{}
|
||||
validSet := sets.NewString()
|
||||
for i, uname := range configuration.Exemptions.Usernames {
|
||||
if uname == "" {
|
||||
path := field.NewPath("exemptions", "usernames").Index(i)
|
||||
errs = append(errs, field.Invalid(path, uname, "username must not be empty"))
|
||||
continue
|
||||
}
|
||||
if validSet.Has(uname) {
|
||||
path := field.NewPath("exemptions", "usernames").Index(i)
|
||||
errs = append(errs, field.Duplicate(path, uname))
|
||||
continue
|
||||
}
|
||||
validSet.Insert(uname)
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
@ -15,3 +15,129 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
package validation
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/pod-security-admission/admission/api"
|
||||
)
|
||||
|
||||
type (
|
||||
test struct {
|
||||
configuration api.PodSecurityConfiguration
|
||||
expectedErrList field.ErrorList
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
invalidValueUppercase = "TEST"
|
||||
invalidValueChars = "_&$%#"
|
||||
invalidValueTooLong = "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest"
|
||||
invalidValueEmpty = ""
|
||||
validValue = "testing"
|
||||
)
|
||||
|
||||
func TestValidatePodSecurityConfiguration(t *testing.T) {
|
||||
tests := []test{
|
||||
// defaults
|
||||
{
|
||||
expectedErrList: field.ErrorList{
|
||||
field.Invalid(exemptionsPath("namespaces", 0), invalidValueEmpty, "..."),
|
||||
field.Invalid(exemptionsPath("namespaces", 1), invalidValueChars, "..."),
|
||||
field.Invalid(exemptionsPath("namespaces", 2), invalidValueUppercase, "..."),
|
||||
field.Invalid(exemptionsPath("namespaces", 3), invalidValueTooLong, "..."),
|
||||
field.Duplicate(exemptionsPath("namespaces", 5), validValue),
|
||||
field.Invalid(exemptionsPath("runtimeClasses", 0), invalidValueEmpty, "..."),
|
||||
field.Invalid(exemptionsPath("runtimeClasses", 1), invalidValueChars, "..."),
|
||||
field.Invalid(exemptionsPath("runtimeClasses", 2), invalidValueUppercase, "..."),
|
||||
field.Duplicate(exemptionsPath("runtimeClasses", 4), validValue),
|
||||
field.Invalid(exemptionsPath("usernames", 0), invalidValueEmpty, "..."),
|
||||
field.Duplicate(exemptionsPath("usernames", 2), validValue),
|
||||
},
|
||||
configuration: api.PodSecurityConfiguration{
|
||||
Defaults: api.PodSecurityDefaults{
|
||||
Enforce: "privileged",
|
||||
EnforceVersion: "v1.22",
|
||||
Audit: "baseline",
|
||||
AuditVersion: "v1.25",
|
||||
Warn: "restricted",
|
||||
WarnVersion: "latest",
|
||||
},
|
||||
Exemptions: api.PodSecurityExemptions{
|
||||
Namespaces: []string{
|
||||
invalidValueEmpty,
|
||||
invalidValueChars,
|
||||
invalidValueUppercase,
|
||||
invalidValueTooLong,
|
||||
validValue,
|
||||
validValue,
|
||||
},
|
||||
RuntimeClasses: []string{
|
||||
invalidValueEmpty,
|
||||
invalidValueChars,
|
||||
invalidValueUppercase,
|
||||
validValue,
|
||||
validValue,
|
||||
},
|
||||
Usernames: []string{
|
||||
invalidValueEmpty,
|
||||
validValue,
|
||||
validValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedErrList: field.ErrorList{
|
||||
field.Invalid(defaultsPath("enforce"), "baslein", "..."),
|
||||
field.Invalid(defaultsPath("enforce-version"), "v.122", "..."),
|
||||
field.Invalid(defaultsPath("warn"), "", "..."),
|
||||
field.Invalid(defaultsPath("warn-version"), "", "..."),
|
||||
field.Invalid(defaultsPath("audit"), "lorum", "..."),
|
||||
field.Invalid(defaultsPath("audit-version"), "ipsum", "..."),
|
||||
},
|
||||
configuration: api.PodSecurityConfiguration{
|
||||
Defaults: api.PodSecurityDefaults{
|
||||
Enforce: "baslein",
|
||||
EnforceVersion: "v.122",
|
||||
Audit: "lorum",
|
||||
AuditVersion: "ipsum",
|
||||
Warn: "",
|
||||
WarnVersion: "",
|
||||
},
|
||||
Exemptions: api.PodSecurityExemptions{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
errList := ValidatePodSecurityConfiguration(&test.configuration)
|
||||
if len(errList) != len(test.expectedErrList) {
|
||||
t.Errorf("expected %d errs, got %d", len(test.expectedErrList), len(errList))
|
||||
}
|
||||
|
||||
for i, expected := range test.expectedErrList {
|
||||
if expected.Type.String() != errList[i].Type.String() {
|
||||
t.Errorf("expected err type %s, got %s",
|
||||
expected.Type.String(),
|
||||
errList[i].Type.String())
|
||||
}
|
||||
if expected.BadValue != errList[i].BadValue {
|
||||
t.Errorf("expected bad value '%s', got '%s'",
|
||||
expected.BadValue,
|
||||
errList[i].BadValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// defaultsPath returns the appropriate defaults path
|
||||
func defaultsPath(child string) *field.Path {
|
||||
return field.NewPath("defaults", child)
|
||||
}
|
||||
|
||||
// exemptionsPath returns the appropriate defaults path
|
||||
func exemptionsPath(child string, i int) *field.Path {
|
||||
return field.NewPath("exemptions", child).Index(i)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user