Delete PSP API types and generated clients
Signed-off-by: Monis Khan <mok@microsoft.com>
This commit is contained in:
@@ -17,33 +17,18 @@ limitations under the License.
|
||||
package validation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
policyapiv1beta1 "k8s.io/api/policy/v1beta1"
|
||||
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
|
||||
unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
appsvalidation "k8s.io/kubernetes/pkg/apis/apps/validation"
|
||||
"k8s.io/kubernetes/pkg/apis/core"
|
||||
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
"k8s.io/kubernetes/pkg/apis/policy"
|
||||
)
|
||||
|
||||
const (
|
||||
// AllowAny is the wildcard used to allow any profile.
|
||||
seccompAllowAny = "*"
|
||||
// DefaultProfileAnnotationKey specifies the default seccomp profile.
|
||||
seccompDefaultProfileAnnotationKey = "seccomp.security.alpha.kubernetes.io/defaultProfileName"
|
||||
// AllowedProfilesAnnotationKey specifies the allowed seccomp profiles.
|
||||
seccompAllowedProfilesAnnotationKey = "seccomp.security.alpha.kubernetes.io/allowedProfileNames"
|
||||
)
|
||||
|
||||
var supportedUnhealthyPodEvictionPolicies = sets.NewString(
|
||||
string(policy.IfHealthyBudget),
|
||||
string(policy.AlwaysAllow),
|
||||
@@ -107,312 +92,6 @@ func ValidatePodDisruptionBudgetStatusUpdate(status, oldStatus policy.PodDisrupt
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePodSecurityPolicyName can be used to check whether the given
|
||||
// pod security policy name is valid.
|
||||
// Prefix indicates this name will be used as part of generation, in which case
|
||||
// trailing dashes are allowed.
|
||||
var ValidatePodSecurityPolicyName = apimachineryvalidation.NameIsDNSSubdomain
|
||||
|
||||
// ValidatePodSecurityPolicy validates a PodSecurityPolicy and returns an ErrorList
|
||||
// with any errors.
|
||||
func ValidatePodSecurityPolicy(psp *policy.PodSecurityPolicy) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&psp.ObjectMeta, false, ValidatePodSecurityPolicyName, field.NewPath("metadata"))...)
|
||||
allErrs = append(allErrs, ValidatePodSecurityPolicySpecificAnnotations(psp.Annotations, field.NewPath("metadata").Child("annotations"))...)
|
||||
allErrs = append(allErrs, ValidatePodSecurityPolicySpec(&psp.Spec, field.NewPath("spec"))...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePodSecurityPolicySpec validates a PodSecurityPolicySpec and returns an ErrorList
|
||||
// with any errors.
|
||||
func ValidatePodSecurityPolicySpec(spec *policy.PodSecurityPolicySpec, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
allErrs = append(allErrs, validatePSPRunAsUser(fldPath.Child("runAsUser"), &spec.RunAsUser)...)
|
||||
allErrs = append(allErrs, validatePSPRunAsGroup(fldPath.Child("runAsGroup"), spec.RunAsGroup)...)
|
||||
allErrs = append(allErrs, validatePSPSELinux(fldPath.Child("seLinux"), &spec.SELinux)...)
|
||||
allErrs = append(allErrs, validatePSPSupplementalGroup(fldPath.Child("supplementalGroups"), &spec.SupplementalGroups)...)
|
||||
allErrs = append(allErrs, validatePSPFSGroup(fldPath.Child("fsGroup"), &spec.FSGroup)...)
|
||||
allErrs = append(allErrs, validatePodSecurityPolicyVolumes(fldPath, spec.Volumes)...)
|
||||
if len(spec.RequiredDropCapabilities) > 0 && hasCap(policy.AllowAllCapabilities, spec.AllowedCapabilities) {
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("requiredDropCapabilities"), spec.RequiredDropCapabilities,
|
||||
"must be empty when all capabilities are allowed by a wildcard"))
|
||||
}
|
||||
allErrs = append(allErrs, validatePSPCapsAgainstDrops(spec.RequiredDropCapabilities, spec.DefaultAddCapabilities, field.NewPath("defaultAddCapabilities"))...)
|
||||
allErrs = append(allErrs, validatePSPCapsAgainstDrops(spec.RequiredDropCapabilities, spec.AllowedCapabilities, field.NewPath("allowedCapabilities"))...)
|
||||
allErrs = append(allErrs, validatePSPDefaultAllowPrivilegeEscalation(fldPath.Child("defaultAllowPrivilegeEscalation"), spec.DefaultAllowPrivilegeEscalation, spec.AllowPrivilegeEscalation)...)
|
||||
allErrs = append(allErrs, validatePSPAllowedProcMountTypes(fldPath.Child("allowedProcMountTypes"), spec.AllowedProcMountTypes)...)
|
||||
allErrs = append(allErrs, validatePSPAllowedHostPaths(fldPath.Child("allowedHostPaths"), spec.AllowedHostPaths)...)
|
||||
allErrs = append(allErrs, validatePSPAllowedFlexVolumes(fldPath.Child("allowedFlexVolumes"), spec.AllowedFlexVolumes)...)
|
||||
allErrs = append(allErrs, validatePSPAllowedCSIDrivers(fldPath.Child("allowedCSIDrivers"), spec.AllowedCSIDrivers)...)
|
||||
allErrs = append(allErrs, validatePodSecurityPolicySysctls(fldPath.Child("allowedUnsafeSysctls"), spec.AllowedUnsafeSysctls)...)
|
||||
allErrs = append(allErrs, validatePodSecurityPolicySysctls(fldPath.Child("forbiddenSysctls"), spec.ForbiddenSysctls)...)
|
||||
allErrs = append(allErrs, validatePodSecurityPolicySysctlListsDoNotOverlap(fldPath.Child("allowedUnsafeSysctls"), fldPath.Child("forbiddenSysctls"), spec.AllowedUnsafeSysctls, spec.ForbiddenSysctls)...)
|
||||
allErrs = append(allErrs, validateRuntimeClassStrategy(fldPath.Child("runtimeClass"), spec.RuntimeClass)...)
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePodSecurityPolicySpecificAnnotations validates annotations and returns an ErrorList
|
||||
// with any errors.
|
||||
func ValidatePodSecurityPolicySpecificAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if p := annotations[v1.AppArmorBetaDefaultProfileAnnotationKey]; p != "" {
|
||||
if err := apivalidation.ValidateAppArmorProfileFormat(p); err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Key(v1.AppArmorBetaDefaultProfileAnnotationKey), p, err.Error()))
|
||||
}
|
||||
}
|
||||
if allowed := annotations[v1.AppArmorBetaAllowedProfilesAnnotationKey]; allowed != "" {
|
||||
for _, p := range strings.Split(allowed, ",") {
|
||||
if err := apivalidation.ValidateAppArmorProfileFormat(p); err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Key(v1.AppArmorBetaAllowedProfilesAnnotationKey), allowed, err.Error()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if p := annotations[seccompDefaultProfileAnnotationKey]; p != "" {
|
||||
allErrs = append(allErrs, apivalidation.ValidateSeccompProfile(p, fldPath.Key(seccompDefaultProfileAnnotationKey))...)
|
||||
}
|
||||
if allowed := annotations[seccompAllowedProfilesAnnotationKey]; allowed != "" {
|
||||
for _, p := range strings.Split(allowed, ",") {
|
||||
if p == seccompAllowAny {
|
||||
continue
|
||||
}
|
||||
allErrs = append(allErrs, apivalidation.ValidateSeccompProfile(p, fldPath.Key(seccompAllowedProfilesAnnotationKey))...)
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validatePSPAllowedHostPaths makes sure all allowed host paths follow:
|
||||
// 1. path prefix is required
|
||||
// 2. path prefix does not have any element which is ".."
|
||||
func validatePSPAllowedHostPaths(fldPath *field.Path, allowedHostPaths []policy.AllowedHostPath) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
for i, target := range allowedHostPaths {
|
||||
if target.PathPrefix == "" {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Index(i), "is required"))
|
||||
break
|
||||
}
|
||||
parts := strings.Split(filepath.ToSlash(target.PathPrefix), "/")
|
||||
for _, item := range parts {
|
||||
if item == ".." {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Index(i), target.PathPrefix, "must not contain '..'"))
|
||||
break // even for `../../..`, one error is sufficient to make the point
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validatePSPAllowedFlexVolumes(fldPath *field.Path, flexVolumes []policy.AllowedFlexVolume) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if len(flexVolumes) > 0 {
|
||||
for idx, fv := range flexVolumes {
|
||||
if len(fv.Driver) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("allowedFlexVolumes").Index(idx).Child("driver"),
|
||||
"must specify a driver"))
|
||||
}
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validatePSPAllowedCSIDrivers(fldPath *field.Path, csiDrivers []policy.AllowedCSIDriver) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if len(csiDrivers) > 0 {
|
||||
for idx, csiDriver := range csiDrivers {
|
||||
fieldPath := fldPath.Child("allowedCSIDriver").Index(idx).Child("name")
|
||||
allErrs = append(allErrs, apivalidation.ValidateCSIDriverName(csiDriver.Name, fieldPath)...)
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validatePSPSELinux validates the SELinux fields of PodSecurityPolicy.
|
||||
func validatePSPSELinux(fldPath *field.Path, seLinux *policy.SELinuxStrategyOptions) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
// ensure the selinux strategy has a valid rule
|
||||
supportedSELinuxRules := sets.NewString(
|
||||
string(policy.SELinuxStrategyMustRunAs),
|
||||
string(policy.SELinuxStrategyRunAsAny),
|
||||
)
|
||||
if !supportedSELinuxRules.Has(string(seLinux.Rule)) {
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("rule"), seLinux.Rule, supportedSELinuxRules.List()))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validatePSPRunAsUser validates the RunAsUser fields of PodSecurityPolicy.
|
||||
func validatePSPRunAsUser(fldPath *field.Path, runAsUser *policy.RunAsUserStrategyOptions) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
// ensure the user strategy has a valid rule
|
||||
supportedRunAsUserRules := sets.NewString(
|
||||
string(policy.RunAsUserStrategyMustRunAs),
|
||||
string(policy.RunAsUserStrategyMustRunAsNonRoot),
|
||||
string(policy.RunAsUserStrategyRunAsAny),
|
||||
)
|
||||
if !supportedRunAsUserRules.Has(string(runAsUser.Rule)) {
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("rule"), runAsUser.Rule, supportedRunAsUserRules.List()))
|
||||
}
|
||||
|
||||
// validate range settings
|
||||
for idx, rng := range runAsUser.Ranges {
|
||||
allErrs = append(allErrs, validateUserIDRange(fldPath.Child("ranges").Index(idx), rng)...)
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validatePSPRunAsGroup validates the RunAsGroup fields of PodSecurityPolicy.
|
||||
func validatePSPRunAsGroup(fldPath *field.Path, runAsGroup *policy.RunAsGroupStrategyOptions) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
|
||||
if runAsGroup == nil {
|
||||
return allErrs
|
||||
}
|
||||
|
||||
switch runAsGroup.Rule {
|
||||
case policy.RunAsGroupStrategyRunAsAny:
|
||||
if len(runAsGroup.Ranges) != 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("ranges"), runAsGroup.Ranges, "Ranges must be empty"))
|
||||
}
|
||||
case policy.RunAsGroupStrategyMustRunAs, policy.RunAsGroupStrategyMayRunAs:
|
||||
if len(runAsGroup.Ranges) == 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("ranges"), runAsGroup.Ranges, "must provide at least one range"))
|
||||
}
|
||||
// validate range settings
|
||||
for idx, rng := range runAsGroup.Ranges {
|
||||
allErrs = append(allErrs, validateGroupIDRange(fldPath.Child("ranges").Index(idx), rng)...)
|
||||
}
|
||||
default:
|
||||
supportedRunAsGroupRules := []string{
|
||||
string(policy.RunAsGroupStrategyMustRunAs),
|
||||
string(policy.RunAsGroupStrategyRunAsAny),
|
||||
string(policy.RunAsGroupStrategyMayRunAs),
|
||||
}
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("rule"), runAsGroup.Rule, supportedRunAsGroupRules))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validatePSPFSGroup validates the FSGroupStrategyOptions fields of the PodSecurityPolicy.
|
||||
func validatePSPFSGroup(fldPath *field.Path, groupOptions *policy.FSGroupStrategyOptions) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
supportedRules := sets.NewString(
|
||||
string(policy.FSGroupStrategyMustRunAs),
|
||||
string(policy.FSGroupStrategyMayRunAs),
|
||||
string(policy.FSGroupStrategyRunAsAny),
|
||||
)
|
||||
if !supportedRules.Has(string(groupOptions.Rule)) {
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("rule"), groupOptions.Rule, supportedRules.List()))
|
||||
}
|
||||
|
||||
for idx, rng := range groupOptions.Ranges {
|
||||
allErrs = append(allErrs, validateGroupIDRange(fldPath.Child("ranges").Index(idx), rng)...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validatePSPSupplementalGroup validates the SupplementalGroupsStrategyOptions fields of the PodSecurityPolicy.
|
||||
func validatePSPSupplementalGroup(fldPath *field.Path, groupOptions *policy.SupplementalGroupsStrategyOptions) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
supportedRules := sets.NewString(
|
||||
string(policy.SupplementalGroupsStrategyRunAsAny),
|
||||
string(policy.SupplementalGroupsStrategyMayRunAs),
|
||||
string(policy.SupplementalGroupsStrategyMustRunAs),
|
||||
)
|
||||
if !supportedRules.Has(string(groupOptions.Rule)) {
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("rule"), groupOptions.Rule, supportedRules.List()))
|
||||
}
|
||||
|
||||
for idx, rng := range groupOptions.Ranges {
|
||||
allErrs = append(allErrs, validateGroupIDRange(fldPath.Child("ranges").Index(idx), rng)...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validatePodSecurityPolicyVolumes validates the volume fields of PodSecurityPolicy.
|
||||
func validatePodSecurityPolicyVolumes(fldPath *field.Path, volumes []policy.FSType) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
allowed := getAllFSTypesAsSet()
|
||||
// add in the * value since that is a pseudo type that is not included by default
|
||||
allowed.Insert(string(policy.All))
|
||||
for _, v := range volumes {
|
||||
if !allowed.Has(string(v)) {
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("volumes"), v, allowed.List()))
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// getAllFSTypesAsSet returns all actual volume types, regardless
|
||||
// of feature gates. The special policy.All pseudo type is not included.
|
||||
func getAllFSTypesAsSet() sets.String {
|
||||
fstypes := sets.NewString()
|
||||
fstypes.Insert(
|
||||
string(policy.HostPath),
|
||||
string(policy.AzureFile),
|
||||
string(policy.Flocker),
|
||||
string(policy.FlexVolume),
|
||||
string(policy.EmptyDir),
|
||||
string(policy.GCEPersistentDisk),
|
||||
string(policy.AWSElasticBlockStore),
|
||||
string(policy.GitRepo),
|
||||
string(policy.Secret),
|
||||
string(policy.NFS),
|
||||
string(policy.ISCSI),
|
||||
string(policy.Glusterfs),
|
||||
string(policy.PersistentVolumeClaim),
|
||||
string(policy.RBD),
|
||||
string(policy.Cinder),
|
||||
string(policy.CephFS),
|
||||
string(policy.DownwardAPI),
|
||||
string(policy.FC),
|
||||
string(policy.ConfigMap),
|
||||
string(policy.VsphereVolume),
|
||||
string(policy.Quobyte),
|
||||
string(policy.AzureDisk),
|
||||
string(policy.PhotonPersistentDisk),
|
||||
string(policy.StorageOS),
|
||||
string(policy.Projected),
|
||||
string(policy.PortworxVolume),
|
||||
string(policy.ScaleIO),
|
||||
string(policy.CSI),
|
||||
string(policy.Ephemeral),
|
||||
)
|
||||
return fstypes
|
||||
}
|
||||
|
||||
// validatePSPDefaultAllowPrivilegeEscalation validates the DefaultAllowPrivilegeEscalation field against the AllowPrivilegeEscalation field of a PodSecurityPolicy.
|
||||
func validatePSPDefaultAllowPrivilegeEscalation(fldPath *field.Path, defaultAllowPrivilegeEscalation *bool, allowPrivilegeEscalation bool) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if defaultAllowPrivilegeEscalation != nil && *defaultAllowPrivilegeEscalation && !allowPrivilegeEscalation {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, defaultAllowPrivilegeEscalation, "Cannot set DefaultAllowPrivilegeEscalation to true without also setting AllowPrivilegeEscalation to true"))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validatePSPAllowedProcMountTypes validates the DefaultAllowPrivilegeEscalation field against the AllowPrivilegeEscalation field of a PodSecurityPolicy.
|
||||
func validatePSPAllowedProcMountTypes(fldPath *field.Path, allowedProcMountTypes []core.ProcMountType) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
for i, procMountType := range allowedProcMountTypes {
|
||||
if err := apivalidation.ValidateProcMountType(fldPath.Index(i), procMountType); err != nil {
|
||||
allErrs = append(allErrs, err)
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
const sysctlPatternSegmentFmt string = "([a-z0-9][-_a-z0-9]*)?[a-z0-9*]"
|
||||
|
||||
// SysctlContainSlashPatternFmt is a regex that contains a slash used for matching valid sysctl patterns.
|
||||
@@ -432,171 +111,3 @@ func IsValidSysctlPattern(name string) bool {
|
||||
}
|
||||
return sysctlContainSlashPatternRegexp.MatchString(name)
|
||||
}
|
||||
|
||||
func validatePodSecurityPolicySysctlListsDoNotOverlap(allowedSysctlsFldPath, forbiddenSysctlsFldPath *field.Path, allowedUnsafeSysctls, forbiddenSysctls []string) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
for i, allowedSysctl := range allowedUnsafeSysctls {
|
||||
isAllowedSysctlPattern := false
|
||||
allowedSysctlPrefix := ""
|
||||
if strings.HasSuffix(allowedSysctl, "*") {
|
||||
isAllowedSysctlPattern = true
|
||||
allowedSysctlPrefix = strings.TrimSuffix(allowedSysctl, "*")
|
||||
}
|
||||
for j, forbiddenSysctl := range forbiddenSysctls {
|
||||
isForbiddenSysctlPattern := false
|
||||
forbiddenSysctlPrefix := ""
|
||||
if strings.HasSuffix(forbiddenSysctl, "*") {
|
||||
isForbiddenSysctlPattern = true
|
||||
forbiddenSysctlPrefix = strings.TrimSuffix(forbiddenSysctl, "*")
|
||||
}
|
||||
switch {
|
||||
case isAllowedSysctlPattern && isForbiddenSysctlPattern:
|
||||
if strings.HasPrefix(allowedSysctlPrefix, forbiddenSysctlPrefix) {
|
||||
allErrs = append(allErrs, field.Invalid(allowedSysctlsFldPath.Index(i), allowedUnsafeSysctls[i], fmt.Sprintf("sysctl overlaps with %v", forbiddenSysctl)))
|
||||
} else if strings.HasPrefix(forbiddenSysctlPrefix, allowedSysctlPrefix) {
|
||||
allErrs = append(allErrs, field.Invalid(forbiddenSysctlsFldPath.Index(j), forbiddenSysctls[j], fmt.Sprintf("sysctl overlaps with %v", allowedSysctl)))
|
||||
}
|
||||
case isAllowedSysctlPattern:
|
||||
if strings.HasPrefix(forbiddenSysctl, allowedSysctlPrefix) {
|
||||
allErrs = append(allErrs, field.Invalid(forbiddenSysctlsFldPath.Index(j), forbiddenSysctls[j], fmt.Sprintf("sysctl overlaps with %v", allowedSysctl)))
|
||||
}
|
||||
case isForbiddenSysctlPattern:
|
||||
if strings.HasPrefix(allowedSysctl, forbiddenSysctlPrefix) {
|
||||
allErrs = append(allErrs, field.Invalid(allowedSysctlsFldPath.Index(i), allowedUnsafeSysctls[i], fmt.Sprintf("sysctl overlaps with %v", forbiddenSysctl)))
|
||||
}
|
||||
default:
|
||||
if allowedSysctl == forbiddenSysctl {
|
||||
allErrs = append(allErrs, field.Invalid(allowedSysctlsFldPath.Index(i), allowedUnsafeSysctls[i], fmt.Sprintf("sysctl overlaps with %v", forbiddenSysctl)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validatePodSecurityPolicySysctls validates the sysctls fields of PodSecurityPolicy.
|
||||
func validatePodSecurityPolicySysctls(fldPath *field.Path, sysctls []string) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if len(sysctls) == 0 {
|
||||
return allErrs
|
||||
}
|
||||
|
||||
coversAll := false
|
||||
for i, s := range sysctls {
|
||||
if len(s) == 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Index(i), sysctls[i], "empty sysctl not allowed"))
|
||||
} else if !IsValidSysctlPattern(string(s)) {
|
||||
allErrs = append(
|
||||
allErrs,
|
||||
field.Invalid(fldPath.Index(i), sysctls[i], fmt.Sprintf("must have at most %d characters and match regex %s",
|
||||
apivalidation.SysctlMaxLength,
|
||||
SysctlContainSlashPatternFmt,
|
||||
)),
|
||||
)
|
||||
} else if s[0] == '*' {
|
||||
coversAll = true
|
||||
}
|
||||
}
|
||||
|
||||
if coversAll && len(sysctls) > 1 {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("items"), "if '*' is present, must not specify other sysctls"))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateUserIDRange(fldPath *field.Path, rng policy.IDRange) field.ErrorList {
|
||||
return validateIDRanges(fldPath, rng.Min, rng.Max)
|
||||
}
|
||||
|
||||
func validateGroupIDRange(fldPath *field.Path, rng policy.IDRange) field.ErrorList {
|
||||
return validateIDRanges(fldPath, rng.Min, rng.Max)
|
||||
}
|
||||
|
||||
// validateIDRanges ensures the range is valid.
|
||||
func validateIDRanges(fldPath *field.Path, min, max int64) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
// if 0 <= Min <= Max then we do not need to validate max. It is always greater than or
|
||||
// equal to 0 and Min.
|
||||
if min < 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("min"), min, "min cannot be negative"))
|
||||
}
|
||||
if max < 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("max"), max, "max cannot be negative"))
|
||||
}
|
||||
if min > max {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("min"), min, "min cannot be greater than max"))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validatePSPCapsAgainstDrops ensures an allowed cap is not listed in the required drops.
|
||||
func validatePSPCapsAgainstDrops(requiredDrops []core.Capability, capsToCheck []core.Capability, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if requiredDrops == nil {
|
||||
return allErrs
|
||||
}
|
||||
for _, cap := range capsToCheck {
|
||||
if hasCap(cap, requiredDrops) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, cap,
|
||||
fmt.Sprintf("capability is listed in %s and requiredDropCapabilities", fldPath.String())))
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validateRuntimeClassStrategy ensures all the RuntimeClass restrictions are valid.
|
||||
func validateRuntimeClassStrategy(fldPath *field.Path, rc *policy.RuntimeClassStrategyOptions) field.ErrorList {
|
||||
if rc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var allErrs field.ErrorList
|
||||
|
||||
allowed := map[string]bool{}
|
||||
for i, name := range rc.AllowedRuntimeClassNames {
|
||||
if name != policy.AllowAllRuntimeClassNames {
|
||||
allErrs = append(allErrs, apivalidation.ValidateRuntimeClassName(name, fldPath.Child("allowedRuntimeClassNames").Index(i))...)
|
||||
}
|
||||
if allowed[name] {
|
||||
allErrs = append(allErrs, field.Duplicate(fldPath.Child("allowedRuntimeClassNames").Index(i), name))
|
||||
}
|
||||
allowed[name] = true
|
||||
}
|
||||
|
||||
if rc.DefaultRuntimeClassName != nil {
|
||||
allErrs = append(allErrs, apivalidation.ValidateRuntimeClassName(*rc.DefaultRuntimeClassName, fldPath.Child("defaultRuntimeClassName"))...)
|
||||
if !allowed[*rc.DefaultRuntimeClassName] && !allowed[policy.AllowAllRuntimeClassNames] {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("allowedRuntimeClassNames"),
|
||||
fmt.Sprintf("default %q must be allowed", *rc.DefaultRuntimeClassName)))
|
||||
}
|
||||
}
|
||||
|
||||
if allowed[policy.AllowAllRuntimeClassNames] && len(rc.AllowedRuntimeClassNames) > 1 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("allowedRuntimeClassNames"), rc.AllowedRuntimeClassNames, "if '*' is present, must not specify other RuntimeClass names"))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePodSecurityPolicyUpdate validates a PSP for updates.
|
||||
func ValidatePodSecurityPolicyUpdate(old *policy.PodSecurityPolicy, new *policy.PodSecurityPolicy) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&new.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))...)
|
||||
allErrs = append(allErrs, ValidatePodSecurityPolicySpecificAnnotations(new.Annotations, field.NewPath("metadata").Child("annotations"))...)
|
||||
allErrs = append(allErrs, ValidatePodSecurityPolicySpec(&new.Spec, field.NewPath("spec"))...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// hasCap checks for needle in haystack.
|
||||
func hasCap(needle core.Capability, haystack []core.Capability) bool {
|
||||
for _, c := range haystack {
|
||||
if needle == c {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@@ -21,16 +21,12 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
policyv1beta1 "k8s.io/api/policy/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/policy"
|
||||
"k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
func TestValidatePodDisruptionBudgetSpec(t *testing.T) {
|
||||
@@ -283,550 +279,6 @@ func TestValidatePodDisruptionBudgetStatus(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePodSecurityPolicy(t *testing.T) {
|
||||
validPSP := func() *policy.PodSecurityPolicy {
|
||||
return &policy.PodSecurityPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
Spec: policy.PodSecurityPolicySpec{
|
||||
SELinux: policy.SELinuxStrategyOptions{
|
||||
Rule: policy.SELinuxStrategyRunAsAny,
|
||||
},
|
||||
RunAsUser: policy.RunAsUserStrategyOptions{
|
||||
Rule: policy.RunAsUserStrategyRunAsAny,
|
||||
},
|
||||
RunAsGroup: &policy.RunAsGroupStrategyOptions{
|
||||
Rule: policy.RunAsGroupStrategyRunAsAny,
|
||||
},
|
||||
FSGroup: policy.FSGroupStrategyOptions{
|
||||
Rule: policy.FSGroupStrategyRunAsAny,
|
||||
},
|
||||
SupplementalGroups: policy.SupplementalGroupsStrategyOptions{
|
||||
Rule: policy.SupplementalGroupsStrategyRunAsAny,
|
||||
},
|
||||
AllowedHostPaths: []policy.AllowedHostPath{
|
||||
{PathPrefix: "/foo/bar"},
|
||||
{PathPrefix: "/baz/"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
noUserOptions := validPSP()
|
||||
noUserOptions.Spec.RunAsUser.Rule = ""
|
||||
|
||||
noGroupOptions := validPSP()
|
||||
noGroupOptions.Spec.RunAsGroup.Rule = ""
|
||||
|
||||
noSELinuxOptions := validPSP()
|
||||
noSELinuxOptions.Spec.SELinux.Rule = ""
|
||||
|
||||
invalidUserStratType := validPSP()
|
||||
invalidUserStratType.Spec.RunAsUser.Rule = "invalid"
|
||||
|
||||
invalidGroupStratType := validPSP()
|
||||
invalidGroupStratType.Spec.RunAsGroup.Rule = "invalid"
|
||||
|
||||
invalidSELinuxStratType := validPSP()
|
||||
invalidSELinuxStratType.Spec.SELinux.Rule = "invalid"
|
||||
|
||||
invalidUIDPSP := validPSP()
|
||||
invalidUIDPSP.Spec.RunAsUser.Rule = policy.RunAsUserStrategyMustRunAs
|
||||
invalidUIDPSP.Spec.RunAsUser.Ranges = []policy.IDRange{{Min: -1, Max: 1}}
|
||||
|
||||
invalidGIDPSP := validPSP()
|
||||
invalidGIDPSP.Spec.RunAsGroup.Rule = policy.RunAsGroupStrategyMustRunAs
|
||||
invalidGIDPSP.Spec.RunAsGroup.Ranges = []policy.IDRange{{Min: -1, Max: 1}}
|
||||
|
||||
missingObjectMetaName := validPSP()
|
||||
missingObjectMetaName.ObjectMeta.Name = ""
|
||||
|
||||
noFSGroupOptions := validPSP()
|
||||
noFSGroupOptions.Spec.FSGroup.Rule = ""
|
||||
|
||||
invalidFSGroupStratType := validPSP()
|
||||
invalidFSGroupStratType.Spec.FSGroup.Rule = "invalid"
|
||||
|
||||
noSupplementalGroupsOptions := validPSP()
|
||||
noSupplementalGroupsOptions.Spec.SupplementalGroups.Rule = ""
|
||||
|
||||
invalidSupGroupStratType := validPSP()
|
||||
invalidSupGroupStratType.Spec.SupplementalGroups.Rule = "invalid"
|
||||
|
||||
invalidRangeMinGreaterThanMax := validPSP()
|
||||
invalidRangeMinGreaterThanMax.Spec.FSGroup.Ranges = []policy.IDRange{
|
||||
{Min: 2, Max: 1},
|
||||
}
|
||||
|
||||
invalidRangeNegativeMin := validPSP()
|
||||
invalidRangeNegativeMin.Spec.FSGroup.Ranges = []policy.IDRange{
|
||||
{Min: -1, Max: 10},
|
||||
}
|
||||
|
||||
invalidRangeNegativeMax := validPSP()
|
||||
invalidRangeNegativeMax.Spec.FSGroup.Ranges = []policy.IDRange{
|
||||
{Min: 1, Max: -10},
|
||||
}
|
||||
|
||||
wildcardAllowedCapAndRequiredDrop := validPSP()
|
||||
wildcardAllowedCapAndRequiredDrop.Spec.RequiredDropCapabilities = []api.Capability{"foo"}
|
||||
wildcardAllowedCapAndRequiredDrop.Spec.AllowedCapabilities = []api.Capability{policy.AllowAllCapabilities}
|
||||
|
||||
requiredCapAddAndDrop := validPSP()
|
||||
requiredCapAddAndDrop.Spec.DefaultAddCapabilities = []api.Capability{"foo"}
|
||||
requiredCapAddAndDrop.Spec.RequiredDropCapabilities = []api.Capability{"foo"}
|
||||
|
||||
allowedCapListedInRequiredDrop := validPSP()
|
||||
allowedCapListedInRequiredDrop.Spec.RequiredDropCapabilities = []api.Capability{"foo"}
|
||||
allowedCapListedInRequiredDrop.Spec.AllowedCapabilities = []api.Capability{"foo"}
|
||||
|
||||
invalidAppArmorDefault := validPSP()
|
||||
invalidAppArmorDefault.Annotations = map[string]string{
|
||||
v1.AppArmorBetaDefaultProfileAnnotationKey: "not-good",
|
||||
}
|
||||
invalidAppArmorAllowed := validPSP()
|
||||
invalidAppArmorAllowed.Annotations = map[string]string{
|
||||
v1.AppArmorBetaAllowedProfilesAnnotationKey: v1.AppArmorBetaProfileRuntimeDefault + ",not-good",
|
||||
}
|
||||
|
||||
invalidAllowedUnsafeSysctlPattern := validPSP()
|
||||
invalidAllowedUnsafeSysctlPattern.Spec.AllowedUnsafeSysctls = []string{"a.*.b"}
|
||||
|
||||
invalidForbiddenSysctlPattern := validPSP()
|
||||
invalidForbiddenSysctlPattern.Spec.ForbiddenSysctls = []string{"a.*.b"}
|
||||
|
||||
invalidOverlappingSysctls := validPSP()
|
||||
invalidOverlappingSysctls.Spec.ForbiddenSysctls = []string{"kernel.*", "net.ipv4.ip_local_port_range"}
|
||||
invalidOverlappingSysctls.Spec.AllowedUnsafeSysctls = []string{"kernel.shmmax", "net.ipv4.ip_local_port_range"}
|
||||
|
||||
invalidDuplicatedSysctls := validPSP()
|
||||
invalidDuplicatedSysctls.Spec.ForbiddenSysctls = []string{"net.ipv4.ip_local_port_range"}
|
||||
invalidDuplicatedSysctls.Spec.AllowedUnsafeSysctls = []string{"net.ipv4.ip_local_port_range"}
|
||||
|
||||
invalidSeccompDefault := validPSP()
|
||||
invalidSeccompDefault.Annotations = map[string]string{
|
||||
seccompDefaultProfileAnnotationKey: "not-good",
|
||||
}
|
||||
invalidSeccompAllowAnyDefault := validPSP()
|
||||
invalidSeccompAllowAnyDefault.Annotations = map[string]string{
|
||||
seccompDefaultProfileAnnotationKey: "*",
|
||||
}
|
||||
invalidSeccompAllowed := validPSP()
|
||||
invalidSeccompAllowed.Annotations = map[string]string{
|
||||
seccompAllowedProfilesAnnotationKey: api.SeccompProfileRuntimeDefault + ",not-good",
|
||||
}
|
||||
|
||||
invalidAllowedHostPathMissingPath := validPSP()
|
||||
invalidAllowedHostPathMissingPath.Spec.AllowedHostPaths = []policy.AllowedHostPath{
|
||||
{PathPrefix: ""},
|
||||
}
|
||||
|
||||
invalidAllowedHostPathBacksteps := validPSP()
|
||||
invalidAllowedHostPathBacksteps.Spec.AllowedHostPaths = []policy.AllowedHostPath{
|
||||
{PathPrefix: "/dont/allow/backsteps/.."},
|
||||
}
|
||||
|
||||
invalidDefaultAllowPrivilegeEscalation := validPSP()
|
||||
pe := true
|
||||
invalidDefaultAllowPrivilegeEscalation.Spec.DefaultAllowPrivilegeEscalation = &pe
|
||||
|
||||
emptyFlexDriver := validPSP()
|
||||
emptyFlexDriver.Spec.Volumes = []policy.FSType{policy.FlexVolume}
|
||||
emptyFlexDriver.Spec.AllowedFlexVolumes = []policy.AllowedFlexVolume{{}}
|
||||
|
||||
nonEmptyFlexVolumes := validPSP()
|
||||
nonEmptyFlexVolumes.Spec.AllowedFlexVolumes = []policy.AllowedFlexVolume{{Driver: "example/driver"}}
|
||||
|
||||
invalidProcMount := validPSP()
|
||||
invalidProcMount.Spec.AllowedProcMountTypes = []api.ProcMountType{api.ProcMountType("bogus")}
|
||||
|
||||
allowedCSIDriverPSP := validPSP()
|
||||
allowedCSIDriverPSP.Spec.Volumes = []policy.FSType{policy.CSI}
|
||||
allowedCSIDriverPSP.Spec.AllowedCSIDrivers = []policy.AllowedCSIDriver{{}}
|
||||
|
||||
type testCase struct {
|
||||
psp *policy.PodSecurityPolicy
|
||||
errorType field.ErrorType
|
||||
errorDetail string
|
||||
}
|
||||
errorCases := map[string]testCase{
|
||||
"no user options": {
|
||||
psp: noUserOptions,
|
||||
errorType: field.ErrorTypeNotSupported,
|
||||
errorDetail: `supported values: "MustRunAs", "MustRunAsNonRoot", "RunAsAny"`,
|
||||
},
|
||||
"no group options": {
|
||||
psp: noGroupOptions,
|
||||
errorType: field.ErrorTypeNotSupported,
|
||||
errorDetail: `supported values: "MustRunAs", "RunAsAny", "MayRunAs"`,
|
||||
},
|
||||
"no selinux options": {
|
||||
psp: noSELinuxOptions,
|
||||
errorType: field.ErrorTypeNotSupported,
|
||||
errorDetail: `supported values: "MustRunAs", "RunAsAny"`,
|
||||
},
|
||||
"no fsgroup options": {
|
||||
psp: noFSGroupOptions,
|
||||
errorType: field.ErrorTypeNotSupported,
|
||||
errorDetail: `supported values: "MayRunAs", "MustRunAs", "RunAsAny"`,
|
||||
},
|
||||
"no sup group options": {
|
||||
psp: noSupplementalGroupsOptions,
|
||||
errorType: field.ErrorTypeNotSupported,
|
||||
errorDetail: `supported values: "MayRunAs", "MustRunAs", "RunAsAny"`,
|
||||
},
|
||||
"invalid user strategy type": {
|
||||
psp: invalidUserStratType,
|
||||
errorType: field.ErrorTypeNotSupported,
|
||||
errorDetail: `supported values: "MustRunAs", "MustRunAsNonRoot", "RunAsAny"`,
|
||||
},
|
||||
"invalid group strategy type": {
|
||||
psp: invalidGroupStratType,
|
||||
errorType: field.ErrorTypeNotSupported,
|
||||
errorDetail: `supported values: "MustRunAs", "RunAsAny", "MayRunAs"`,
|
||||
},
|
||||
"invalid selinux strategy type": {
|
||||
psp: invalidSELinuxStratType,
|
||||
errorType: field.ErrorTypeNotSupported,
|
||||
errorDetail: `supported values: "MustRunAs", "RunAsAny"`,
|
||||
},
|
||||
"invalid sup group strategy type": {
|
||||
psp: invalidSupGroupStratType,
|
||||
errorType: field.ErrorTypeNotSupported,
|
||||
errorDetail: `supported values: "MayRunAs", "MustRunAs", "RunAsAny"`,
|
||||
},
|
||||
"invalid fs group strategy type": {
|
||||
psp: invalidFSGroupStratType,
|
||||
errorType: field.ErrorTypeNotSupported,
|
||||
errorDetail: `supported values: "MayRunAs", "MustRunAs", "RunAsAny"`,
|
||||
},
|
||||
"invalid uid": {
|
||||
psp: invalidUIDPSP,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: "min cannot be negative",
|
||||
},
|
||||
"invalid gid": {
|
||||
psp: invalidGIDPSP,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: "min cannot be negative",
|
||||
},
|
||||
"missing object meta name": {
|
||||
psp: missingObjectMetaName,
|
||||
errorType: field.ErrorTypeRequired,
|
||||
errorDetail: "name or generateName is required",
|
||||
},
|
||||
"invalid range min greater than max": {
|
||||
psp: invalidRangeMinGreaterThanMax,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: "min cannot be greater than max",
|
||||
},
|
||||
"invalid range negative min": {
|
||||
psp: invalidRangeNegativeMin,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: "min cannot be negative",
|
||||
},
|
||||
"invalid range negative max": {
|
||||
psp: invalidRangeNegativeMax,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: "max cannot be negative",
|
||||
},
|
||||
"non-empty required drops and all caps are allowed by a wildcard": {
|
||||
psp: wildcardAllowedCapAndRequiredDrop,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: "must be empty when all capabilities are allowed by a wildcard",
|
||||
},
|
||||
"invalid required caps": {
|
||||
psp: requiredCapAddAndDrop,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: "capability is listed in defaultAddCapabilities and requiredDropCapabilities",
|
||||
},
|
||||
"allowed cap listed in required drops": {
|
||||
psp: allowedCapListedInRequiredDrop,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: "capability is listed in allowedCapabilities and requiredDropCapabilities",
|
||||
},
|
||||
"invalid AppArmor default profile": {
|
||||
psp: invalidAppArmorDefault,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: "invalid AppArmor profile name: \"not-good\"",
|
||||
},
|
||||
"invalid AppArmor allowed profile": {
|
||||
psp: invalidAppArmorAllowed,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: "invalid AppArmor profile name: \"not-good\"",
|
||||
},
|
||||
"invalid allowed unsafe sysctl pattern": {
|
||||
psp: invalidAllowedUnsafeSysctlPattern,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: fmt.Sprintf("must have at most 253 characters and match regex %s", SysctlContainSlashPatternFmt),
|
||||
},
|
||||
"invalid forbidden sysctl pattern": {
|
||||
psp: invalidForbiddenSysctlPattern,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: fmt.Sprintf("must have at most 253 characters and match regex %s", SysctlContainSlashPatternFmt),
|
||||
},
|
||||
"invalid overlapping sysctl pattern": {
|
||||
psp: invalidOverlappingSysctls,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: fmt.Sprintf("sysctl overlaps with %s", invalidOverlappingSysctls.Spec.ForbiddenSysctls[0]),
|
||||
},
|
||||
"invalid duplicated sysctls": {
|
||||
psp: invalidDuplicatedSysctls,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: fmt.Sprintf("sysctl overlaps with %s", invalidDuplicatedSysctls.Spec.AllowedUnsafeSysctls[0]),
|
||||
},
|
||||
"invalid seccomp default profile": {
|
||||
psp: invalidSeccompDefault,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: "must be a valid seccomp profile",
|
||||
},
|
||||
"invalid seccomp allow any default profile": {
|
||||
psp: invalidSeccompAllowAnyDefault,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: "must be a valid seccomp profile",
|
||||
},
|
||||
"invalid seccomp allowed profile": {
|
||||
psp: invalidSeccompAllowed,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: "must be a valid seccomp profile",
|
||||
},
|
||||
"invalid defaultAllowPrivilegeEscalation": {
|
||||
psp: invalidDefaultAllowPrivilegeEscalation,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: "Cannot set DefaultAllowPrivilegeEscalation to true without also setting AllowPrivilegeEscalation to true",
|
||||
},
|
||||
"invalid allowed host path empty path": {
|
||||
psp: invalidAllowedHostPathMissingPath,
|
||||
errorType: field.ErrorTypeRequired,
|
||||
errorDetail: "is required",
|
||||
},
|
||||
"invalid allowed host path with backsteps": {
|
||||
psp: invalidAllowedHostPathBacksteps,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: "must not contain '..'",
|
||||
},
|
||||
"empty flex volume driver": {
|
||||
psp: emptyFlexDriver,
|
||||
errorType: field.ErrorTypeRequired,
|
||||
errorDetail: "must specify a driver",
|
||||
},
|
||||
"CSI policy with empty allowed driver list": {
|
||||
psp: allowedCSIDriverPSP,
|
||||
errorType: field.ErrorTypeRequired,
|
||||
},
|
||||
"invalid allowedProcMountTypes": {
|
||||
psp: invalidProcMount,
|
||||
errorType: field.ErrorTypeNotSupported,
|
||||
errorDetail: `supported values: "Default", "Unmasked"`,
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range errorCases {
|
||||
errs := ValidatePodSecurityPolicy(v.psp)
|
||||
if len(errs) == 0 {
|
||||
t.Errorf("%s expected errors but got none", k)
|
||||
continue
|
||||
}
|
||||
if errs[0].Type != v.errorType {
|
||||
t.Errorf("[%s] received an unexpected error type. Expected: '%s' got: '%s'", k, v.errorType, errs[0].Type)
|
||||
}
|
||||
if errs[0].Detail != v.errorDetail {
|
||||
t.Errorf("[%s] received an unexpected error detail. Expected '%s' got: '%s'", k, v.errorDetail, errs[0].Detail)
|
||||
}
|
||||
}
|
||||
|
||||
// Update error is different for 'missing object meta name'.
|
||||
errorCases["missing object meta name"] = testCase{
|
||||
psp: errorCases["missing object meta name"].psp,
|
||||
errorType: field.ErrorTypeInvalid,
|
||||
errorDetail: "field is immutable",
|
||||
}
|
||||
|
||||
// Should not be able to update to an invalid policy.
|
||||
for k, v := range errorCases {
|
||||
v.psp.ResourceVersion = "444" // Required for updates.
|
||||
errs := ValidatePodSecurityPolicyUpdate(validPSP(), v.psp)
|
||||
if len(errs) == 0 {
|
||||
t.Errorf("[%s] expected update errors but got none", k)
|
||||
continue
|
||||
}
|
||||
if errs[0].Type != v.errorType {
|
||||
t.Errorf("[%s] received an unexpected error type. Expected: '%s' got: '%s'", k, v.errorType, errs[0].Type)
|
||||
}
|
||||
if errs[0].Detail != v.errorDetail {
|
||||
t.Errorf("[%s] received an unexpected error detail. Expected '%s' got: '%s'", k, v.errorDetail, errs[0].Detail)
|
||||
}
|
||||
}
|
||||
|
||||
mustRunAs := validPSP()
|
||||
mustRunAs.Spec.FSGroup.Rule = policy.FSGroupStrategyMustRunAs
|
||||
mustRunAs.Spec.SupplementalGroups.Rule = policy.SupplementalGroupsStrategyMustRunAs
|
||||
mustRunAs.Spec.RunAsUser.Rule = policy.RunAsUserStrategyMustRunAs
|
||||
mustRunAs.Spec.RunAsUser.Ranges = []policy.IDRange{
|
||||
{Min: 1, Max: 1},
|
||||
}
|
||||
mustRunAs.Spec.SELinux.Rule = policy.SELinuxStrategyMustRunAs
|
||||
|
||||
runAsNonRoot := validPSP()
|
||||
runAsNonRoot.Spec.RunAsUser.Rule = policy.RunAsUserStrategyMustRunAsNonRoot
|
||||
|
||||
caseInsensitiveAddDrop := validPSP()
|
||||
caseInsensitiveAddDrop.Spec.DefaultAddCapabilities = []api.Capability{"foo"}
|
||||
caseInsensitiveAddDrop.Spec.RequiredDropCapabilities = []api.Capability{"FOO"}
|
||||
|
||||
caseInsensitiveAllowedDrop := validPSP()
|
||||
caseInsensitiveAllowedDrop.Spec.RequiredDropCapabilities = []api.Capability{"FOO"}
|
||||
caseInsensitiveAllowedDrop.Spec.AllowedCapabilities = []api.Capability{"foo"}
|
||||
|
||||
validAppArmor := validPSP()
|
||||
validAppArmor.Annotations = map[string]string{
|
||||
v1.AppArmorBetaDefaultProfileAnnotationKey: v1.AppArmorBetaProfileRuntimeDefault,
|
||||
v1.AppArmorBetaAllowedProfilesAnnotationKey: v1.AppArmorBetaProfileRuntimeDefault + "," + v1.AppArmorBetaProfileNamePrefix + "foo",
|
||||
}
|
||||
|
||||
withForbiddenSysctl := validPSP()
|
||||
withForbiddenSysctl.Spec.ForbiddenSysctls = []string{"net.*"}
|
||||
|
||||
withAllowedUnsafeSysctl := validPSP()
|
||||
withAllowedUnsafeSysctl.Spec.AllowedUnsafeSysctls = []string{"net.ipv4.tcp_max_syn_backlog"}
|
||||
|
||||
validSeccomp := validPSP()
|
||||
validSeccomp.Annotations = map[string]string{
|
||||
seccompDefaultProfileAnnotationKey: api.SeccompProfileRuntimeDefault,
|
||||
seccompAllowedProfilesAnnotationKey: api.SeccompProfileRuntimeDefault + ",unconfined,localhost/foo,*",
|
||||
}
|
||||
|
||||
validDefaultAllowPrivilegeEscalation := validPSP()
|
||||
pe = true
|
||||
validDefaultAllowPrivilegeEscalation.Spec.DefaultAllowPrivilegeEscalation = &pe
|
||||
validDefaultAllowPrivilegeEscalation.Spec.AllowPrivilegeEscalation = true
|
||||
|
||||
flexvolumeWhenFlexVolumesAllowed := validPSP()
|
||||
flexvolumeWhenFlexVolumesAllowed.Spec.Volumes = []policy.FSType{policy.FlexVolume}
|
||||
flexvolumeWhenFlexVolumesAllowed.Spec.AllowedFlexVolumes = []policy.AllowedFlexVolume{
|
||||
{Driver: "example/driver1"},
|
||||
}
|
||||
|
||||
flexvolumeWhenAllVolumesAllowed := validPSP()
|
||||
flexvolumeWhenAllVolumesAllowed.Spec.Volumes = []policy.FSType{policy.All}
|
||||
flexvolumeWhenAllVolumesAllowed.Spec.AllowedFlexVolumes = []policy.AllowedFlexVolume{
|
||||
{Driver: "example/driver2"},
|
||||
}
|
||||
|
||||
validProcMount := validPSP()
|
||||
validProcMount.Spec.AllowedProcMountTypes = []api.ProcMountType{api.DefaultProcMount, api.UnmaskedProcMount}
|
||||
|
||||
allowedCSIDriversWithCSIFsType := validPSP()
|
||||
allowedCSIDriversWithCSIFsType.Spec.Volumes = []policy.FSType{policy.CSI}
|
||||
allowedCSIDriversWithCSIFsType.Spec.AllowedCSIDrivers = []policy.AllowedCSIDriver{{Name: "foo"}}
|
||||
|
||||
allowedCSIDriversWithAllFsTypes := validPSP()
|
||||
allowedCSIDriversWithAllFsTypes.Spec.Volumes = []policy.FSType{policy.All}
|
||||
allowedCSIDriversWithAllFsTypes.Spec.AllowedCSIDrivers = []policy.AllowedCSIDriver{{Name: "bar"}}
|
||||
|
||||
successCases := map[string]struct {
|
||||
psp *policy.PodSecurityPolicy
|
||||
}{
|
||||
"must run as": {
|
||||
psp: mustRunAs,
|
||||
},
|
||||
"run as any": {
|
||||
psp: validPSP(),
|
||||
},
|
||||
"run as non-root (user only)": {
|
||||
psp: runAsNonRoot,
|
||||
},
|
||||
"comparison for add -> drop is case sensitive": {
|
||||
psp: caseInsensitiveAddDrop,
|
||||
},
|
||||
"comparison for allowed -> drop is case sensitive": {
|
||||
psp: caseInsensitiveAllowedDrop,
|
||||
},
|
||||
"valid AppArmor annotations": {
|
||||
psp: validAppArmor,
|
||||
},
|
||||
"with network sysctls forbidden": {
|
||||
psp: withForbiddenSysctl,
|
||||
},
|
||||
"with unsafe net.ipv4.tcp_max_syn_backlog sysctl allowed": {
|
||||
psp: withAllowedUnsafeSysctl,
|
||||
},
|
||||
"valid seccomp annotations": {
|
||||
psp: validSeccomp,
|
||||
},
|
||||
"valid defaultAllowPrivilegeEscalation as true": {
|
||||
psp: validDefaultAllowPrivilegeEscalation,
|
||||
},
|
||||
"allow white-listed flexVolume when flex volumes are allowed": {
|
||||
psp: flexvolumeWhenFlexVolumesAllowed,
|
||||
},
|
||||
"allow white-listed flexVolume when all volumes are allowed": {
|
||||
psp: flexvolumeWhenAllVolumesAllowed,
|
||||
},
|
||||
"valid allowedProcMountTypes": {
|
||||
psp: validProcMount,
|
||||
},
|
||||
"allowed CSI drivers when FSType policy is set to CSI": {
|
||||
psp: allowedCSIDriversWithCSIFsType,
|
||||
},
|
||||
"allowed CSI drivers when FSType policy is set to All": {
|
||||
psp: allowedCSIDriversWithAllFsTypes,
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range successCases {
|
||||
if errs := ValidatePodSecurityPolicy(v.psp); len(errs) != 0 {
|
||||
t.Errorf("Expected success for %s, got %v", k, errs)
|
||||
}
|
||||
|
||||
// Should be able to update to a valid PSP.
|
||||
v.psp.ResourceVersion = "444" // Required for updates.
|
||||
if errs := ValidatePodSecurityPolicyUpdate(validPSP(), v.psp); len(errs) != 0 {
|
||||
t.Errorf("Expected success for %s update, got %v", k, errs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePSPVolumes(t *testing.T) {
|
||||
validPSP := func() *policy.PodSecurityPolicy {
|
||||
return &policy.PodSecurityPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
|
||||
Spec: policy.PodSecurityPolicySpec{
|
||||
SELinux: policy.SELinuxStrategyOptions{
|
||||
Rule: policy.SELinuxStrategyRunAsAny,
|
||||
},
|
||||
RunAsUser: policy.RunAsUserStrategyOptions{
|
||||
Rule: policy.RunAsUserStrategyRunAsAny,
|
||||
},
|
||||
RunAsGroup: &policy.RunAsGroupStrategyOptions{
|
||||
Rule: policy.RunAsGroupStrategyRunAsAny,
|
||||
},
|
||||
FSGroup: policy.FSGroupStrategyOptions{
|
||||
Rule: policy.FSGroupStrategyRunAsAny,
|
||||
},
|
||||
SupplementalGroups: policy.SupplementalGroupsStrategyOptions{
|
||||
Rule: policy.SupplementalGroupsStrategyRunAsAny,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
volumes := getAllFSTypesAsSet()
|
||||
// add in the * value since that is a pseudo type that is not included by default
|
||||
volumes.Insert(string(policy.All))
|
||||
|
||||
for _, strVolume := range volumes.List() {
|
||||
psp := validPSP()
|
||||
psp.Spec.Volumes = []policy.FSType{policy.FSType(strVolume)}
|
||||
errs := ValidatePodSecurityPolicy(psp)
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("%s validation expected no errors but received %v", strVolume, errs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidSysctlPattern(t *testing.T) {
|
||||
valid := []string{
|
||||
"a.b.c.d",
|
||||
@@ -888,295 +340,3 @@ func TestIsValidSysctlPattern(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePSPRunAsUser(t *testing.T) {
|
||||
var testCases = []struct {
|
||||
name string
|
||||
runAsUserStrategy policy.RunAsUserStrategyOptions
|
||||
fail bool
|
||||
}{
|
||||
{"Invalid RunAsUserStrategy", policy.RunAsUserStrategyOptions{Rule: policy.RunAsUserStrategy("someInvalidStrategy")}, true},
|
||||
{"RunAsUserStrategyMustRunAs", policy.RunAsUserStrategyOptions{Rule: policy.RunAsUserStrategyMustRunAs}, false},
|
||||
{"RunAsUserStrategyMustRunAsNonRoot", policy.RunAsUserStrategyOptions{Rule: policy.RunAsUserStrategyMustRunAsNonRoot}, false},
|
||||
{"RunAsUserStrategyMustRunAsNonRoot With Valid Range", policy.RunAsUserStrategyOptions{Rule: policy.RunAsUserStrategyMustRunAs, Ranges: []policy.IDRange{{Min: 2, Max: 3}, {Min: 4, Max: 5}}}, false},
|
||||
{"RunAsUserStrategyMustRunAsNonRoot With Invalid Range", policy.RunAsUserStrategyOptions{Rule: policy.RunAsUserStrategyMustRunAs, Ranges: []policy.IDRange{{Min: 2, Max: 3}, {Min: 5, Max: 4}}}, true},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
errList := validatePSPRunAsUser(field.NewPath("status"), &testCase.runAsUserStrategy)
|
||||
actualErrors := len(errList)
|
||||
expectedErrors := 1
|
||||
if !testCase.fail {
|
||||
expectedErrors = 0
|
||||
}
|
||||
if actualErrors != expectedErrors {
|
||||
t.Errorf("In testCase %v, expected %v errors, got %v errors", testCase.name, expectedErrors, actualErrors)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePSPFSGroup(t *testing.T) {
|
||||
var testCases = []struct {
|
||||
name string
|
||||
fsGroupStrategy policy.FSGroupStrategyOptions
|
||||
fail bool
|
||||
}{
|
||||
{"Invalid FSGroupStrategy", policy.FSGroupStrategyOptions{Rule: policy.FSGroupStrategyType("someInvalidStrategy")}, true},
|
||||
{"FSGroupStrategyMustRunAs", policy.FSGroupStrategyOptions{Rule: policy.FSGroupStrategyMustRunAs}, false},
|
||||
{"FSGroupStrategyMayRunAs", policy.FSGroupStrategyOptions{Rule: policy.FSGroupStrategyMayRunAs, Ranges: []policy.IDRange{{Min: 1, Max: 5}}}, false},
|
||||
{"FSGroupStrategyRunAsAny", policy.FSGroupStrategyOptions{Rule: policy.FSGroupStrategyRunAsAny}, false},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
errList := validatePSPFSGroup(field.NewPath("Status"), &testCase.fsGroupStrategy)
|
||||
actualErrors := len(errList)
|
||||
expectedErrors := 1
|
||||
if !testCase.fail {
|
||||
expectedErrors = 0
|
||||
}
|
||||
if actualErrors != expectedErrors {
|
||||
t.Errorf("In testCase %v, expected %v errors, got %v errors", testCase.name, expectedErrors, actualErrors)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePSPSupplementalGroup(t *testing.T) {
|
||||
var testCases = []struct {
|
||||
name string
|
||||
supplementalGroupStrategy policy.SupplementalGroupsStrategyOptions
|
||||
fail bool
|
||||
}{
|
||||
{"Invalid SupplementalGroupStrategy", policy.SupplementalGroupsStrategyOptions{Rule: policy.SupplementalGroupsStrategyType("someInvalidStrategy")}, true},
|
||||
{"SupplementalGroupsStrategyMustRunAs", policy.SupplementalGroupsStrategyOptions{Rule: policy.SupplementalGroupsStrategyMustRunAs}, false},
|
||||
{"SupplementalGroupsStrategyMayRunAs", policy.SupplementalGroupsStrategyOptions{Rule: policy.SupplementalGroupsStrategyMayRunAs, Ranges: []policy.IDRange{{Min: 1, Max: 5}}}, false},
|
||||
{"SupplementalGroupsStrategyRunAsAny", policy.SupplementalGroupsStrategyOptions{Rule: policy.SupplementalGroupsStrategyRunAsAny}, false},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
errList := validatePSPSupplementalGroup(field.NewPath("Status"), &testCase.supplementalGroupStrategy)
|
||||
actualErrors := len(errList)
|
||||
expectedErrors := 1
|
||||
if !testCase.fail {
|
||||
expectedErrors = 0
|
||||
}
|
||||
if actualErrors != expectedErrors {
|
||||
t.Errorf("In testCase %v, expected %v errors, got %v errors", testCase.name, expectedErrors, actualErrors)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePSPRunAsGroup(t *testing.T) {
|
||||
var testCases = []struct {
|
||||
name string
|
||||
runAsGroup policy.RunAsGroupStrategyOptions
|
||||
fail bool
|
||||
}{
|
||||
{"RunAsGroupStrategyMayRunAs", policy.RunAsGroupStrategyOptions{Rule: policy.RunAsGroupStrategyMayRunAs, Ranges: []policy.IDRange{{Min: 1, Max: 5}}}, false},
|
||||
{"RunAsGroupStrategyMustRunAs", policy.RunAsGroupStrategyOptions{Rule: policy.RunAsGroupStrategyMustRunAs, Ranges: []policy.IDRange{{Min: 1, Max: 5}}}, false},
|
||||
{"RunAsGroupStrategyRunAsAny", policy.RunAsGroupStrategyOptions{Rule: policy.RunAsGroupStrategyRunAsAny}, false},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
errList := validatePSPRunAsGroup(field.NewPath("Status"), &testCase.runAsGroup)
|
||||
actualErrors := len(errList)
|
||||
expectedErrors := 1
|
||||
if !testCase.fail {
|
||||
expectedErrors = 0
|
||||
}
|
||||
if actualErrors != expectedErrors {
|
||||
t.Errorf("In testCase %v, expected %v errors, got %v errors", testCase.name, expectedErrors, actualErrors)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePSPSELinux(t *testing.T) {
|
||||
var testCases = []struct {
|
||||
name string
|
||||
selinux policy.SELinuxStrategyOptions
|
||||
fail bool
|
||||
}{
|
||||
{"SELinuxStrategyMustRunAs",
|
||||
policy.SELinuxStrategyOptions{
|
||||
Rule: policy.SELinuxStrategyMustRunAs,
|
||||
SELinuxOptions: &api.SELinuxOptions{Level: "s9:z0,z1"}}, false},
|
||||
{"SELinuxStrategyMustRunAs",
|
||||
policy.SELinuxStrategyOptions{
|
||||
Rule: policy.SELinuxStrategyMustRunAs,
|
||||
SELinuxOptions: &api.SELinuxOptions{Level: "s0"}}, false},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
errList := validatePSPSELinux(field.NewPath("Status"), &testCase.selinux)
|
||||
actualErrors := len(errList)
|
||||
expectedErrors := 1
|
||||
if !testCase.fail {
|
||||
expectedErrors = 0
|
||||
}
|
||||
if actualErrors != expectedErrors {
|
||||
t.Errorf("In testCase %v, expected %v errors, got %v errors", testCase.name, expectedErrors, actualErrors)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRuntimeClassStrategy(t *testing.T) {
|
||||
var testCases = []struct {
|
||||
name string
|
||||
strategy *policy.RuntimeClassStrategyOptions
|
||||
expectErrors bool
|
||||
}{{
|
||||
name: "nil strategy",
|
||||
strategy: nil,
|
||||
}, {
|
||||
name: "empty strategy",
|
||||
strategy: &policy.RuntimeClassStrategyOptions{},
|
||||
}, {
|
||||
name: "allow all strategy",
|
||||
strategy: &policy.RuntimeClassStrategyOptions{
|
||||
AllowedRuntimeClassNames: []string{"*"},
|
||||
},
|
||||
}, {
|
||||
name: "valid defaulting & allow all",
|
||||
strategy: &policy.RuntimeClassStrategyOptions{
|
||||
DefaultRuntimeClassName: pointer.String("native"),
|
||||
AllowedRuntimeClassNames: []string{"*"},
|
||||
},
|
||||
}, {
|
||||
name: "valid defaulting & allow explicit",
|
||||
strategy: &policy.RuntimeClassStrategyOptions{
|
||||
DefaultRuntimeClassName: pointer.String("native"),
|
||||
AllowedRuntimeClassNames: []string{"foo", "native", "sandboxed"},
|
||||
},
|
||||
}, {
|
||||
name: "valid whitelisting",
|
||||
strategy: &policy.RuntimeClassStrategyOptions{
|
||||
AllowedRuntimeClassNames: []string{"foo", "native", "sandboxed"},
|
||||
},
|
||||
}, {
|
||||
name: "invalid default name",
|
||||
strategy: &policy.RuntimeClassStrategyOptions{
|
||||
DefaultRuntimeClassName: pointer.String("foo bar"),
|
||||
},
|
||||
expectErrors: true,
|
||||
}, {
|
||||
name: "disallowed default",
|
||||
strategy: &policy.RuntimeClassStrategyOptions{
|
||||
DefaultRuntimeClassName: pointer.String("foo"),
|
||||
AllowedRuntimeClassNames: []string{"native", "sandboxed"},
|
||||
},
|
||||
expectErrors: true,
|
||||
}, {
|
||||
name: "nothing allowed default",
|
||||
strategy: &policy.RuntimeClassStrategyOptions{
|
||||
DefaultRuntimeClassName: pointer.String("foo"),
|
||||
},
|
||||
expectErrors: true,
|
||||
}, {
|
||||
name: "invalid whitelist name",
|
||||
strategy: &policy.RuntimeClassStrategyOptions{
|
||||
AllowedRuntimeClassNames: []string{"native", "sandboxed", "foo*"},
|
||||
},
|
||||
expectErrors: true,
|
||||
}, {
|
||||
name: "duplicate whitelist names",
|
||||
strategy: &policy.RuntimeClassStrategyOptions{
|
||||
AllowedRuntimeClassNames: []string{"native", "sandboxed", "native"},
|
||||
},
|
||||
expectErrors: true,
|
||||
}, {
|
||||
name: "allow all redundant whitelist",
|
||||
strategy: &policy.RuntimeClassStrategyOptions{
|
||||
AllowedRuntimeClassNames: []string{"*", "sandboxed", "native"},
|
||||
},
|
||||
expectErrors: true,
|
||||
}}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
errs := validateRuntimeClassStrategy(field.NewPath(""), test.strategy)
|
||||
if test.expectErrors {
|
||||
assert.NotEmpty(t, errs)
|
||||
} else {
|
||||
assert.Empty(t, errs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllowEphemeralVolumeType(t *testing.T) {
|
||||
pspWithoutGenericVolume := func() *policy.PodSecurityPolicy {
|
||||
return &policy.PodSecurityPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "psp",
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Spec: policy.PodSecurityPolicySpec{
|
||||
RunAsUser: policy.RunAsUserStrategyOptions{
|
||||
Rule: policy.RunAsUserStrategyMustRunAs,
|
||||
},
|
||||
SupplementalGroups: policy.SupplementalGroupsStrategyOptions{
|
||||
Rule: policy.SupplementalGroupsStrategyMustRunAs,
|
||||
},
|
||||
SELinux: policy.SELinuxStrategyOptions{
|
||||
Rule: policy.SELinuxStrategyMustRunAs,
|
||||
},
|
||||
FSGroup: policy.FSGroupStrategyOptions{
|
||||
Rule: policy.FSGroupStrategyMustRunAs,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
pspWithGenericVolume := func() *policy.PodSecurityPolicy {
|
||||
psp := pspWithoutGenericVolume()
|
||||
psp.Spec.Volumes = append(psp.Spec.Volumes, policy.Ephemeral)
|
||||
return psp
|
||||
}
|
||||
pspNil := func() *policy.PodSecurityPolicy {
|
||||
return nil
|
||||
}
|
||||
|
||||
pspInfo := []struct {
|
||||
description string
|
||||
hasGenericVolume bool
|
||||
psp func() *policy.PodSecurityPolicy
|
||||
}{{
|
||||
description: "PodSecurityPolicySpec Without GenericVolume",
|
||||
hasGenericVolume: false,
|
||||
psp: pspWithoutGenericVolume,
|
||||
}, {
|
||||
description: "PodSecurityPolicySpec With GenericVolume",
|
||||
hasGenericVolume: true,
|
||||
psp: pspWithGenericVolume,
|
||||
}, {
|
||||
description: "is nil",
|
||||
hasGenericVolume: false,
|
||||
psp: pspNil,
|
||||
}}
|
||||
|
||||
for _, oldPSPInfo := range pspInfo {
|
||||
for _, newPSPInfo := range pspInfo {
|
||||
oldPSP := oldPSPInfo.psp()
|
||||
newPSP := newPSPInfo.psp()
|
||||
if newPSP == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
t.Run(fmt.Sprintf("old PodSecurityPolicySpec %v, new PodSecurityPolicySpec %v", oldPSPInfo.description, newPSPInfo.description), func(t *testing.T) {
|
||||
var errs field.ErrorList
|
||||
if oldPSP == nil {
|
||||
errs = ValidatePodSecurityPolicy(newPSP)
|
||||
} else {
|
||||
errs = ValidatePodSecurityPolicyUpdate(oldPSP, newPSP)
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
t.Errorf("expected no errors, got: %v", errs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user