From 839034b46ba2f646b6e78dd9a8b8fc69aa218126 Mon Sep 17 00:00:00 2001 From: "Tim St. Clair" Date: Tue, 16 Aug 2016 16:41:05 -0700 Subject: [PATCH] Validate AppArmor annotations in the API server --- pkg/api/validation/validation.go | 68 ++++++- pkg/api/validation/validation_test.go | 260 ++++++++++--------------- pkg/apis/apps/validation/validation.go | 2 +- 3 files changed, 172 insertions(+), 158 deletions(-) diff --git a/pkg/api/validation/validation.go b/pkg/api/validation/validation.go index 13722e09a4d..b754f39c5cb 100644 --- a/pkg/api/validation/validation.go +++ b/pkg/api/validation/validation.go @@ -36,6 +36,7 @@ import ( "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/capabilities" "k8s.io/kubernetes/pkg/labels" + "k8s.io/kubernetes/pkg/security/apparmor" "k8s.io/kubernetes/pkg/util/intstr" "k8s.io/kubernetes/pkg/util/sets" "k8s.io/kubernetes/pkg/util/validation" @@ -110,7 +111,7 @@ func ValidateDNS1123Subdomain(value string, fldPath *field.Path) field.ErrorList return allErrs } -func ValidatePodSpecificAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList { +func ValidatePodSpecificAnnotations(annotations map[string]string, spec *api.PodSpec, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if annotations[api.AffinityAnnotationKey] != "" { allErrs = append(allErrs, ValidateAffinityInPodAnnotations(annotations, fldPath)...) @@ -129,10 +130,36 @@ func ValidatePodSpecificAnnotations(annotations map[string]string, fldPath *fiel } allErrs = append(allErrs, ValidateSeccompPodAnnotations(annotations, fldPath)...) + allErrs = append(allErrs, ValidateAppArmorPodAnnotations(annotations, spec, fldPath)...) return allErrs } +func ValidatePodSpecificAnnotationUpdates(newPod, oldPod *api.Pod, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + newAnnotations := newPod.Annotations + oldAnnotations := oldPod.Annotations + for k, oldVal := range oldAnnotations { + if newAnnotations[k] == oldVal { + continue // No change. + } + if strings.HasPrefix(k, apparmor.ContainerAnnotationKeyPrefix) { + allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not update AppArmor annotations")) + } + } + // Check for removals. + for k := range newAnnotations { + if _, ok := oldAnnotations[k]; ok { + continue // No change. + } + if strings.HasPrefix(k, apparmor.ContainerAnnotationKeyPrefix) { + allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not remove AppArmor annotations")) + } + } + allErrs = append(allErrs, ValidatePodSpecificAnnotations(newAnnotations, &newPod.Spec, fldPath)...) + return allErrs +} + func ValidateEndpointsSpecificAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} // TODO: remove this after we EOL the annotation. @@ -1717,7 +1744,7 @@ func validateTolerations(tolerations []api.Toleration, fldPath *field.Path) fiel func ValidatePod(pod *api.Pod) field.ErrorList { fldPath := field.NewPath("metadata") allErrs := ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName, fldPath) - allErrs = append(allErrs, ValidatePodSpecificAnnotations(pod.ObjectMeta.Annotations, fldPath.Child("annotations"))...) + allErrs = append(allErrs, ValidatePodSpecificAnnotations(pod.ObjectMeta.Annotations, &pod.Spec, fldPath.Child("annotations"))...) allErrs = append(allErrs, ValidatePodSpec(&pod.Spec, field.NewPath("spec"))...) return allErrs } @@ -2033,6 +2060,39 @@ func ValidateSeccompPodAnnotations(annotations map[string]string, fldPath *field return allErrs } +func ValidateAppArmorPodAnnotations(annotations map[string]string, spec *api.PodSpec, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + for k, p := range annotations { + if !strings.HasPrefix(k, apparmor.ContainerAnnotationKeyPrefix) { + continue + } + containerName := strings.TrimPrefix(k, apparmor.ContainerAnnotationKeyPrefix) + if !podSpecHasContainer(spec, containerName) { + allErrs = append(allErrs, field.Invalid(fldPath.Child(k), containerName, "container not found")) + } + + if err := apparmor.ValidateProfileFormat(p); err != nil { + allErrs = append(allErrs, field.Invalid(fldPath.Child(k), p, err.Error())) + } + } + + return allErrs +} + +func podSpecHasContainer(spec *api.PodSpec, containerName string) bool { + for _, c := range spec.InitContainers { + if c.Name == containerName { + return true + } + } + for _, c := range spec.Containers { + if c.Name == containerName { + return true + } + } + return false +} + // ValidatePodSecurityContext test that the specified PodSecurityContext has valid data. func ValidatePodSecurityContext(securityContext *api.PodSecurityContext, spec *api.PodSpec, specPath, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} @@ -2081,7 +2141,7 @@ func ValidateContainerUpdates(newContainers, oldContainers []api.Container, fldP func ValidatePodUpdate(newPod, oldPod *api.Pod) field.ErrorList { fldPath := field.NewPath("metadata") allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath) - allErrs = append(allErrs, ValidatePodSpecificAnnotations(newPod.ObjectMeta.Annotations, fldPath.Child("annotations"))...) + allErrs = append(allErrs, ValidatePodSpecificAnnotationUpdates(newPod, oldPod, fldPath.Child("annotations"))...) specPath := field.NewPath("spec") // validate updateable fields: @@ -2473,7 +2533,7 @@ func ValidatePodTemplateSpec(spec *api.PodTemplateSpec, fldPath *field.Path) fie allErrs := field.ErrorList{} allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.Labels, fldPath.Child("labels"))...) allErrs = append(allErrs, ValidateAnnotations(spec.Annotations, fldPath.Child("annotations"))...) - allErrs = append(allErrs, ValidatePodSpecificAnnotations(spec.Annotations, fldPath.Child("annotations"))...) + allErrs = append(allErrs, ValidatePodSpecificAnnotations(spec.Annotations, &spec.Spec, fldPath.Child("annotations"))...) allErrs = append(allErrs, ValidatePodSpec(&spec.Spec, fldPath.Child("spec"))...) return allErrs } diff --git a/pkg/api/validation/validation_test.go b/pkg/api/validation/validation_test.go index bf9cfd2bebd..763f5b8f37c 100644 --- a/pkg/api/validation/validation_test.go +++ b/pkg/api/validation/validation_test.go @@ -29,6 +29,7 @@ import ( "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/capabilities" + "k8s.io/kubernetes/pkg/security/apparmor" "k8s.io/kubernetes/pkg/util/intstr" "k8s.io/kubernetes/pkg/util/sets" "k8s.io/kubernetes/pkg/util/validation/field" @@ -3186,6 +3187,11 @@ func TestValidatePodSpec(t *testing.T) { } func TestValidatePod(t *testing.T) { + validPodSpec := api.PodSpec{ + Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, + RestartPolicy: api.RestartPolicyAlways, + DNSPolicy: api.DNSClusterFirst, + } successCases := []api.Pod{ { // Basic fields. ObjectMeta: api.ObjectMeta{Name: "123", Namespace: "ns"}, @@ -3305,11 +3311,7 @@ func TestValidatePod(t *testing.T) { }}`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, { // Serialized pod anti affinity with different Label Operators in affinity requirements in annotations. ObjectMeta: api.ObjectMeta{ @@ -3357,11 +3359,7 @@ func TestValidatePod(t *testing.T) { }}`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, { // populate tolerations equal operator in annotations. ObjectMeta: api.ObjectMeta{ @@ -3377,11 +3375,7 @@ func TestValidatePod(t *testing.T) { }]`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, { // populate tolerations exists operator in annotations. ObjectMeta: api.ObjectMeta{ @@ -3396,11 +3390,7 @@ func TestValidatePod(t *testing.T) { }]`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, { // empty operator is ok for toleration ObjectMeta: api.ObjectMeta{ @@ -3415,11 +3405,7 @@ func TestValidatePod(t *testing.T) { }]`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, { // empty efffect is ok for toleration ObjectMeta: api.ObjectMeta{ @@ -3434,11 +3420,7 @@ func TestValidatePod(t *testing.T) { }]`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, { // docker default seccomp profile ObjectMeta: api.ObjectMeta{ @@ -3448,11 +3430,7 @@ func TestValidatePod(t *testing.T) { api.SeccompPodAnnotationKey: "docker/default", }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, { // unconfined seccomp profile ObjectMeta: api.ObjectMeta{ @@ -3462,11 +3440,7 @@ func TestValidatePod(t *testing.T) { api.SeccompPodAnnotationKey: "unconfined", }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, { // localhost seccomp profile ObjectMeta: api.ObjectMeta{ @@ -3476,11 +3450,7 @@ func TestValidatePod(t *testing.T) { api.SeccompPodAnnotationKey: "localhost/foo", }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, { // localhost seccomp profile for a container ObjectMeta: api.ObjectMeta{ @@ -3490,11 +3460,42 @@ func TestValidatePod(t *testing.T) { api.SeccompContainerAnnotationKeyPrefix + "foo": "localhost/foo", }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, + Spec: validPodSpec, + }, + { // default AppArmor profile for a container + ObjectMeta: api.ObjectMeta{ + Name: "123", + Namespace: "ns", + Annotations: map[string]string{ + apparmor.ContainerAnnotationKeyPrefix + "ctr": apparmor.ProfileRuntimeDefault, + }, }, + Spec: validPodSpec, + }, + { // default AppArmor profile for an init container + ObjectMeta: api.ObjectMeta{ + Name: "123", + Namespace: "ns", + Annotations: map[string]string{ + apparmor.ContainerAnnotationKeyPrefix + "init-ctr": apparmor.ProfileRuntimeDefault, + }, + }, + Spec: api.PodSpec{ + InitContainers: []api.Container{{Name: "init-ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, + Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, + RestartPolicy: api.RestartPolicyAlways, + DNSPolicy: api.DNSClusterFirst, + }, + }, + { // localhost AppArmor profile for a container + ObjectMeta: api.ObjectMeta{ + Name: "123", + Namespace: "ns", + Annotations: map[string]string{ + apparmor.ContainerAnnotationKeyPrefix + "ctr": apparmor.ProfileNamePrefix + "foo", + }, + }, + Spec: validPodSpec, }, } for _, pod := range successCases { @@ -3552,11 +3553,7 @@ func TestValidatePod(t *testing.T) { `, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "invalid node selector requirement in node affinity in pod annotations, operator can't be null": { ObjectMeta: api.ObjectMeta{ @@ -3573,11 +3570,7 @@ func TestValidatePod(t *testing.T) { }}}`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "invalid preferredSchedulingTerm in node affinity in pod annotations, weight should be in range 1-100": { ObjectMeta: api.ObjectMeta{ @@ -3599,11 +3592,7 @@ func TestValidatePod(t *testing.T) { ]}}`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "invalid requiredDuringSchedulingIgnoredDuringExecution node selector, nodeSelectorTerms must have at least one term": { ObjectMeta: api.ObjectMeta{ @@ -3618,11 +3607,7 @@ func TestValidatePod(t *testing.T) { }}`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "invalid requiredDuringSchedulingIgnoredDuringExecution node selector term, matchExpressions must have at least one node selector requirement": { ObjectMeta: api.ObjectMeta{ @@ -3639,11 +3624,7 @@ func TestValidatePod(t *testing.T) { }}`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "invalid weight in preferredDuringSchedulingIgnoredDuringExecution in pod affinity annotations, weight should be in range 1-100": { ObjectMeta: api.ObjectMeta{ @@ -3668,11 +3649,7 @@ func TestValidatePod(t *testing.T) { }]}}`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "invalid labelSelector in preferredDuringSchedulingIgnoredDuringExecution in podaffinity annotations, values should be empty if the operator is Exists": { ObjectMeta: api.ObjectMeta{ @@ -3697,11 +3674,7 @@ func TestValidatePod(t *testing.T) { }]}}`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "invalid name space in preferredDuringSchedulingIgnoredDuringExecution in podaffinity annotations, name space shouldbe valid": { ObjectMeta: api.ObjectMeta{ @@ -3726,11 +3699,7 @@ func TestValidatePod(t *testing.T) { }]}}`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "invalid labelOperator in preferredDuringSchedulingIgnoredDuringExecution in podantiaffinity annotations, labelOperator should be proper": { ObjectMeta: api.ObjectMeta{ @@ -3755,11 +3724,7 @@ func TestValidatePod(t *testing.T) { }]}}`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "invalid pod affinity, empty topologyKey is not allowed for hard pod affinity": { ObjectMeta: api.ObjectMeta{ @@ -3784,11 +3749,7 @@ func TestValidatePod(t *testing.T) { }]}}`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "invalid pod anti-affinity, empty topologyKey is not allowed for hard pod anti-affinity": { ObjectMeta: api.ObjectMeta{ @@ -3813,11 +3774,7 @@ func TestValidatePod(t *testing.T) { }]}}`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "invalid pod anti-affinity, empty topologyKey is not allowed for soft pod affinity": { ObjectMeta: api.ObjectMeta{ @@ -3842,11 +3799,7 @@ func TestValidatePod(t *testing.T) { }]}}`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "invalid toleration key": { ObjectMeta: api.ObjectMeta{ @@ -3862,11 +3815,7 @@ func TestValidatePod(t *testing.T) { }]`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "invalid toleration operator": { ObjectMeta: api.ObjectMeta{ @@ -3882,11 +3831,7 @@ func TestValidatePod(t *testing.T) { }]`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "value must be empty when `operator` is 'Exists'": { ObjectMeta: api.ObjectMeta{ @@ -3902,11 +3847,7 @@ func TestValidatePod(t *testing.T) { }]`, }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "must be a valid pod seccomp profile": { ObjectMeta: api.ObjectMeta{ @@ -3916,11 +3857,7 @@ func TestValidatePod(t *testing.T) { api.SeccompPodAnnotationKey: "foo", }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "must be a valid container seccomp profile": { ObjectMeta: api.ObjectMeta{ @@ -3930,11 +3867,7 @@ func TestValidatePod(t *testing.T) { api.SeccompContainerAnnotationKeyPrefix + "foo": "foo", }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "must be a non-empty container name in seccomp annotation": { ObjectMeta: api.ObjectMeta{ @@ -3944,11 +3877,7 @@ func TestValidatePod(t *testing.T) { api.SeccompContainerAnnotationKeyPrefix: "foo", }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "must be a non-empty container profile in seccomp annotation": { ObjectMeta: api.ObjectMeta{ @@ -3958,11 +3887,7 @@ func TestValidatePod(t *testing.T) { api.SeccompContainerAnnotationKeyPrefix + "foo": "", }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "must be a relative path in a node-local seccomp profile annotation": { ObjectMeta: api.ObjectMeta{ @@ -3972,11 +3897,7 @@ func TestValidatePod(t *testing.T) { api.SeccompPodAnnotationKey: "localhost//foo", }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, + Spec: validPodSpec, }, "must not start with '../'": { ObjectMeta: api.ObjectMeta{ @@ -3986,11 +3907,44 @@ func TestValidatePod(t *testing.T) { api.SeccompPodAnnotationKey: "localhost/../foo", }, }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, + Spec: validPodSpec, + }, + "AppArmor profile must apply to a container": { + ObjectMeta: api.ObjectMeta{ + Name: "123", + Namespace: "ns", + Annotations: map[string]string{ + apparmor.ContainerAnnotationKeyPrefix + "ctr": apparmor.ProfileRuntimeDefault, + apparmor.ContainerAnnotationKeyPrefix + "init-ctr": apparmor.ProfileRuntimeDefault, + apparmor.ContainerAnnotationKeyPrefix + "fake-ctr": apparmor.ProfileRuntimeDefault, + }, }, + Spec: api.PodSpec{ + InitContainers: []api.Container{{Name: "init-ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, + Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, + RestartPolicy: api.RestartPolicyAlways, + DNSPolicy: api.DNSClusterFirst, + }, + }, + "AppArmor profile format must be valid": { + ObjectMeta: api.ObjectMeta{ + Name: "123", + Namespace: "ns", + Annotations: map[string]string{ + apparmor.ContainerAnnotationKeyPrefix + "ctr": "bad-name", + }, + }, + Spec: validPodSpec, + }, + "only default AppArmor profile may start with runtime/": { + ObjectMeta: api.ObjectMeta{ + Name: "123", + Namespace: "ns", + Annotations: map[string]string{ + apparmor.ContainerAnnotationKeyPrefix + "ctr": "runtime/foo", + }, + }, + Spec: validPodSpec, }, } for k, v := range errorCases { diff --git a/pkg/apis/apps/validation/validation.go b/pkg/apis/apps/validation/validation.go index ab4859f11a6..67203dbe46b 100644 --- a/pkg/apis/apps/validation/validation.go +++ b/pkg/apis/apps/validation/validation.go @@ -57,7 +57,7 @@ func ValidatePodTemplateSpecForPetSet(template *api.PodTemplateSpec, selector la // allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(template, fldPath)...) allErrs = append(allErrs, unversionedvalidation.ValidateLabels(template.Labels, fldPath.Child("labels"))...) allErrs = append(allErrs, apivalidation.ValidateAnnotations(template.Annotations, fldPath.Child("annotations"))...) - allErrs = append(allErrs, apivalidation.ValidatePodSpecificAnnotations(template.Annotations, fldPath.Child("annotations"))...) + allErrs = append(allErrs, apivalidation.ValidatePodSpecificAnnotations(template.Annotations, &template.Spec, fldPath.Child("annotations"))...) } return allErrs }