Merge pull request #123435 from tallclair/apparmor-ga

AppArmor fields API
This commit is contained in:
Kubernetes Prow Robot
2024-03-06 15:35:14 -08:00
committed by GitHub
103 changed files with 4465 additions and 1427 deletions

View File

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