mirror of https://github.com/k3s-io/k3s
Merge pull request #75680 from tallclair/psp-refactor
Clean up some PodSecurityPolicy codek3s-v1.15.3
commit
ccc90b2ba6
|
@ -49,8 +49,6 @@ go_test(
|
||||||
"//staging/src/k8s.io/api/policy/v1beta1:go_default_library",
|
"//staging/src/k8s.io/api/policy/v1beta1: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/util/diff:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
|
||||||
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
|
|
||||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||||
"//vendor/github.com/stretchr/testify/require:go_default_library",
|
"//vendor/github.com/stretchr/testify/require:go_default_library",
|
||||||
],
|
],
|
||||||
|
|
|
@ -59,10 +59,10 @@ func NewSimpleProvider(psp *policy.PodSecurityPolicy, namespace string, strategy
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultPodSecurityContext sets the default values of the required but not filled fields.
|
// MutatePod sets the default values of the required but not filled fields.
|
||||||
// It modifies the SecurityContext and annotations of the provided pod. Validation should be
|
// Validation should be used after the context is defaulted to ensure it
|
||||||
// used after the context is defaulted to ensure it complies with the required restrictions.
|
// complies with the required restrictions.
|
||||||
func (s *simpleProvider) DefaultPodSecurityContext(pod *api.Pod) error {
|
func (s *simpleProvider) MutatePod(pod *api.Pod) error {
|
||||||
sc := securitycontext.NewPodSecurityContextMutator(pod.Spec.SecurityContext)
|
sc := securitycontext.NewPodSecurityContextMutator(pod.Spec.SecurityContext)
|
||||||
|
|
||||||
if sc.SupplementalGroups() == nil {
|
if sc.SupplementalGroups() == nil {
|
||||||
|
@ -104,13 +104,25 @@ func (s *simpleProvider) DefaultPodSecurityContext(pod *api.Pod) error {
|
||||||
|
|
||||||
pod.Spec.SecurityContext = sc.PodSecurityContext()
|
pod.Spec.SecurityContext = sc.PodSecurityContext()
|
||||||
|
|
||||||
|
for i := range pod.Spec.InitContainers {
|
||||||
|
if err := s.mutateContainer(pod, &pod.Spec.InitContainers[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range pod.Spec.Containers {
|
||||||
|
if err := s.mutateContainer(pod, &pod.Spec.Containers[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultContainerSecurityContext sets the default values of the required but not filled fields.
|
// mutateContainer sets the default values of the required but not filled fields.
|
||||||
// It modifies the SecurityContext of the container and annotations of the pod. Validation should
|
// It modifies the SecurityContext of the container and annotations of the pod. Validation should
|
||||||
// be used after the context is defaulted to ensure it complies with the required restrictions.
|
// be used after the context is defaulted to ensure it complies with the required restrictions.
|
||||||
func (s *simpleProvider) DefaultContainerSecurityContext(pod *api.Pod, container *api.Container) error {
|
func (s *simpleProvider) mutateContainer(pod *api.Pod, container *api.Container) error {
|
||||||
sc := securitycontext.NewEffectiveContainerSecurityContextMutator(
|
sc := securitycontext.NewEffectiveContainerSecurityContextMutator(
|
||||||
securitycontext.NewPodSecurityContextAccessor(pod.Spec.SecurityContext),
|
securitycontext.NewPodSecurityContextAccessor(pod.Spec.SecurityContext),
|
||||||
securitycontext.NewContainerSecurityContextMutator(container.SecurityContext),
|
securitycontext.NewContainerSecurityContextMutator(container.SecurityContext),
|
||||||
|
@ -282,11 +294,22 @@ func (s *simpleProvider) ValidatePod(pod *api.Pod) field.ErrorList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fldPath := field.NewPath("spec", "initContainers")
|
||||||
|
for i := range pod.Spec.InitContainers {
|
||||||
|
allErrs = append(allErrs, s.validateContainer(pod, &pod.Spec.InitContainers[i], fldPath.Index(i))...)
|
||||||
|
}
|
||||||
|
|
||||||
|
fldPath = field.NewPath("spec", "containers")
|
||||||
|
for i := range pod.Spec.Containers {
|
||||||
|
allErrs = append(allErrs, s.validateContainer(pod, &pod.Spec.Containers[i], fldPath.Index(i))...)
|
||||||
|
}
|
||||||
|
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure a container's SecurityContext is in compliance with the given constraints
|
// Ensure a container's SecurityContext is in compliance with the given constraints
|
||||||
func (s *simpleProvider) ValidateContainer(pod *api.Pod, container *api.Container, containerPath *field.Path) field.ErrorList {
|
func (s *simpleProvider) validateContainer(pod *api.Pod, container *api.Container, containerPath *field.Path) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
podSC := securitycontext.NewPodSecurityContextAccessor(pod.Spec.SecurityContext)
|
podSC := securitycontext.NewPodSecurityContextAccessor(pod.Spec.SecurityContext)
|
||||||
|
|
|
@ -20,10 +20,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
@ -31,7 +29,6 @@ import (
|
||||||
policy "k8s.io/api/policy/v1beta1"
|
policy "k8s.io/api/policy/v1beta1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/diff"
|
"k8s.io/apimachinery/pkg/util/diff"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1"
|
k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1"
|
||||||
"k8s.io/kubernetes/pkg/security/apparmor"
|
"k8s.io/kubernetes/pkg/security/apparmor"
|
||||||
|
@ -41,7 +38,7 @@ import (
|
||||||
|
|
||||||
const defaultContainerName = "test-c"
|
const defaultContainerName = "test-c"
|
||||||
|
|
||||||
func TestDefaultPodSecurityContextNonmutating(t *testing.T) {
|
func TestMutatePodNonmutating(t *testing.T) {
|
||||||
// Create a pod with a security context that needs filling in
|
// Create a pod with a security context that needs filling in
|
||||||
createPod := func() *api.Pod {
|
createPod := func() *api.Pod {
|
||||||
return &api.Pod{
|
return &api.Pod{
|
||||||
|
@ -86,26 +83,22 @@ func TestDefaultPodSecurityContextNonmutating(t *testing.T) {
|
||||||
psp := createPSP()
|
psp := createPSP()
|
||||||
|
|
||||||
provider, err := NewSimpleProvider(psp, "namespace", NewSimpleStrategyFactory())
|
provider, err := NewSimpleProvider(psp, "namespace", NewSimpleStrategyFactory())
|
||||||
if err != nil {
|
require.NoError(t, err, "unable to create provider")
|
||||||
t.Fatalf("unable to create provider %v", err)
|
err = provider.MutatePod(pod)
|
||||||
}
|
require.NoError(t, err, "unable to modify pod")
|
||||||
err = provider.DefaultPodSecurityContext(pod)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to create psc %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creating the provider or the security context should not have mutated the psp or pod
|
// Creating the provider or the security context should not have mutated the psp or pod
|
||||||
// since all the strategies were permissive
|
// since all the strategies were permissive
|
||||||
if !reflect.DeepEqual(createPod(), pod) {
|
if !reflect.DeepEqual(createPod(), pod) {
|
||||||
diffs := diff.ObjectDiff(createPod(), pod)
|
diffs := diff.ObjectDiff(createPod(), pod)
|
||||||
t.Errorf("pod was mutated by DefaultPodSecurityContext. diff:\n%s", diffs)
|
t.Errorf("pod was mutated by MutatePod. diff:\n%s", diffs)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(createPSP(), psp) {
|
if !reflect.DeepEqual(createPSP(), psp) {
|
||||||
t.Error("psp was mutated by DefaultPodSecurityContext")
|
t.Error("psp was mutated by MutatePod")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultContainerSecurityContextNonmutating(t *testing.T) {
|
func TestMutateContainerNonmutating(t *testing.T) {
|
||||||
untrue := false
|
untrue := false
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
security *api.SecurityContext
|
security *api.SecurityContext
|
||||||
|
@ -134,7 +127,6 @@ func TestDefaultContainerSecurityContextNonmutating(t *testing.T) {
|
||||||
Name: "psp-sa",
|
Name: "psp-sa",
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
seccomp.AllowedProfilesAnnotationKey: "*",
|
seccomp.AllowedProfilesAnnotationKey: "*",
|
||||||
seccomp.DefaultProfileAnnotationKey: "foo",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: policy.PodSecurityPolicySpec{
|
Spec: policy.PodSecurityPolicySpec{
|
||||||
|
@ -162,27 +154,23 @@ func TestDefaultContainerSecurityContextNonmutating(t *testing.T) {
|
||||||
psp := createPSP()
|
psp := createPSP()
|
||||||
|
|
||||||
provider, err := NewSimpleProvider(psp, "namespace", NewSimpleStrategyFactory())
|
provider, err := NewSimpleProvider(psp, "namespace", NewSimpleStrategyFactory())
|
||||||
if err != nil {
|
require.NoError(t, err, "unable to create provider")
|
||||||
t.Fatalf("unable to create provider %v", err)
|
err = provider.MutatePod(pod)
|
||||||
}
|
require.NoError(t, err, "unable to modify pod")
|
||||||
err = provider.DefaultContainerSecurityContext(pod, &pod.Spec.Containers[0])
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to create container security context %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creating the provider or the security context should not have mutated the psp or pod
|
// Creating the provider or the security context should not have mutated the psp or pod
|
||||||
// since all the strategies were permissive
|
// since all the strategies were permissive
|
||||||
if !reflect.DeepEqual(createPod(), pod) {
|
if !reflect.DeepEqual(createPod(), pod) {
|
||||||
diffs := diff.ObjectDiff(createPod(), pod)
|
diffs := diff.ObjectDiff(createPod(), pod)
|
||||||
t.Errorf("pod was mutated by DefaultContainerSecurityContext. diff:\n%s", diffs)
|
t.Errorf("pod was mutated. diff:\n%s", diffs)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(createPSP(), psp) {
|
if !reflect.DeepEqual(createPSP(), psp) {
|
||||||
t.Error("psp was mutated by DefaultContainerSecurityContext")
|
t.Error("psp was mutated")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidatePodSecurityContextFailures(t *testing.T) {
|
func TestValidatePodFailures(t *testing.T) {
|
||||||
failHostNetworkPod := defaultPod()
|
failHostNetworkPod := defaultPod()
|
||||||
failHostNetworkPod.Spec.SecurityContext.HostNetwork = true
|
failHostNetworkPod.Spec.SecurityContext.HostNetwork = true
|
||||||
|
|
||||||
|
@ -445,19 +433,14 @@ func TestValidatePodSecurityContextFailures(t *testing.T) {
|
||||||
expectedError: "Flexvolume driver is not allowed to be used",
|
expectedError: "Flexvolume driver is not allowed to be used",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for k, v := range errorCases {
|
for name, test := range errorCases {
|
||||||
provider, err := NewSimpleProvider(v.psp, "namespace", NewSimpleStrategyFactory())
|
t.Run(name, func(t *testing.T) {
|
||||||
if err != nil {
|
provider, err := NewSimpleProvider(test.psp, "namespace", NewSimpleStrategyFactory())
|
||||||
t.Fatalf("unable to create provider %v", err)
|
require.NoError(t, err, "unable to create provider")
|
||||||
}
|
errs := provider.ValidatePod(test.pod)
|
||||||
errs := provider.ValidatePod(v.pod)
|
require.NotEmpty(t, errs, "expected validation failure but did not receive errors")
|
||||||
if len(errs) == 0 {
|
assert.Contains(t, errs[0].Error(), test.expectedError, "received unexpected error")
|
||||||
t.Errorf("%s expected validation failure but did not receive errors", k)
|
})
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !strings.Contains(errs[0].Error(), v.expectedError) {
|
|
||||||
t.Errorf("%s received unexpected error %v", k, errs)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,6 +487,7 @@ func TestValidateContainerFailures(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
failSELinuxPod := defaultPod()
|
failSELinuxPod := defaultPod()
|
||||||
|
failSELinuxPod.Spec.SecurityContext.SELinuxOptions = &api.SELinuxOptions{Level: "foo"}
|
||||||
failSELinuxPod.Spec.Containers[0].SecurityContext.SELinuxOptions = &api.SELinuxOptions{
|
failSELinuxPod.Spec.Containers[0].SecurityContext.SELinuxOptions = &api.SELinuxOptions{
|
||||||
Level: "bar",
|
Level: "bar",
|
||||||
}
|
}
|
||||||
|
@ -619,23 +603,18 @@ func TestValidateContainerFailures(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range errorCases {
|
for name, test := range errorCases {
|
||||||
provider, err := NewSimpleProvider(v.psp, "namespace", NewSimpleStrategyFactory())
|
t.Run(name, func(t *testing.T) {
|
||||||
if err != nil {
|
provider, err := NewSimpleProvider(test.psp, "namespace", NewSimpleStrategyFactory())
|
||||||
t.Fatalf("unable to create provider %v", err)
|
require.NoError(t, err, "unable to create provider")
|
||||||
}
|
errs := provider.ValidatePod(test.pod)
|
||||||
errs := provider.ValidateContainer(v.pod, &v.pod.Spec.Containers[0], field.NewPath(""))
|
require.NotEmpty(t, errs, "expected validation failure but did not receive errors")
|
||||||
if len(errs) == 0 {
|
assert.Contains(t, errs[0].Error(), test.expectedError, "unexpected error")
|
||||||
t.Errorf("%s expected validation failure but did not receive errors", k)
|
})
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !strings.Contains(errs[0].Error(), v.expectedError) {
|
|
||||||
t.Errorf("%s received unexpected error %v\nexpected: %s", k, errs, v.expectedError)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidatePodSecurityContextSuccess(t *testing.T) {
|
func TestValidatePodSuccess(t *testing.T) {
|
||||||
hostNetworkPSP := defaultPSP()
|
hostNetworkPSP := defaultPSP()
|
||||||
hostNetworkPSP.Spec.HostNetwork = true
|
hostNetworkPSP.Spec.HostNetwork = true
|
||||||
hostNetworkPod := defaultPod()
|
hostNetworkPod := defaultPod()
|
||||||
|
@ -908,16 +887,13 @@ func TestValidatePodSecurityContextSuccess(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range successCases {
|
for name, test := range successCases {
|
||||||
provider, err := NewSimpleProvider(v.psp, "namespace", NewSimpleStrategyFactory())
|
t.Run(name, func(t *testing.T) {
|
||||||
if err != nil {
|
provider, err := NewSimpleProvider(test.psp, "namespace", NewSimpleStrategyFactory())
|
||||||
t.Fatalf("unable to create provider %v", err)
|
require.NoError(t, err, "unable to create provider")
|
||||||
}
|
errs := provider.ValidatePod(test.pod)
|
||||||
errs := provider.ValidatePod(v.pod)
|
assert.Empty(t, errs, "expected validation pass but received errors")
|
||||||
if len(errs) != 0 {
|
})
|
||||||
t.Errorf("%s expected validation pass but received errors %v", k, errs)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -941,6 +917,7 @@ func TestValidateContainerSuccess(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
seLinuxPod := defaultPod()
|
seLinuxPod := defaultPod()
|
||||||
|
seLinuxPod.Spec.SecurityContext.SELinuxOptions = &api.SELinuxOptions{Level: "foo"}
|
||||||
seLinuxPod.Spec.Containers[0].SecurityContext.SELinuxOptions = &api.SELinuxOptions{
|
seLinuxPod.Spec.Containers[0].SecurityContext.SELinuxOptions = &api.SELinuxOptions{
|
||||||
Level: "foo",
|
Level: "foo",
|
||||||
}
|
}
|
||||||
|
@ -1007,6 +984,7 @@ func TestValidateContainerSuccess(t *testing.T) {
|
||||||
|
|
||||||
seccompPod := defaultPod()
|
seccompPod := defaultPod()
|
||||||
seccompPod.Annotations = map[string]string{
|
seccompPod.Annotations = map[string]string{
|
||||||
|
api.SeccompPodAnnotationKey: "foo",
|
||||||
api.SeccompContainerAnnotationKeyPrefix + seccompPod.Spec.Containers[0].Name: "foo",
|
api.SeccompContainerAnnotationKeyPrefix + seccompPod.Spec.Containers[0].Name: "foo",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1073,16 +1051,13 @@ func TestValidateContainerSuccess(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range successCases {
|
for name, test := range successCases {
|
||||||
provider, err := NewSimpleProvider(v.psp, "namespace", NewSimpleStrategyFactory())
|
t.Run(name, func(t *testing.T) {
|
||||||
if err != nil {
|
provider, err := NewSimpleProvider(test.psp, "namespace", NewSimpleStrategyFactory())
|
||||||
t.Fatalf("unable to create provider %v", err)
|
require.NoError(t, err, "unable to create provider")
|
||||||
}
|
errs := provider.ValidatePod(test.pod)
|
||||||
errs := provider.ValidateContainer(v.pod, &v.pod.Spec.Containers[0], field.NewPath(""))
|
assert.Empty(t, errs, "expected validation pass but received errors")
|
||||||
if len(errs) != 0 {
|
})
|
||||||
t.Errorf("%s expected validation pass but received errors %v\n%s", k, errs, spew.Sdump(v.pod.ObjectMeta))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1140,29 +1115,21 @@ func TestGenerateContainerSecurityContextReadOnlyRootFS(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range tests {
|
for name, test := range tests {
|
||||||
provider, err := NewSimpleProvider(v.psp, "namespace", NewSimpleStrategyFactory())
|
t.Run(name, func(t *testing.T) {
|
||||||
if err != nil {
|
provider, err := NewSimpleProvider(test.psp, "namespace", NewSimpleStrategyFactory())
|
||||||
t.Errorf("%s unable to create provider %v", k, err)
|
require.NoError(t, err, "unable to create provider")
|
||||||
continue
|
err = provider.MutatePod(test.pod)
|
||||||
}
|
require.NoError(t, err, "unable to mutate container")
|
||||||
err = provider.DefaultContainerSecurityContext(v.pod, &v.pod.Spec.Containers[0])
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%s unable to create container security context %v", k, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
sc := v.pod.Spec.Containers[0].SecurityContext
|
|
||||||
if v.expected == nil && sc.ReadOnlyRootFilesystem != nil {
|
|
||||||
t.Errorf("%s expected a nil ReadOnlyRootFilesystem but got %t", k, *sc.ReadOnlyRootFilesystem)
|
|
||||||
}
|
|
||||||
if v.expected != nil && sc.ReadOnlyRootFilesystem == nil {
|
|
||||||
t.Errorf("%s expected a non nil ReadOnlyRootFilesystem but received nil", k)
|
|
||||||
}
|
|
||||||
if v.expected != nil && sc.ReadOnlyRootFilesystem != nil && (*v.expected != *sc.ReadOnlyRootFilesystem) {
|
|
||||||
t.Errorf("%s expected a non nil ReadOnlyRootFilesystem set to %t but got %t", k, *v.expected, *sc.ReadOnlyRootFilesystem)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
sc := test.pod.Spec.Containers[0].SecurityContext
|
||||||
|
if test.expected == nil {
|
||||||
|
assert.Nil(t, sc.ReadOnlyRootFilesystem, "expected a nil ReadOnlyRootFilesystem")
|
||||||
|
} else {
|
||||||
|
require.NotNil(t, sc.ReadOnlyRootFilesystem, "expected a non nil ReadOnlyRootFilesystem")
|
||||||
|
assert.Equal(t, *test.expected, *sc.ReadOnlyRootFilesystem)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1252,55 +1219,42 @@ func TestValidateAllowedVolumes(t *testing.T) {
|
||||||
// reflectively create the volume source
|
// reflectively create the volume source
|
||||||
fieldVal := val.Type().Field(i)
|
fieldVal := val.Type().Field(i)
|
||||||
|
|
||||||
volumeSource := api.VolumeSource{}
|
t.Run(fieldVal.Name, func(t *testing.T) {
|
||||||
volumeSourceVolume := reflect.New(fieldVal.Type.Elem())
|
volumeSource := api.VolumeSource{}
|
||||||
|
volumeSourceVolume := reflect.New(fieldVal.Type.Elem())
|
||||||
|
|
||||||
reflect.ValueOf(&volumeSource).Elem().FieldByName(fieldVal.Name).Set(volumeSourceVolume)
|
reflect.ValueOf(&volumeSource).Elem().FieldByName(fieldVal.Name).Set(volumeSourceVolume)
|
||||||
volume := api.Volume{VolumeSource: volumeSource}
|
volume := api.Volume{VolumeSource: volumeSource}
|
||||||
|
|
||||||
// sanity check before moving on
|
// sanity check before moving on
|
||||||
fsType, err := psputil.GetVolumeFSType(volume)
|
fsType, err := psputil.GetVolumeFSType(volume)
|
||||||
if err != nil {
|
require.NoError(t, err, "error getting FSType")
|
||||||
t.Errorf("error getting FSType for %s: %s", fieldVal.Name, err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the volume to the pod
|
// add the volume to the pod
|
||||||
pod := defaultPod()
|
pod := defaultPod()
|
||||||
pod.Spec.Volumes = []api.Volume{volume}
|
pod.Spec.Volumes = []api.Volume{volume}
|
||||||
|
|
||||||
// create a PSP that allows no volumes
|
// create a PSP that allows no volumes
|
||||||
psp := defaultPSP()
|
psp := defaultPSP()
|
||||||
|
|
||||||
provider, err := NewSimpleProvider(psp, "namespace", NewSimpleStrategyFactory())
|
provider, err := NewSimpleProvider(psp, "namespace", NewSimpleStrategyFactory())
|
||||||
if err != nil {
|
require.NoError(t, err, "error creating provider")
|
||||||
t.Errorf("error creating provider for %s: %s", fieldVal.Name, err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// expect a denial for this PSP and test the error message to ensure it's related to the volumesource
|
// expect a denial for this PSP and test the error message to ensure it's related to the volumesource
|
||||||
errs := provider.ValidatePod(pod)
|
errs := provider.ValidatePod(pod)
|
||||||
if len(errs) != 1 {
|
require.Len(t, errs, 1, "expected exactly 1 error")
|
||||||
t.Errorf("expected exactly 1 error for %s but got %v", fieldVal.Name, errs)
|
assert.Contains(t, errs.ToAggregate().Error(), fmt.Sprintf("%s volumes are not allowed to be used", fsType), "did not find the expected error")
|
||||||
} else {
|
|
||||||
if !strings.Contains(errs.ToAggregate().Error(), fmt.Sprintf("%s volumes are not allowed to be used", fsType)) {
|
|
||||||
t.Errorf("did not find the expected error, received: %v", errs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now add the fstype directly to the psp and it should validate
|
// now add the fstype directly to the psp and it should validate
|
||||||
psp.Spec.Volumes = []policy.FSType{fsType}
|
psp.Spec.Volumes = []policy.FSType{fsType}
|
||||||
errs = provider.ValidatePod(pod)
|
errs = provider.ValidatePod(pod)
|
||||||
if len(errs) != 0 {
|
assert.Empty(t, errs, "directly allowing volume expected no errors")
|
||||||
t.Errorf("directly allowing volume expected no errors for %s but got %v", fieldVal.Name, errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// now change the psp to allow any volumes and the pod should still validate
|
// now change the psp to allow any volumes and the pod should still validate
|
||||||
psp.Spec.Volumes = []policy.FSType{policy.All}
|
psp.Spec.Volumes = []policy.FSType{policy.All}
|
||||||
errs = provider.ValidatePod(pod)
|
errs = provider.ValidatePod(pod)
|
||||||
if len(errs) != 0 {
|
assert.Empty(t, errs, "wildcard volume expected no errors")
|
||||||
t.Errorf("wildcard volume expected no errors for %s but got %v", fieldVal.Name, errs)
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1351,10 +1305,10 @@ func TestAllowPrivilegeEscalation(t *testing.T) {
|
||||||
provider, err := NewSimpleProvider(psp, "namespace", NewSimpleStrategyFactory())
|
provider, err := NewSimpleProvider(psp, "namespace", NewSimpleStrategyFactory())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = provider.DefaultContainerSecurityContext(pod, &pod.Spec.Containers[0])
|
err = provider.MutatePod(pod)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
errs := provider.ValidateContainer(pod, &pod.Spec.Containers[0], field.NewPath(""))
|
errs := provider.ValidatePod(pod)
|
||||||
if test.expectErr {
|
if test.expectErr {
|
||||||
assert.NotEmpty(t, errs, "expected validation error")
|
assert.NotEmpty(t, errs, "expected validation error")
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -32,16 +32,12 @@ import (
|
||||||
// Provider provides the implementation to generate a new security
|
// Provider provides the implementation to generate a new security
|
||||||
// context based on constraints or validate an existing security context against constraints.
|
// context based on constraints or validate an existing security context against constraints.
|
||||||
type Provider interface {
|
type Provider interface {
|
||||||
// DefaultPodSecurityContext sets the default values of the required but not filled fields.
|
// MutatePod sets the default values of the required but not filled fields of the pod and all
|
||||||
// It modifies the SecurityContext and annotations of the provided pod.
|
// containers in the pod.
|
||||||
DefaultPodSecurityContext(pod *api.Pod) error
|
MutatePod(pod *api.Pod) error
|
||||||
// DefaultContainerSecurityContext sets the default values of the required but not filled fields.
|
// ValidatePod ensures a pod and all its containers are in compliance with the given constraints.
|
||||||
// It modifies the SecurityContext of the container and annotations of the pod.
|
// ValidatePod MUST NOT mutate the pod.
|
||||||
DefaultContainerSecurityContext(pod *api.Pod, container *api.Container) error
|
|
||||||
// Ensure a pod is in compliance with the given constraints.
|
|
||||||
ValidatePod(pod *api.Pod) field.ErrorList
|
ValidatePod(pod *api.Pod) field.ErrorList
|
||||||
// Ensure a container's SecurityContext is in compliance with the given constraints.
|
|
||||||
ValidateContainer(pod *api.Pod, container *api.Container, containerPath *field.Path) field.ErrorList
|
|
||||||
// Get the name of the PSP that this provider was initialized with.
|
// Get the name of the PSP that this provider was initialized with.
|
||||||
GetPSPName() string
|
GetPSPName() string
|
||||||
}
|
}
|
||||||
|
|
|
@ -306,35 +306,14 @@ func (c *PodSecurityPolicyPlugin) computeSecurityContext(a admission.Attributes,
|
||||||
func assignSecurityContext(provider psp.Provider, pod *api.Pod) field.ErrorList {
|
func assignSecurityContext(provider psp.Provider, pod *api.Pod) field.ErrorList {
|
||||||
errs := field.ErrorList{}
|
errs := field.ErrorList{}
|
||||||
|
|
||||||
err := provider.DefaultPodSecurityContext(pod)
|
if err := provider.MutatePod(pod); err != nil {
|
||||||
if err != nil {
|
// TODO(tallclair): MutatePod should return a field.ErrorList
|
||||||
errs = append(errs, field.Invalid(field.NewPath("spec", "securityContext"), pod.Spec.SecurityContext, err.Error()))
|
errs = append(errs, field.Invalid(field.NewPath(""), pod, err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
errs = append(errs, provider.ValidatePod(pod)...)
|
errs = append(errs, provider.ValidatePod(pod)...)
|
||||||
|
|
||||||
for i := range pod.Spec.InitContainers {
|
return errs
|
||||||
err := provider.DefaultContainerSecurityContext(pod, &pod.Spec.InitContainers[i])
|
|
||||||
if err != nil {
|
|
||||||
errs = append(errs, field.Invalid(field.NewPath("spec", "initContainers").Index(i).Child("securityContext"), "", err.Error()))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
errs = append(errs, provider.ValidateContainer(pod, &pod.Spec.InitContainers[i], field.NewPath("spec", "initContainers").Index(i))...)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range pod.Spec.Containers {
|
|
||||||
err := provider.DefaultContainerSecurityContext(pod, &pod.Spec.Containers[i])
|
|
||||||
if err != nil {
|
|
||||||
errs = append(errs, field.Invalid(field.NewPath("spec", "containers").Index(i).Child("securityContext"), "", err.Error()))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
errs = append(errs, provider.ValidateContainer(pod, &pod.Spec.Containers[i], field.NewPath("spec", "containers").Index(i))...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(errs) > 0 {
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// createProvidersFromPolicies creates providers from the constraints supplied.
|
// createProvidersFromPolicies creates providers from the constraints supplied.
|
||||||
|
|
Loading…
Reference in New Issue