Merge pull request #123435 from tallclair/apparmor-ga
AppArmor fields API
This commit is contained in:
@@ -204,7 +204,7 @@ func ValidatePodSpecificAnnotationUpdates(newPod, oldPod *core.Pod, fldPath *fie
|
||||
if newVal, exists := newAnnotations[k]; exists && newVal == oldVal {
|
||||
continue // No change.
|
||||
}
|
||||
if strings.HasPrefix(k, v1.AppArmorBetaContainerAnnotationKeyPrefix) {
|
||||
if strings.HasPrefix(k, v1.DeprecatedAppArmorBetaContainerAnnotationKeyPrefix) {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not remove or update AppArmor annotations"))
|
||||
}
|
||||
if k == core.MirrorPodAnnotationKey {
|
||||
@@ -216,7 +216,7 @@ func ValidatePodSpecificAnnotationUpdates(newPod, oldPod *core.Pod, fldPath *fie
|
||||
if _, ok := oldAnnotations[k]; ok {
|
||||
continue // No change.
|
||||
}
|
||||
if strings.HasPrefix(k, v1.AppArmorBetaContainerAnnotationKeyPrefix) {
|
||||
if strings.HasPrefix(k, v1.DeprecatedAppArmorBetaContainerAnnotationKeyPrefix) {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not add AppArmor annotations"))
|
||||
}
|
||||
if k == core.MirrorPodAnnotationKey {
|
||||
@@ -4248,6 +4248,9 @@ func validateWindows(spec *core.PodSpec, fldPath *field.Path) field.ErrorList {
|
||||
securityContext := spec.SecurityContext
|
||||
// validate Pod SecurityContext
|
||||
if securityContext != nil {
|
||||
if securityContext.AppArmorProfile != nil {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("securityContext").Child("appArmorProfile"), "cannot be set for a windows pod"))
|
||||
}
|
||||
if securityContext.SELinuxOptions != nil {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("securityContext").Child("seLinuxOptions"), "cannot be set for a windows pod"))
|
||||
}
|
||||
@@ -4294,6 +4297,9 @@ func validateWindows(spec *core.PodSpec, fldPath *field.Path) field.ErrorList {
|
||||
// TODO: Think if we need to relax this restriction or some of the restrictions
|
||||
if sc != nil {
|
||||
fldPath := cFldPath.Child("securityContext")
|
||||
if sc.AppArmorProfile != nil {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("appArmorProfile"), "cannot be set for a windows pod"))
|
||||
}
|
||||
if sc.SELinuxOptions != nil {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("seLinuxOptions"), "cannot be set for a windows pod"))
|
||||
}
|
||||
@@ -4671,13 +4677,55 @@ func validateSeccompProfileType(fldPath *field.Path, seccompProfileType core.Sec
|
||||
}
|
||||
}
|
||||
|
||||
func ValidateAppArmorProfileField(profile *core.AppArmorProfile, fldPath *field.Path) field.ErrorList {
|
||||
if profile == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
switch profile.Type {
|
||||
case core.AppArmorProfileTypeLocalhost:
|
||||
if profile.LocalhostProfile == nil {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("localhostProfile"), "must be set when AppArmor type is Localhost"))
|
||||
} else {
|
||||
localhostProfile := strings.TrimSpace(*profile.LocalhostProfile)
|
||||
if localhostProfile != *profile.LocalhostProfile {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("localhostProfile"), *profile.LocalhostProfile, "must not be padded with whitespace"))
|
||||
} else if localhostProfile == "" {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("localhostProfile"), "must be set when AppArmor type is Localhost"))
|
||||
}
|
||||
|
||||
const maxLocalhostProfileLength = 4095 // PATH_MAX - 1
|
||||
if len(*profile.LocalhostProfile) > maxLocalhostProfileLength {
|
||||
allErrs = append(allErrs, field.TooLongMaxLength(fldPath.Child("localhostProfile"), *profile.LocalhostProfile, maxLocalhostProfileLength))
|
||||
}
|
||||
}
|
||||
|
||||
case core.AppArmorProfileTypeRuntimeDefault, core.AppArmorProfileTypeUnconfined:
|
||||
if profile.LocalhostProfile != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("localhostProfile"), profile.LocalhostProfile, "can only be set when AppArmor type is Localhost"))
|
||||
}
|
||||
|
||||
case "":
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("type"), "type is required when appArmorProfile is set"))
|
||||
|
||||
default:
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("type"), profile.Type,
|
||||
[]core.AppArmorProfileType{core.AppArmorProfileTypeLocalhost, core.AppArmorProfileTypeRuntimeDefault, core.AppArmorProfileTypeUnconfined}))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
|
||||
}
|
||||
|
||||
func ValidateAppArmorPodAnnotations(annotations map[string]string, spec *core.PodSpec, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
for k, p := range annotations {
|
||||
if !strings.HasPrefix(k, v1.AppArmorBetaContainerAnnotationKeyPrefix) {
|
||||
if !strings.HasPrefix(k, v1.DeprecatedAppArmorBetaContainerAnnotationKeyPrefix) {
|
||||
continue
|
||||
}
|
||||
containerName := strings.TrimPrefix(k, v1.AppArmorBetaContainerAnnotationKeyPrefix)
|
||||
containerName := strings.TrimPrefix(k, v1.DeprecatedAppArmorBetaContainerAnnotationKeyPrefix)
|
||||
if !podSpecHasContainer(spec, containerName) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Key(k), containerName, "container not found"))
|
||||
}
|
||||
@@ -4691,15 +4739,70 @@ func ValidateAppArmorPodAnnotations(annotations map[string]string, spec *core.Po
|
||||
}
|
||||
|
||||
func ValidateAppArmorProfileFormat(profile string) error {
|
||||
if profile == "" || profile == v1.AppArmorBetaProfileRuntimeDefault || profile == v1.AppArmorBetaProfileNameUnconfined {
|
||||
if profile == "" || profile == v1.DeprecatedAppArmorBetaProfileRuntimeDefault || profile == v1.DeprecatedAppArmorBetaProfileNameUnconfined {
|
||||
return nil
|
||||
}
|
||||
if !strings.HasPrefix(profile, v1.AppArmorBetaProfileNamePrefix) {
|
||||
if !strings.HasPrefix(profile, v1.DeprecatedAppArmorBetaProfileNamePrefix) {
|
||||
return fmt.Errorf("invalid AppArmor profile name: %q", profile)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateAppArmorAnnotationsAndFieldsMatchOnCreate validates that AppArmor fields and annotations are consistent.
|
||||
func validateAppArmorAnnotationsAndFieldsMatchOnCreate(objectMeta metav1.ObjectMeta, podSpec *core.PodSpec, specPath *field.Path) field.ErrorList {
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.AppArmorFields) {
|
||||
return nil
|
||||
}
|
||||
if podSpec.OS != nil && podSpec.OS.Name == core.Windows {
|
||||
// Skip consistency check for windows pods.
|
||||
return nil
|
||||
}
|
||||
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
var podProfile *core.AppArmorProfile
|
||||
if podSpec.SecurityContext != nil {
|
||||
podProfile = podSpec.SecurityContext.AppArmorProfile
|
||||
}
|
||||
podshelper.VisitContainersWithPath(podSpec, specPath, func(c *core.Container, cFldPath *field.Path) bool {
|
||||
containerProfile := podProfile
|
||||
if c.SecurityContext != nil && c.SecurityContext.AppArmorProfile != nil {
|
||||
containerProfile = c.SecurityContext.AppArmorProfile
|
||||
}
|
||||
|
||||
if containerProfile == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
key := core.DeprecatedAppArmorAnnotationKeyPrefix + c.Name
|
||||
if annotation, found := objectMeta.Annotations[key]; found {
|
||||
apparmorPath := cFldPath.Child("securityContext").Child("appArmorProfile")
|
||||
|
||||
switch containerProfile.Type {
|
||||
case core.AppArmorProfileTypeUnconfined:
|
||||
if annotation != core.DeprecatedAppArmorAnnotationValueUnconfined {
|
||||
allErrs = append(allErrs, field.Forbidden(apparmorPath.Child("type"), "apparmor type in annotation and field must match"))
|
||||
}
|
||||
|
||||
case core.AppArmorProfileTypeRuntimeDefault:
|
||||
if annotation != core.DeprecatedAppArmorAnnotationValueRuntimeDefault {
|
||||
allErrs = append(allErrs, field.Forbidden(apparmorPath.Child("type"), "apparmor type in annotation and field must match"))
|
||||
}
|
||||
|
||||
case core.AppArmorProfileTypeLocalhost:
|
||||
if !strings.HasPrefix(annotation, core.DeprecatedAppArmorAnnotationValueLocalhostPrefix) {
|
||||
allErrs = append(allErrs, field.Forbidden(apparmorPath.Child("type"), "apparmor type in annotation and field must match"))
|
||||
} else if containerProfile.LocalhostProfile == nil || strings.TrimPrefix(annotation, core.DeprecatedAppArmorAnnotationValueLocalhostPrefix) != *containerProfile.LocalhostProfile {
|
||||
allErrs = append(allErrs, field.Forbidden(apparmorPath.Child("localhostProfile"), "apparmor profile in annotation and field must match"))
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func podSpecHasContainer(spec *core.PodSpec, containerName string) bool {
|
||||
var hasContainer bool
|
||||
podshelper.VisitContainersWithPath(spec, field.NewPath("spec"), func(c *core.Container, _ *field.Path) bool {
|
||||
@@ -4813,6 +4916,7 @@ func validatePodSpecSecurityContext(securityContext *core.PodSecurityContext, sp
|
||||
|
||||
allErrs = append(allErrs, validateSeccompProfileField(securityContext.SeccompProfile, fldPath.Child("seccompProfile"))...)
|
||||
allErrs = append(allErrs, validateWindowsSecurityContextOptions(securityContext.WindowsOptions, fldPath.Child("windowsOptions"))...)
|
||||
allErrs = append(allErrs, ValidateAppArmorProfileField(securityContext.AppArmorProfile, fldPath.Child("appArmorProfile"))...)
|
||||
}
|
||||
|
||||
return allErrs
|
||||
@@ -4853,6 +4957,7 @@ func ValidatePodCreate(pod *core.Pod, opts PodValidationOptions) field.ErrorList
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("nodeName"), "cannot be set until all schedulingGates have been cleared"))
|
||||
}
|
||||
allErrs = append(allErrs, validateSeccompAnnotationsAndFields(pod.ObjectMeta, &pod.Spec, fldPath)...)
|
||||
allErrs = append(allErrs, validateAppArmorAnnotationsAndFieldsMatchOnCreate(pod.ObjectMeta, &pod.Spec, fldPath)...)
|
||||
|
||||
return allErrs
|
||||
}
|
||||
@@ -5830,6 +5935,7 @@ func ValidatePodTemplateSpec(spec *core.PodTemplateSpec, fldPath *field.Path, op
|
||||
allErrs = append(allErrs, ValidatePodSpecificAnnotations(spec.Annotations, &spec.Spec, fldPath.Child("annotations"), opts)...)
|
||||
allErrs = append(allErrs, ValidatePodSpec(&spec.Spec, nil, fldPath.Child("spec"), opts)...)
|
||||
allErrs = append(allErrs, validateSeccompAnnotationsAndFields(spec.ObjectMeta, &spec.Spec, fldPath.Child("spec"))...)
|
||||
allErrs = append(allErrs, validateAppArmorAnnotationsAndFieldsMatchOnCreate(spec.ObjectMeta, &spec.Spec, fldPath.Child("spec"))...)
|
||||
|
||||
if len(spec.Spec.EphemeralContainers) > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("spec", "ephemeralContainers"), "ephemeral containers not allowed in pod template"))
|
||||
@@ -7098,6 +7204,7 @@ func ValidateSecurityContext(sc *core.SecurityContext, fldPath *field.Path) fiel
|
||||
}
|
||||
|
||||
allErrs = append(allErrs, validateWindowsSecurityContextOptions(sc.WindowsOptions, fldPath.Child("windowsOptions"))...)
|
||||
allErrs = append(allErrs, ValidateAppArmorProfileField(sc.AppArmorProfile, fldPath.Child("appArmorProfile"))...)
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
Reference in New Issue
Block a user