podsecurity: added ValidatePodSecurityConfiguration

This commit is contained in:
Samuel Roth 2021-07-07 18:59:23 -04:00
parent e67979eaf6
commit a6b30e9629
2 changed files with 228 additions and 3 deletions

View File

@ -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
}

View File

@ -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)
}