mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 05:57:25 +00:00
Merge pull request #91408 from saschagrunert/seccomp-api-migration
Add seccomp GA version skew for pods
This commit is contained in:
commit
26f0227019
@ -22,6 +22,7 @@ go_library(
|
|||||||
"//pkg/features:go_default_library",
|
"//pkg/features:go_default_library",
|
||||||
"//pkg/kubelet/client:go_default_library",
|
"//pkg/kubelet/client:go_default_library",
|
||||||
"//pkg/proxy/util:go_default_library",
|
"//pkg/proxy/util:go_default_library",
|
||||||
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library",
|
||||||
@ -49,6 +50,7 @@ go_test(
|
|||||||
"//pkg/apis/core/install:go_default_library",
|
"//pkg/apis/core/install:go_default_library",
|
||||||
"//pkg/features:go_default_library",
|
"//pkg/features:go_default_library",
|
||||||
"//pkg/kubelet/client:go_default_library",
|
"//pkg/kubelet/client:go_default_library",
|
||||||
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
@ -60,6 +62,7 @@ go_test(
|
|||||||
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
|
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
|
||||||
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
|
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
|
||||||
|
"//vendor/github.com/stretchr/testify/require:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
@ -74,6 +75,8 @@ func (podStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
podutil.DropDisabledPodFields(pod, nil)
|
podutil.DropDisabledPodFields(pod, nil)
|
||||||
|
|
||||||
|
applySeccompVersionSkew(pod)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
|
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
|
||||||
@ -569,3 +572,129 @@ func validateContainer(container string, pod *api.Pod) (string, error) {
|
|||||||
|
|
||||||
return container, nil
|
return container, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// applySeccompVersionSkew implements the version skew behavior described in:
|
||||||
|
// https://github.com/kubernetes/enhancements/blob/master/keps/sig-node/20190717-seccomp-ga.md#version-skew-strategy
|
||||||
|
func applySeccompVersionSkew(pod *api.Pod) {
|
||||||
|
// get possible annotation and field
|
||||||
|
annotation, hasAnnotation := pod.Annotations[v1.SeccompPodAnnotationKey]
|
||||||
|
field, hasField := (*api.SeccompProfile)(nil), false
|
||||||
|
|
||||||
|
if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.SeccompProfile != nil {
|
||||||
|
field = pod.Spec.SecurityContext.SeccompProfile
|
||||||
|
hasField = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// sync field and annotation
|
||||||
|
if hasField && !hasAnnotation {
|
||||||
|
newAnnotation := seccompAnnotationForField(field)
|
||||||
|
|
||||||
|
if newAnnotation != "" {
|
||||||
|
if pod.Annotations == nil {
|
||||||
|
pod.Annotations = map[string]string{}
|
||||||
|
}
|
||||||
|
pod.Annotations[v1.SeccompPodAnnotationKey] = newAnnotation
|
||||||
|
}
|
||||||
|
} else if hasAnnotation && !hasField {
|
||||||
|
newField := seccompFieldForAnnotation(annotation)
|
||||||
|
|
||||||
|
if newField != nil {
|
||||||
|
if pod.Spec.SecurityContext == nil {
|
||||||
|
pod.Spec.SecurityContext = &api.PodSecurityContext{}
|
||||||
|
}
|
||||||
|
pod.Spec.SecurityContext.SeccompProfile = newField
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the containers of the pod
|
||||||
|
podutil.VisitContainers(&pod.Spec, podutil.AllFeatureEnabledContainers(),
|
||||||
|
func(ctr *api.Container, _ podutil.ContainerType) bool {
|
||||||
|
// get possible annotation and field
|
||||||
|
key := api.SeccompContainerAnnotationKeyPrefix + ctr.Name
|
||||||
|
annotation, hasAnnotation := pod.Annotations[key]
|
||||||
|
|
||||||
|
field, hasField := (*api.SeccompProfile)(nil), false
|
||||||
|
if ctr.SecurityContext != nil && ctr.SecurityContext.SeccompProfile != nil {
|
||||||
|
field = ctr.SecurityContext.SeccompProfile
|
||||||
|
hasField = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// sync field and annotation
|
||||||
|
if hasField && !hasAnnotation {
|
||||||
|
newAnnotation := seccompAnnotationForField(field)
|
||||||
|
|
||||||
|
if newAnnotation != "" {
|
||||||
|
if pod.Annotations == nil {
|
||||||
|
pod.Annotations = map[string]string{}
|
||||||
|
}
|
||||||
|
pod.Annotations[key] = newAnnotation
|
||||||
|
}
|
||||||
|
} else if hasAnnotation && !hasField {
|
||||||
|
newField := seccompFieldForAnnotation(annotation)
|
||||||
|
|
||||||
|
if newField != nil {
|
||||||
|
if ctr.SecurityContext == nil {
|
||||||
|
ctr.SecurityContext = &api.SecurityContext{}
|
||||||
|
}
|
||||||
|
ctr.SecurityContext.SeccompProfile = newField
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// seccompFieldForAnnotation takes a pod seccomp profile field and returns the
|
||||||
|
// converted annotation value
|
||||||
|
func seccompAnnotationForField(field *api.SeccompProfile) string {
|
||||||
|
// If only seccomp fields are specified, add the corresponding annotations.
|
||||||
|
// This ensures that the fields are enforced even if the node version
|
||||||
|
// trails the API version
|
||||||
|
switch field.Type {
|
||||||
|
case api.SeccompProfileTypeUnconfined:
|
||||||
|
return v1.SeccompProfileNameUnconfined
|
||||||
|
|
||||||
|
case api.SeccompProfileTypeRuntimeDefault:
|
||||||
|
return v1.SeccompProfileRuntimeDefault
|
||||||
|
|
||||||
|
case api.SeccompProfileTypeLocalhost:
|
||||||
|
if field.LocalhostProfile != nil {
|
||||||
|
return v1.SeccompLocalhostProfileNamePrefix + *field.LocalhostProfile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can only reach this code path if the LocalhostProfile is nil but the
|
||||||
|
// provided field type is SeccompProfileTypeLocalhost or if an unrecognized
|
||||||
|
// type is specified
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// seccompFieldForAnnotation takes a pod annotation and returns the converted
|
||||||
|
// seccomp profile field.
|
||||||
|
func seccompFieldForAnnotation(annotation string) *api.SeccompProfile {
|
||||||
|
// If only seccomp annotations are specified, copy the values into the
|
||||||
|
// corresponding fields. This ensures that existing applications continue
|
||||||
|
// to enforce seccomp, and prevents the kubelet from needing to resolve
|
||||||
|
// annotations & fields.
|
||||||
|
if annotation == v1.SeccompProfileNameUnconfined {
|
||||||
|
return &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined}
|
||||||
|
}
|
||||||
|
|
||||||
|
if annotation == api.SeccompProfileRuntimeDefault || annotation == api.DeprecatedSeccompProfileDockerDefault {
|
||||||
|
return &api.SeccompProfile{Type: api.SeccompProfileTypeRuntimeDefault}
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(annotation, v1.SeccompLocalhostProfileNamePrefix) {
|
||||||
|
localhostProfile := strings.TrimPrefix(annotation, v1.SeccompLocalhostProfileNamePrefix)
|
||||||
|
if localhostProfile != "" {
|
||||||
|
return &api.SeccompProfile{
|
||||||
|
Type: api.SeccompProfileTypeLocalhost,
|
||||||
|
LocalhostProfile: &localhostProfile,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can only reach this code path if the localhostProfile name has a zero
|
||||||
|
// length or if the annotation has an unrecognized value
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -24,6 +24,8 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@ -696,3 +698,408 @@ func TestPodIndexFunc(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func TestApplySeccompVersionSkew(t *testing.T) {
|
||||||
|
const containerName = "container"
|
||||||
|
testProfile := "test"
|
||||||
|
|
||||||
|
for _, test := range []struct {
|
||||||
|
description string
|
||||||
|
pod *api.Pod
|
||||||
|
validation func(*testing.T, *api.Pod)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
description: "Security context nil",
|
||||||
|
pod: &api.Pod{},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.NotNil(t, pod)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Security context not nil",
|
||||||
|
pod: &api.Pod{
|
||||||
|
Spec: api.PodSpec{SecurityContext: &api.PodSecurityContext{}},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.NotNil(t, pod)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Field type unconfined and no annotation present",
|
||||||
|
pod: &api.Pod{
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
SecurityContext: &api.PodSecurityContext{
|
||||||
|
SeccompProfile: &api.SeccompProfile{
|
||||||
|
Type: api.SeccompProfileTypeUnconfined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.Len(t, pod.Annotations, 1)
|
||||||
|
require.Equal(t, v1.SeccompProfileNameUnconfined, pod.Annotations[api.SeccompPodAnnotationKey])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Field type default and no annotation present",
|
||||||
|
pod: &api.Pod{
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
SecurityContext: &api.PodSecurityContext{
|
||||||
|
SeccompProfile: &api.SeccompProfile{
|
||||||
|
Type: api.SeccompProfileTypeRuntimeDefault,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.Len(t, pod.Annotations, 1)
|
||||||
|
require.Equal(t, v1.SeccompProfileRuntimeDefault, pod.Annotations[v1.SeccompPodAnnotationKey])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Field type localhost and no annotation present",
|
||||||
|
pod: &api.Pod{
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
SecurityContext: &api.PodSecurityContext{
|
||||||
|
SeccompProfile: &api.SeccompProfile{
|
||||||
|
Type: api.SeccompProfileTypeLocalhost,
|
||||||
|
LocalhostProfile: &testProfile,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.Len(t, pod.Annotations, 1)
|
||||||
|
require.Equal(t, "localhost/test", pod.Annotations[v1.SeccompPodAnnotationKey])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Field type localhost but profile is nil",
|
||||||
|
pod: &api.Pod{
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
SecurityContext: &api.PodSecurityContext{
|
||||||
|
SeccompProfile: &api.SeccompProfile{
|
||||||
|
Type: api.SeccompProfileTypeLocalhost,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.Len(t, pod.Annotations, 0)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Annotation 'unconfined' and no field present",
|
||||||
|
pod: &api.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Annotations: map[string]string{
|
||||||
|
v1.SeccompPodAnnotationKey: v1.SeccompProfileNameUnconfined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.Equal(t, api.SeccompProfileTypeUnconfined, pod.Spec.SecurityContext.SeccompProfile.Type)
|
||||||
|
require.Nil(t, pod.Spec.SecurityContext.SeccompProfile.LocalhostProfile)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Annotation 'runtime/default' and no field present",
|
||||||
|
pod: &api.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Annotations: map[string]string{
|
||||||
|
v1.SeccompPodAnnotationKey: v1.SeccompProfileRuntimeDefault,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{SecurityContext: &api.PodSecurityContext{}},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.Equal(t, api.SeccompProfileTypeRuntimeDefault, pod.Spec.SecurityContext.SeccompProfile.Type)
|
||||||
|
require.Nil(t, pod.Spec.SecurityContext.SeccompProfile.LocalhostProfile)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Annotation 'docker/default' and no field present",
|
||||||
|
pod: &api.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Annotations: map[string]string{
|
||||||
|
v1.SeccompPodAnnotationKey: v1.DeprecatedSeccompProfileDockerDefault,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{SecurityContext: &api.PodSecurityContext{}},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.Equal(t, api.SeccompProfileTypeRuntimeDefault, pod.Spec.SecurityContext.SeccompProfile.Type)
|
||||||
|
require.Nil(t, pod.Spec.SecurityContext.SeccompProfile.LocalhostProfile)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Annotation 'localhost/test' and no field present",
|
||||||
|
pod: &api.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Annotations: map[string]string{
|
||||||
|
v1.SeccompPodAnnotationKey: v1.SeccompLocalhostProfileNamePrefix + testProfile,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{SecurityContext: &api.PodSecurityContext{}},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.Equal(t, api.SeccompProfileTypeLocalhost, pod.Spec.SecurityContext.SeccompProfile.Type)
|
||||||
|
require.Equal(t, testProfile, *pod.Spec.SecurityContext.SeccompProfile.LocalhostProfile)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Annotation 'localhost/' has zero length",
|
||||||
|
pod: &api.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Annotations: map[string]string{
|
||||||
|
v1.SeccompPodAnnotationKey: v1.SeccompLocalhostProfileNamePrefix,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{SecurityContext: &api.PodSecurityContext{}},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.Nil(t, pod.Spec.SecurityContext.SeccompProfile)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Security context nil (container)",
|
||||||
|
pod: &api.Pod{
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{{}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.NotNil(t, pod)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Security context not nil (container)",
|
||||||
|
pod: &api.Pod{
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{{
|
||||||
|
SecurityContext: &api.SecurityContext{},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.NotNil(t, pod)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Field type unconfined and no annotation present (container)",
|
||||||
|
pod: &api.Pod{
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{
|
||||||
|
{
|
||||||
|
Name: containerName,
|
||||||
|
SecurityContext: &api.SecurityContext{
|
||||||
|
SeccompProfile: &api.SeccompProfile{
|
||||||
|
Type: api.SeccompProfileTypeUnconfined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.Len(t, pod.Annotations, 1)
|
||||||
|
require.Equal(t, v1.SeccompProfileNameUnconfined, pod.Annotations[v1.SeccompContainerAnnotationKeyPrefix+containerName])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Field type runtime/default and no annotation present (container)",
|
||||||
|
pod: &api.Pod{
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{
|
||||||
|
{
|
||||||
|
Name: containerName,
|
||||||
|
SecurityContext: &api.SecurityContext{
|
||||||
|
SeccompProfile: &api.SeccompProfile{
|
||||||
|
Type: api.SeccompProfileTypeRuntimeDefault,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.Len(t, pod.Annotations, 1)
|
||||||
|
require.Equal(t, v1.SeccompProfileRuntimeDefault, pod.Annotations[v1.SeccompContainerAnnotationKeyPrefix+containerName])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Field type localhost and no annotation present (container)",
|
||||||
|
pod: &api.Pod{
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{
|
||||||
|
{
|
||||||
|
Name: containerName,
|
||||||
|
SecurityContext: &api.SecurityContext{
|
||||||
|
SeccompProfile: &api.SeccompProfile{
|
||||||
|
Type: api.SeccompProfileTypeLocalhost,
|
||||||
|
LocalhostProfile: &testProfile,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.Len(t, pod.Annotations, 1)
|
||||||
|
require.Equal(t, "localhost/test", pod.Annotations[v1.SeccompContainerAnnotationKeyPrefix+containerName])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Multiple containers with fields (container)",
|
||||||
|
pod: &api.Pod{
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{
|
||||||
|
{
|
||||||
|
Name: containerName + "1",
|
||||||
|
SecurityContext: &api.SecurityContext{
|
||||||
|
SeccompProfile: &api.SeccompProfile{
|
||||||
|
Type: api.SeccompProfileTypeUnconfined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: containerName + "2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: containerName + "3",
|
||||||
|
SecurityContext: &api.SecurityContext{
|
||||||
|
SeccompProfile: &api.SeccompProfile{
|
||||||
|
Type: api.SeccompProfileTypeRuntimeDefault,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.Len(t, pod.Annotations, 2)
|
||||||
|
require.Equal(t, v1.SeccompProfileNameUnconfined, pod.Annotations[v1.SeccompContainerAnnotationKeyPrefix+containerName+"1"])
|
||||||
|
require.Equal(t, v1.SeccompProfileRuntimeDefault, pod.Annotations[v1.SeccompContainerAnnotationKeyPrefix+containerName+"3"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Annotation 'unconfined' and no field present (container)",
|
||||||
|
pod: &api.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Annotations: map[string]string{
|
||||||
|
v1.SeccompContainerAnnotationKeyPrefix + containerName: v1.SeccompProfileNameUnconfined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{{
|
||||||
|
Name: containerName,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.Equal(t, api.SeccompProfileTypeUnconfined, pod.Spec.Containers[0].SecurityContext.SeccompProfile.Type)
|
||||||
|
require.Nil(t, pod.Spec.Containers[0].SecurityContext.SeccompProfile.LocalhostProfile)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Annotation 'runtime/default' and no field present (container)",
|
||||||
|
pod: &api.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Annotations: map[string]string{
|
||||||
|
v1.SeccompContainerAnnotationKeyPrefix + containerName: v1.SeccompProfileRuntimeDefault,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{{
|
||||||
|
Name: containerName,
|
||||||
|
SecurityContext: &api.SecurityContext{},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.Equal(t, api.SeccompProfileTypeRuntimeDefault, pod.Spec.Containers[0].SecurityContext.SeccompProfile.Type)
|
||||||
|
require.Nil(t, pod.Spec.Containers[0].SecurityContext.SeccompProfile.LocalhostProfile)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Annotation 'docker/default' and no field present (container)",
|
||||||
|
pod: &api.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Annotations: map[string]string{
|
||||||
|
v1.SeccompContainerAnnotationKeyPrefix + containerName: v1.DeprecatedSeccompProfileDockerDefault,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{{
|
||||||
|
Name: containerName,
|
||||||
|
SecurityContext: &api.SecurityContext{},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.Equal(t, api.SeccompProfileTypeRuntimeDefault, pod.Spec.Containers[0].SecurityContext.SeccompProfile.Type)
|
||||||
|
require.Nil(t, pod.Spec.Containers[0].SecurityContext.SeccompProfile.LocalhostProfile)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Multiple containers by annotations (container)",
|
||||||
|
pod: &api.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Annotations: map[string]string{
|
||||||
|
v1.SeccompContainerAnnotationKeyPrefix + containerName + "1": v1.SeccompLocalhostProfileNamePrefix + testProfile,
|
||||||
|
v1.SeccompContainerAnnotationKeyPrefix + containerName + "3": v1.SeccompProfileRuntimeDefault,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{
|
||||||
|
{Name: containerName + "1"},
|
||||||
|
{Name: containerName + "2"},
|
||||||
|
{Name: containerName + "3"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.Equal(t, api.SeccompProfileTypeLocalhost, pod.Spec.Containers[0].SecurityContext.SeccompProfile.Type)
|
||||||
|
require.Equal(t, testProfile, *pod.Spec.Containers[0].SecurityContext.SeccompProfile.LocalhostProfile)
|
||||||
|
require.Equal(t, api.SeccompProfileTypeRuntimeDefault, pod.Spec.Containers[2].SecurityContext.SeccompProfile.Type)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Annotation 'localhost/test' and no field present (container)",
|
||||||
|
pod: &api.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Annotations: map[string]string{
|
||||||
|
v1.SeccompContainerAnnotationKeyPrefix + containerName: v1.SeccompLocalhostProfileNamePrefix + testProfile,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{{
|
||||||
|
Name: containerName,
|
||||||
|
SecurityContext: &api.SecurityContext{},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validation: func(t *testing.T, pod *api.Pod) {
|
||||||
|
require.Equal(t, api.SeccompProfileTypeLocalhost, pod.Spec.Containers[0].SecurityContext.SeccompProfile.Type)
|
||||||
|
require.Equal(t, testProfile, *pod.Spec.Containers[0].SecurityContext.SeccompProfile.LocalhostProfile)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
output := &api.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}},
|
||||||
|
}
|
||||||
|
for i, ctr := range test.pod.Spec.Containers {
|
||||||
|
output.Spec.Containers = append(output.Spec.Containers, api.Container{})
|
||||||
|
if ctr.SecurityContext != nil && ctr.SecurityContext.SeccompProfile != nil {
|
||||||
|
output.Spec.Containers[i].SecurityContext = &api.SecurityContext{
|
||||||
|
SeccompProfile: &api.SeccompProfile{
|
||||||
|
Type: api.SeccompProfileType(ctr.SecurityContext.SeccompProfile.Type),
|
||||||
|
LocalhostProfile: ctr.SecurityContext.SeccompProfile.LocalhostProfile,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
applySeccompVersionSkew(test.pod)
|
||||||
|
test.validation(t, test.pod)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user