mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 11:21:47 +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
|
package validation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
machinery "k8s.io/apimachinery/pkg/api/validation"
|
||||||
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"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{}
|
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
|
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
|
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