mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-14 22:33:34 +00:00
Merge pull request #30183 from timstclair/aa-psp
Automatic merge from submit-queue AppArmor PodSecurityPolicy support Implements the AppArmor PodSecurityPolicy support based on the alpha API proposed [here](https://github.com/kubernetes/kubernetes/blob/master/docs/proposals/apparmor.md#pod-security-policy) This implementation deviates from the original proposal in one way: it adds a separate option for specifying a default profile: ``` apparmor.security.alpha.kubernetes.io/defaultProfileName ``` This has several advantages over the original proposal: - The default is explicit, rather than implicit on the ordering - The default can be specified without constraining the allowed profiles - The allowed profiles can be restricted without specifying a default (requires every pod to explicitly set a profile) The E2E cluster does not currently enable the PodSecurityPolicy, so I will submit E2E tests in a separate PR. /cc @dchen1107 @pweil- @sttts @jfrazelle @Amey-D
This commit is contained in:
commit
0b5547f462
@ -207,3 +207,4 @@ pkg/volume/quobyte
|
|||||||
test/integration/discoverysummarizer
|
test/integration/discoverysummarizer
|
||||||
test/integration/examples
|
test/integration/examples
|
||||||
test/integration/federation
|
test/integration/federation
|
||||||
|
pkg/security/podsecuritypolicy/apparmor
|
||||||
|
@ -30,6 +30,7 @@ import (
|
|||||||
apivalidation "k8s.io/kubernetes/pkg/api/validation"
|
apivalidation "k8s.io/kubernetes/pkg/api/validation"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
"k8s.io/kubernetes/pkg/labels"
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
|
"k8s.io/kubernetes/pkg/security/apparmor"
|
||||||
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
|
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
|
||||||
"k8s.io/kubernetes/pkg/util/intstr"
|
"k8s.io/kubernetes/pkg/util/intstr"
|
||||||
"k8s.io/kubernetes/pkg/util/sets"
|
"k8s.io/kubernetes/pkg/util/sets"
|
||||||
@ -552,6 +553,7 @@ var ValidatePodSecurityPolicyName = apivalidation.NameIsDNSSubdomain
|
|||||||
func ValidatePodSecurityPolicy(psp *extensions.PodSecurityPolicy) field.ErrorList {
|
func ValidatePodSecurityPolicy(psp *extensions.PodSecurityPolicy) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&psp.ObjectMeta, false, ValidatePodSecurityPolicyName, field.NewPath("metadata"))...)
|
allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&psp.ObjectMeta, false, ValidatePodSecurityPolicyName, field.NewPath("metadata"))...)
|
||||||
|
allErrs = append(allErrs, ValidatePodSecurityPolicySpecificAnnotations(psp.Annotations, field.NewPath("metadata").Child("annotations"))...)
|
||||||
allErrs = append(allErrs, ValidatePodSecurityPolicySpec(&psp.Spec, field.NewPath("spec"))...)
|
allErrs = append(allErrs, ValidatePodSecurityPolicySpec(&psp.Spec, field.NewPath("spec"))...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
@ -570,6 +572,23 @@ func ValidatePodSecurityPolicySpec(spec *extensions.PodSecurityPolicySpec, fldPa
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ValidatePodSecurityPolicySpecificAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
|
||||||
|
allErrs := field.ErrorList{}
|
||||||
|
if p := annotations[apparmor.DefaultProfileAnnotationKey]; p != "" {
|
||||||
|
if err := apparmor.ValidateProfileFormat(p); err != nil {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath.Key(apparmor.DefaultProfileAnnotationKey), p, err.Error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if allowed := annotations[apparmor.AllowedProfilesAnnotationKey]; allowed != "" {
|
||||||
|
for _, p := range strings.Split(allowed, ",") {
|
||||||
|
if err := apparmor.ValidateProfileFormat(p); err != nil {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath.Key(apparmor.AllowedProfilesAnnotationKey), allowed, err.Error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
// validatePSPSELinux validates the SELinux fields of PodSecurityPolicy.
|
// validatePSPSELinux validates the SELinux fields of PodSecurityPolicy.
|
||||||
func validatePSPSELinux(fldPath *field.Path, seLinux *extensions.SELinuxStrategyOptions) field.ErrorList {
|
func validatePSPSELinux(fldPath *field.Path, seLinux *extensions.SELinuxStrategyOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
|
"k8s.io/kubernetes/pkg/security/apparmor"
|
||||||
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
|
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
|
||||||
"k8s.io/kubernetes/pkg/util/intstr"
|
"k8s.io/kubernetes/pkg/util/intstr"
|
||||||
"k8s.io/kubernetes/pkg/util/validation/field"
|
"k8s.io/kubernetes/pkg/util/validation/field"
|
||||||
@ -1510,7 +1511,9 @@ func TestValidateReplicaSet(t *testing.T) {
|
|||||||
func TestValidatePodSecurityPolicy(t *testing.T) {
|
func TestValidatePodSecurityPolicy(t *testing.T) {
|
||||||
validPSP := func() *extensions.PodSecurityPolicy {
|
validPSP := func() *extensions.PodSecurityPolicy {
|
||||||
return &extensions.PodSecurityPolicy{
|
return &extensions.PodSecurityPolicy{
|
||||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
Spec: extensions.PodSecurityPolicySpec{
|
Spec: extensions.PodSecurityPolicySpec{
|
||||||
SELinux: extensions.SELinuxStrategyOptions{
|
SELinux: extensions.SELinuxStrategyOptions{
|
||||||
Rule: extensions.SELinuxStrategyRunAsAny,
|
Rule: extensions.SELinuxStrategyRunAsAny,
|
||||||
@ -1584,6 +1587,15 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
|
|||||||
allowedCapListedInRequiredDrop.Spec.RequiredDropCapabilities = []api.Capability{"foo"}
|
allowedCapListedInRequiredDrop.Spec.RequiredDropCapabilities = []api.Capability{"foo"}
|
||||||
allowedCapListedInRequiredDrop.Spec.AllowedCapabilities = []api.Capability{"foo"}
|
allowedCapListedInRequiredDrop.Spec.AllowedCapabilities = []api.Capability{"foo"}
|
||||||
|
|
||||||
|
invalidAppArmorDefault := validPSP()
|
||||||
|
invalidAppArmorDefault.Annotations = map[string]string{
|
||||||
|
apparmor.DefaultProfileAnnotationKey: "not-good",
|
||||||
|
}
|
||||||
|
invalidAppArmorAllowed := validPSP()
|
||||||
|
invalidAppArmorAllowed.Annotations = map[string]string{
|
||||||
|
apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault + ",not-good",
|
||||||
|
}
|
||||||
|
|
||||||
errorCases := map[string]struct {
|
errorCases := map[string]struct {
|
||||||
psp *extensions.PodSecurityPolicy
|
psp *extensions.PodSecurityPolicy
|
||||||
errorType field.ErrorType
|
errorType field.ErrorType
|
||||||
@ -1664,6 +1676,16 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
|
|||||||
errorType: field.ErrorTypeInvalid,
|
errorType: field.ErrorTypeInvalid,
|
||||||
errorDetail: "capability is listed in allowedCapabilities and requiredDropCapabilities",
|
errorDetail: "capability is listed in allowedCapabilities and requiredDropCapabilities",
|
||||||
},
|
},
|
||||||
|
"invalid AppArmor default profile": {
|
||||||
|
psp: invalidAppArmorDefault,
|
||||||
|
errorType: field.ErrorTypeInvalid,
|
||||||
|
errorDetail: "invalid AppArmor profile name: \"not-good\"",
|
||||||
|
},
|
||||||
|
"invalid AppArmor allowed profile": {
|
||||||
|
psp: invalidAppArmorAllowed,
|
||||||
|
errorType: field.ErrorTypeInvalid,
|
||||||
|
errorDetail: "invalid AppArmor profile name: \"not-good\"",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range errorCases {
|
for k, v := range errorCases {
|
||||||
@ -1700,6 +1722,12 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
|
|||||||
caseInsensitiveAllowedDrop.Spec.RequiredDropCapabilities = []api.Capability{"FOO"}
|
caseInsensitiveAllowedDrop.Spec.RequiredDropCapabilities = []api.Capability{"FOO"}
|
||||||
caseInsensitiveAllowedDrop.Spec.AllowedCapabilities = []api.Capability{"foo"}
|
caseInsensitiveAllowedDrop.Spec.AllowedCapabilities = []api.Capability{"foo"}
|
||||||
|
|
||||||
|
validAppArmor := validPSP()
|
||||||
|
validAppArmor.Annotations = map[string]string{
|
||||||
|
apparmor.DefaultProfileAnnotationKey: apparmor.ProfileRuntimeDefault,
|
||||||
|
apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault + "," + apparmor.ProfileNamePrefix + "foo",
|
||||||
|
}
|
||||||
|
|
||||||
successCases := map[string]struct {
|
successCases := map[string]struct {
|
||||||
psp *extensions.PodSecurityPolicy
|
psp *extensions.PodSecurityPolicy
|
||||||
}{
|
}{
|
||||||
@ -1718,6 +1746,9 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
|
|||||||
"comparison for allowed -> drop is case sensitive": {
|
"comparison for allowed -> drop is case sensitive": {
|
||||||
psp: caseInsensitiveAllowedDrop,
|
psp: caseInsensitiveAllowedDrop,
|
||||||
},
|
},
|
||||||
|
"valid AppArmor annotations": {
|
||||||
|
psp: validAppArmor,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range successCases {
|
for k, v := range successCases {
|
||||||
|
@ -26,6 +26,10 @@ import (
|
|||||||
const (
|
const (
|
||||||
// The prefix to an annotation key specifying a container profile.
|
// The prefix to an annotation key specifying a container profile.
|
||||||
ContainerAnnotationKeyPrefix = "container.apparmor.security.alpha.kubernetes.io/"
|
ContainerAnnotationKeyPrefix = "container.apparmor.security.alpha.kubernetes.io/"
|
||||||
|
// The annotation key specifying the default AppArmor profile.
|
||||||
|
DefaultProfileAnnotationKey = "apparmor.security.alpha.kubernetes.io/defaultProfileName"
|
||||||
|
// The annotation key specifying the allowed AppArmor profiles.
|
||||||
|
AllowedProfilesAnnotationKey = "apparmor.security.alpha.kubernetes.io/allowedProfileNames"
|
||||||
|
|
||||||
// The profile specifying the runtime default.
|
// The profile specifying the runtime default.
|
||||||
ProfileRuntimeDefault = "runtime/default"
|
ProfileRuntimeDefault = "runtime/default"
|
||||||
@ -47,3 +51,12 @@ func isRequired(pod *api.Pod) bool {
|
|||||||
func GetProfileName(pod *api.Pod, containerName string) string {
|
func GetProfileName(pod *api.Pod, containerName string) string {
|
||||||
return pod.Annotations[ContainerAnnotationKeyPrefix+containerName]
|
return pod.Annotations[ContainerAnnotationKeyPrefix+containerName]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets the name of the profile to use with the container.
|
||||||
|
func SetProfileName(pod *api.Pod, containerName, profileName string) error {
|
||||||
|
if pod.Annotations == nil {
|
||||||
|
pod.Annotations = map[string]string{}
|
||||||
|
}
|
||||||
|
pod.Annotations[ContainerAnnotationKeyPrefix+containerName] = profileName
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
110
pkg/security/podsecuritypolicy/apparmor/strategy.go
Normal file
110
pkg/security/podsecuritypolicy/apparmor/strategy.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package apparmor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/security/apparmor"
|
||||||
|
"k8s.io/kubernetes/pkg/util/maps"
|
||||||
|
"k8s.io/kubernetes/pkg/util/validation/field"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Strategy defines the interface for all AppArmor constraint strategies.
|
||||||
|
type Strategy interface {
|
||||||
|
// Generate updates the annotations based on constraint rules. The updates are applied to a copy
|
||||||
|
// of the annotations, and returned.
|
||||||
|
Generate(annotations map[string]string, container *api.Container) (map[string]string, error)
|
||||||
|
// Validate ensures that the specified values fall within the range of the strategy.
|
||||||
|
Validate(pod *api.Pod, container *api.Container) field.ErrorList
|
||||||
|
}
|
||||||
|
|
||||||
|
type strategy struct {
|
||||||
|
defaultProfile string
|
||||||
|
allowedProfiles map[string]bool
|
||||||
|
// For printing error messages (preserves order).
|
||||||
|
allowedProfilesString string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Strategy = &strategy{}
|
||||||
|
|
||||||
|
// NewStrategy creates a new strategy that enforces AppArmor profile constraints.
|
||||||
|
func NewStrategy(pspAnnotations map[string]string) Strategy {
|
||||||
|
var allowedProfiles map[string]bool
|
||||||
|
if allowed, ok := pspAnnotations[apparmor.AllowedProfilesAnnotationKey]; ok {
|
||||||
|
profiles := strings.Split(allowed, ",")
|
||||||
|
allowedProfiles = make(map[string]bool, len(profiles))
|
||||||
|
for _, p := range profiles {
|
||||||
|
allowedProfiles[p] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &strategy{
|
||||||
|
defaultProfile: pspAnnotations[apparmor.DefaultProfileAnnotationKey],
|
||||||
|
allowedProfiles: allowedProfiles,
|
||||||
|
allowedProfilesString: pspAnnotations[apparmor.AllowedProfilesAnnotationKey],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *strategy) Generate(annotations map[string]string, container *api.Container) (map[string]string, error) {
|
||||||
|
copy := maps.CopySS(annotations)
|
||||||
|
|
||||||
|
if annotations[apparmor.ContainerAnnotationKeyPrefix+container.Name] != "" {
|
||||||
|
// Profile already set, nothing to do.
|
||||||
|
return copy, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.defaultProfile == "" {
|
||||||
|
// No default set.
|
||||||
|
return copy, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if copy == nil {
|
||||||
|
copy = map[string]string{}
|
||||||
|
}
|
||||||
|
// Add the default profile.
|
||||||
|
copy[apparmor.ContainerAnnotationKeyPrefix+container.Name] = s.defaultProfile
|
||||||
|
|
||||||
|
return copy, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *strategy) Validate(pod *api.Pod, container *api.Container) field.ErrorList {
|
||||||
|
if s.allowedProfiles == nil {
|
||||||
|
// Unrestricted: allow all.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
allErrs := field.ErrorList{}
|
||||||
|
fieldPath := field.NewPath("pod", "metadata", "annotations").Key(apparmor.ContainerAnnotationKeyPrefix + container.Name)
|
||||||
|
|
||||||
|
profile := apparmor.GetProfileName(pod, container.Name)
|
||||||
|
if profile == "" {
|
||||||
|
if len(s.allowedProfiles) > 0 {
|
||||||
|
allErrs = append(allErrs, field.Forbidden(fieldPath, "AppArmor profile must be set"))
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !s.allowedProfiles[profile] {
|
||||||
|
msg := fmt.Sprintf("%s is not an allowed profile. Allowed values: %q", profile, s.allowedProfilesString)
|
||||||
|
allErrs = append(allErrs, field.Forbidden(fieldPath, msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
return allErrs
|
||||||
|
}
|
173
pkg/security/podsecuritypolicy/apparmor/strategy_test.go
Normal file
173
pkg/security/podsecuritypolicy/apparmor/strategy_test.go
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package apparmor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/security/apparmor"
|
||||||
|
"k8s.io/kubernetes/pkg/util/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
containerName = "test-c"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
withoutAppArmor = map[string]string{"foo": "bar"}
|
||||||
|
withDefault = map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
apparmor.ContainerAnnotationKeyPrefix + containerName: apparmor.ProfileRuntimeDefault,
|
||||||
|
}
|
||||||
|
withLocal = map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
apparmor.ContainerAnnotationKeyPrefix + containerName: apparmor.ProfileNamePrefix + "foo",
|
||||||
|
}
|
||||||
|
withDisallowed = map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
apparmor.ContainerAnnotationKeyPrefix + containerName: apparmor.ProfileNamePrefix + "bad",
|
||||||
|
}
|
||||||
|
|
||||||
|
noAppArmor = map[string]string{"foo": "bar"}
|
||||||
|
unconstrainedWithDefault = map[string]string{
|
||||||
|
apparmor.DefaultProfileAnnotationKey: apparmor.ProfileRuntimeDefault,
|
||||||
|
}
|
||||||
|
constrained = map[string]string{
|
||||||
|
apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault + "," +
|
||||||
|
apparmor.ProfileNamePrefix + "foo",
|
||||||
|
}
|
||||||
|
constrainedWithDefault = map[string]string{
|
||||||
|
apparmor.DefaultProfileAnnotationKey: apparmor.ProfileRuntimeDefault,
|
||||||
|
apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault + "," +
|
||||||
|
apparmor.ProfileNamePrefix + "foo",
|
||||||
|
}
|
||||||
|
|
||||||
|
container = api.Container{
|
||||||
|
Name: containerName,
|
||||||
|
Image: "busybox",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGenerate(t *testing.T) {
|
||||||
|
type testcase struct {
|
||||||
|
pspAnnotations map[string]string
|
||||||
|
podAnnotations map[string]string
|
||||||
|
expected map[string]string
|
||||||
|
}
|
||||||
|
tests := []testcase{{
|
||||||
|
pspAnnotations: noAppArmor,
|
||||||
|
podAnnotations: withoutAppArmor,
|
||||||
|
expected: withoutAppArmor,
|
||||||
|
}, {
|
||||||
|
pspAnnotations: unconstrainedWithDefault,
|
||||||
|
podAnnotations: withoutAppArmor,
|
||||||
|
expected: withDefault,
|
||||||
|
}, {
|
||||||
|
pspAnnotations: constrained,
|
||||||
|
podAnnotations: withoutAppArmor,
|
||||||
|
expected: withoutAppArmor,
|
||||||
|
}, {
|
||||||
|
pspAnnotations: constrainedWithDefault,
|
||||||
|
podAnnotations: withoutAppArmor,
|
||||||
|
expected: withDefault,
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Add unchanging permutations.
|
||||||
|
for _, podAnnotations := range []map[string]string{withDefault, withLocal} {
|
||||||
|
for _, pspAnnotations := range []map[string]string{noAppArmor, unconstrainedWithDefault, constrained, constrainedWithDefault} {
|
||||||
|
tests = append(tests, testcase{
|
||||||
|
pspAnnotations: pspAnnotations,
|
||||||
|
podAnnotations: podAnnotations,
|
||||||
|
expected: podAnnotations,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
s := NewStrategy(test.pspAnnotations)
|
||||||
|
msgAndArgs := []interface{}{"testcase[%d]: %s", i, spew.Sdump(test)}
|
||||||
|
actual, err := s.Generate(test.podAnnotations, &container)
|
||||||
|
assert.NoError(t, err, msgAndArgs...)
|
||||||
|
assert.Equal(t, test.expected, actual, msgAndArgs...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidate(t *testing.T) {
|
||||||
|
type testcase struct {
|
||||||
|
pspAnnotations map[string]string
|
||||||
|
podAnnotations map[string]string
|
||||||
|
expectErr bool
|
||||||
|
}
|
||||||
|
tests := []testcase{}
|
||||||
|
// Valid combinations
|
||||||
|
for _, podAnnotations := range []map[string]string{withDefault, withLocal} {
|
||||||
|
for _, pspAnnotations := range []map[string]string{noAppArmor, unconstrainedWithDefault, constrained, constrainedWithDefault} {
|
||||||
|
tests = append(tests, testcase{
|
||||||
|
pspAnnotations: pspAnnotations,
|
||||||
|
podAnnotations: podAnnotations,
|
||||||
|
expectErr: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, podAnnotations := range []map[string]string{withoutAppArmor, withDisallowed} {
|
||||||
|
for _, pspAnnotations := range []map[string]string{noAppArmor, unconstrainedWithDefault} {
|
||||||
|
tests = append(tests, testcase{
|
||||||
|
pspAnnotations: pspAnnotations,
|
||||||
|
podAnnotations: podAnnotations,
|
||||||
|
expectErr: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Invalid combinations
|
||||||
|
for _, podAnnotations := range []map[string]string{withoutAppArmor, withDisallowed} {
|
||||||
|
for _, pspAnnotations := range []map[string]string{constrained, constrainedWithDefault} {
|
||||||
|
tests = append(tests, testcase{
|
||||||
|
pspAnnotations: pspAnnotations,
|
||||||
|
podAnnotations: podAnnotations,
|
||||||
|
expectErr: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
s := NewStrategy(test.pspAnnotations)
|
||||||
|
pod, container := makeTestPod(test.podAnnotations)
|
||||||
|
msgAndArgs := []interface{}{"testcase[%d]: %s", i, spew.Sdump(test)}
|
||||||
|
errs := s.Validate(pod, container)
|
||||||
|
if test.expectErr {
|
||||||
|
assert.Len(t, errs, 1, msgAndArgs...)
|
||||||
|
} else {
|
||||||
|
assert.Len(t, errs, 0, msgAndArgs...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeTestPod(annotations map[string]string) (*api.Pod, *api.Container) {
|
||||||
|
return &api.Pod{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "test-pod",
|
||||||
|
Annotations: maps.CopySS(annotations),
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{container},
|
||||||
|
},
|
||||||
|
}, &container
|
||||||
|
}
|
@ -21,6 +21,7 @@ import (
|
|||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
|
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/apparmor"
|
||||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities"
|
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities"
|
||||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/group"
|
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/group"
|
||||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux"
|
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux"
|
||||||
@ -49,6 +50,11 @@ func (f *simpleStrategyFactory) CreateStrategies(psp *extensions.PodSecurityPoli
|
|||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
appArmorStrat, err := createAppArmorStrategy(psp)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
|
||||||
fsGroupStrat, err := createFSGroupStrategy(&psp.Spec.FSGroup)
|
fsGroupStrat, err := createFSGroupStrategy(&psp.Spec.FSGroup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
@ -71,6 +77,7 @@ func (f *simpleStrategyFactory) CreateStrategies(psp *extensions.PodSecurityPoli
|
|||||||
strategies := &ProviderStrategies{
|
strategies := &ProviderStrategies{
|
||||||
RunAsUserStrategy: userStrat,
|
RunAsUserStrategy: userStrat,
|
||||||
SELinuxStrategy: seLinuxStrat,
|
SELinuxStrategy: seLinuxStrat,
|
||||||
|
AppArmorStrategy: appArmorStrat,
|
||||||
FSGroupStrategy: fsGroupStrat,
|
FSGroupStrategy: fsGroupStrat,
|
||||||
SupplementalGroupStrategy: supGroupStrat,
|
SupplementalGroupStrategy: supGroupStrat,
|
||||||
CapabilitiesStrategy: capStrat,
|
CapabilitiesStrategy: capStrat,
|
||||||
@ -105,6 +112,11 @@ func createSELinuxStrategy(opts *extensions.SELinuxStrategyOptions) (selinux.SEL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createAppArmorStrategy creates a new AppArmor strategy.
|
||||||
|
func createAppArmorStrategy(psp *extensions.PodSecurityPolicy) (apparmor.Strategy, error) {
|
||||||
|
return apparmor.NewStrategy(psp.Annotations), nil
|
||||||
|
}
|
||||||
|
|
||||||
// createFSGroupStrategy creates a new fsgroup strategy
|
// createFSGroupStrategy creates a new fsgroup strategy
|
||||||
func createFSGroupStrategy(opts *extensions.FSGroupStrategyOptions) (group.GroupStrategy, error) {
|
func createFSGroupStrategy(opts *extensions.FSGroupStrategyOptions) (group.GroupStrategy, error) {
|
||||||
switch opts.Rule {
|
switch opts.Rule {
|
||||||
|
@ -139,6 +139,11 @@ func (s *simpleProvider) CreateContainerSecurityContext(pod *api.Pod, container
|
|||||||
sc.SELinuxOptions = seLinux
|
sc.SELinuxOptions = seLinux
|
||||||
}
|
}
|
||||||
|
|
||||||
|
annotations, err := s.strategies.AppArmorStrategy.Generate(annotations, container)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if sc.Privileged == nil {
|
if sc.Privileged == nil {
|
||||||
priv := false
|
priv := false
|
||||||
sc.Privileged = &priv
|
sc.Privileged = &priv
|
||||||
@ -220,6 +225,7 @@ func (s *simpleProvider) ValidateContainerSecurityContext(pod *api.Pod, containe
|
|||||||
sc := container.SecurityContext
|
sc := container.SecurityContext
|
||||||
allErrs = append(allErrs, s.strategies.RunAsUserStrategy.Validate(pod, container)...)
|
allErrs = append(allErrs, s.strategies.RunAsUserStrategy.Validate(pod, container)...)
|
||||||
allErrs = append(allErrs, s.strategies.SELinuxStrategy.Validate(pod, container)...)
|
allErrs = append(allErrs, s.strategies.SELinuxStrategy.Validate(pod, container)...)
|
||||||
|
allErrs = append(allErrs, s.strategies.AppArmorStrategy.Validate(pod, container)...)
|
||||||
|
|
||||||
if !s.psp.Spec.Privileged && *sc.Privileged {
|
if !s.psp.Spec.Privileged && *sc.Privileged {
|
||||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("privileged"), *sc.Privileged, "Privileged containers are not allowed"))
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("privileged"), *sc.Privileged, "Privileged containers are not allowed"))
|
||||||
|
@ -22,13 +22,18 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
|
"k8s.io/kubernetes/pkg/security/apparmor"
|
||||||
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
|
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
|
||||||
"k8s.io/kubernetes/pkg/util/diff"
|
"k8s.io/kubernetes/pkg/util/diff"
|
||||||
"k8s.io/kubernetes/pkg/util/validation/field"
|
"k8s.io/kubernetes/pkg/util/validation/field"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const defaultContainerName = "test-c"
|
||||||
|
|
||||||
func TestCreatePodSecurityContextNonmutating(t *testing.T) {
|
func TestCreatePodSecurityContextNonmutating(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 {
|
||||||
@ -303,6 +308,14 @@ func TestValidateContainerSecurityContextFailures(t *testing.T) {
|
|||||||
Level: "bar",
|
Level: "bar",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
failNilAppArmorPod := defaultPod()
|
||||||
|
failInvalidAppArmorPod := defaultPod()
|
||||||
|
apparmor.SetProfileName(failInvalidAppArmorPod, defaultContainerName, apparmor.ProfileNamePrefix+"foo")
|
||||||
|
failAppArmorPSP := defaultPSP()
|
||||||
|
failAppArmorPSP.Annotations = map[string]string{
|
||||||
|
apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault,
|
||||||
|
}
|
||||||
|
|
||||||
failPrivPod := defaultPod()
|
failPrivPod := defaultPod()
|
||||||
var priv bool = true
|
var priv bool = true
|
||||||
failPrivPod.Spec.Containers[0].SecurityContext.Privileged = &priv
|
failPrivPod.Spec.Containers[0].SecurityContext.Privileged = &priv
|
||||||
@ -347,6 +360,16 @@ func TestValidateContainerSecurityContextFailures(t *testing.T) {
|
|||||||
psp: failSELinuxPSP,
|
psp: failSELinuxPSP,
|
||||||
expectedError: "does not match required level",
|
expectedError: "does not match required level",
|
||||||
},
|
},
|
||||||
|
"failNilAppArmor": {
|
||||||
|
pod: failNilAppArmorPod,
|
||||||
|
psp: failAppArmorPSP,
|
||||||
|
expectedError: "AppArmor profile must be set",
|
||||||
|
},
|
||||||
|
"failInvalidAppArmor": {
|
||||||
|
pod: failInvalidAppArmorPod,
|
||||||
|
psp: failAppArmorPSP,
|
||||||
|
expectedError: "localhost/foo is not an allowed profile. Allowed values: \"runtime/default\"",
|
||||||
|
},
|
||||||
"failPrivPSP": {
|
"failPrivPSP": {
|
||||||
pod: failPrivPod,
|
pod: failPrivPod,
|
||||||
psp: defaultPSP(),
|
psp: defaultPSP(),
|
||||||
@ -499,6 +522,7 @@ func TestValidateContainerSecurityContextSuccess(t *testing.T) {
|
|||||||
SecurityContext: &api.PodSecurityContext{},
|
SecurityContext: &api.PodSecurityContext{},
|
||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{
|
{
|
||||||
|
Name: defaultContainerName,
|
||||||
SecurityContext: &api.SecurityContext{
|
SecurityContext: &api.SecurityContext{
|
||||||
// expected to be set by defaulting mechanisms
|
// expected to be set by defaulting mechanisms
|
||||||
Privileged: ¬Priv,
|
Privileged: ¬Priv,
|
||||||
@ -510,7 +534,7 @@ func TestValidateContainerSecurityContextSuccess(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fail user strat
|
// success user strat
|
||||||
userPSP := defaultPSP()
|
userPSP := defaultPSP()
|
||||||
var uid int64 = 999
|
var uid int64 = 999
|
||||||
userPSP.Spec.RunAsUser = extensions.RunAsUserStrategyOptions{
|
userPSP.Spec.RunAsUser = extensions.RunAsUserStrategyOptions{
|
||||||
@ -520,7 +544,7 @@ func TestValidateContainerSecurityContextSuccess(t *testing.T) {
|
|||||||
userPod := defaultPod()
|
userPod := defaultPod()
|
||||||
userPod.Spec.Containers[0].SecurityContext.RunAsUser = &uid
|
userPod.Spec.Containers[0].SecurityContext.RunAsUser = &uid
|
||||||
|
|
||||||
// fail selinux strat
|
// success selinux strat
|
||||||
seLinuxPSP := defaultPSP()
|
seLinuxPSP := defaultPSP()
|
||||||
seLinuxPSP.Spec.SELinux = extensions.SELinuxStrategyOptions{
|
seLinuxPSP.Spec.SELinux = extensions.SELinuxStrategyOptions{
|
||||||
Rule: extensions.SELinuxStrategyMustRunAs,
|
Rule: extensions.SELinuxStrategyMustRunAs,
|
||||||
@ -533,6 +557,13 @@ func TestValidateContainerSecurityContextSuccess(t *testing.T) {
|
|||||||
Level: "foo",
|
Level: "foo",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
appArmorPSP := defaultPSP()
|
||||||
|
appArmorPSP.Annotations = map[string]string{
|
||||||
|
apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault,
|
||||||
|
}
|
||||||
|
appArmorPod := defaultPod()
|
||||||
|
apparmor.SetProfileName(appArmorPod, defaultContainerName, apparmor.ProfileRuntimeDefault)
|
||||||
|
|
||||||
privPSP := defaultPSP()
|
privPSP := defaultPSP()
|
||||||
privPSP.Spec.Privileged = true
|
privPSP.Spec.Privileged = true
|
||||||
privPod := defaultPod()
|
privPod := defaultPod()
|
||||||
@ -591,6 +622,10 @@ func TestValidateContainerSecurityContextSuccess(t *testing.T) {
|
|||||||
pod: seLinuxPod,
|
pod: seLinuxPod,
|
||||||
psp: seLinuxPSP,
|
psp: seLinuxPSP,
|
||||||
},
|
},
|
||||||
|
"pass AppArmor allowed profiles": {
|
||||||
|
pod: appArmorPod,
|
||||||
|
psp: appArmorPSP,
|
||||||
|
},
|
||||||
"pass priv validating PSP": {
|
"pass priv validating PSP": {
|
||||||
pod: privPod,
|
pod: privPod,
|
||||||
psp: privPSP,
|
psp: privPSP,
|
||||||
@ -632,7 +667,7 @@ func TestValidateContainerSecurityContextSuccess(t *testing.T) {
|
|||||||
}
|
}
|
||||||
errs := provider.ValidateContainerSecurityContext(v.pod, &v.pod.Spec.Containers[0], field.NewPath(""))
|
errs := provider.ValidateContainerSecurityContext(v.pod, &v.pod.Spec.Containers[0], field.NewPath(""))
|
||||||
if len(errs) != 0 {
|
if len(errs) != 0 {
|
||||||
t.Errorf("%s expected validation pass but received errors %v", k, errs)
|
t.Errorf("%s expected validation pass but received errors %v\n%s", k, errs, spew.Sdump(v.pod.ObjectMeta))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -748,6 +783,7 @@ func defaultPod() *api.Pod {
|
|||||||
},
|
},
|
||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{
|
{
|
||||||
|
Name: defaultContainerName,
|
||||||
SecurityContext: &api.SecurityContext{
|
SecurityContext: &api.SecurityContext{
|
||||||
// expected to be set by defaulting mechanisms
|
// expected to be set by defaulting mechanisms
|
||||||
Privileged: ¬Priv,
|
Privileged: ¬Priv,
|
||||||
|
@ -19,6 +19,7 @@ package podsecuritypolicy
|
|||||||
import (
|
import (
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
|
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/apparmor"
|
||||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities"
|
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities"
|
||||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/group"
|
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/group"
|
||||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux"
|
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux"
|
||||||
@ -58,6 +59,7 @@ type StrategyFactory interface {
|
|||||||
type ProviderStrategies struct {
|
type ProviderStrategies struct {
|
||||||
RunAsUserStrategy user.RunAsUserStrategy
|
RunAsUserStrategy user.RunAsUserStrategy
|
||||||
SELinuxStrategy selinux.SELinuxStrategy
|
SELinuxStrategy selinux.SELinuxStrategy
|
||||||
|
AppArmorStrategy apparmor.Strategy
|
||||||
FSGroupStrategy group.GroupStrategy
|
FSGroupStrategy group.GroupStrategy
|
||||||
SupplementalGroupStrategy group.GroupStrategy
|
SupplementalGroupStrategy group.GroupStrategy
|
||||||
CapabilitiesStrategy capabilities.Strategy
|
CapabilitiesStrategy capabilities.Strategy
|
||||||
|
@ -22,6 +22,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
kadmission "k8s.io/kubernetes/pkg/admission"
|
kadmission "k8s.io/kubernetes/pkg/admission"
|
||||||
kapi "k8s.io/kubernetes/pkg/api"
|
kapi "k8s.io/kubernetes/pkg/api"
|
||||||
extensions "k8s.io/kubernetes/pkg/apis/extensions"
|
extensions "k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
@ -29,11 +31,14 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/client/cache"
|
"k8s.io/kubernetes/pkg/client/cache"
|
||||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||||
clientsetfake "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
|
clientsetfake "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
|
||||||
|
"k8s.io/kubernetes/pkg/security/apparmor"
|
||||||
kpsp "k8s.io/kubernetes/pkg/security/podsecuritypolicy"
|
kpsp "k8s.io/kubernetes/pkg/security/podsecuritypolicy"
|
||||||
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
|
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
|
||||||
diff "k8s.io/kubernetes/pkg/util/diff"
|
diff "k8s.io/kubernetes/pkg/util/diff"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const defaultContainerName = "test-c"
|
||||||
|
|
||||||
func NewTestAdmission(store cache.Store, kclient clientset.Interface) kadmission.Interface {
|
func NewTestAdmission(store cache.Store, kclient clientset.Interface) kadmission.Interface {
|
||||||
return &podSecurityPolicyPlugin{
|
return &podSecurityPolicyPlugin{
|
||||||
Handler: kadmission.NewHandler(kadmission.Create),
|
Handler: kadmission.NewHandler(kadmission.Create),
|
||||||
@ -610,6 +615,85 @@ func TestAdmitSELinux(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAdmitAppArmor(t *testing.T) {
|
||||||
|
createPodWithAppArmor := func(profile string) *kapi.Pod {
|
||||||
|
pod := goodPod()
|
||||||
|
apparmor.SetProfileName(pod, defaultContainerName, profile)
|
||||||
|
return pod
|
||||||
|
}
|
||||||
|
|
||||||
|
unconstrainedPSP := restrictivePSP()
|
||||||
|
defaultedPSP := restrictivePSP()
|
||||||
|
defaultedPSP.Annotations = map[string]string{
|
||||||
|
apparmor.DefaultProfileAnnotationKey: apparmor.ProfileRuntimeDefault,
|
||||||
|
}
|
||||||
|
appArmorPSP := restrictivePSP()
|
||||||
|
appArmorPSP.Annotations = map[string]string{
|
||||||
|
apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault,
|
||||||
|
}
|
||||||
|
appArmorDefaultPSP := restrictivePSP()
|
||||||
|
appArmorDefaultPSP.Annotations = map[string]string{
|
||||||
|
apparmor.DefaultProfileAnnotationKey: apparmor.ProfileRuntimeDefault,
|
||||||
|
apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault + "," + apparmor.ProfileNamePrefix + "foo",
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := map[string]struct {
|
||||||
|
pod *kapi.Pod
|
||||||
|
psp *extensions.PodSecurityPolicy
|
||||||
|
shouldPass bool
|
||||||
|
expectedProfile string
|
||||||
|
}{
|
||||||
|
"unconstrained with no profile": {
|
||||||
|
pod: goodPod(),
|
||||||
|
psp: unconstrainedPSP,
|
||||||
|
shouldPass: true,
|
||||||
|
expectedProfile: "",
|
||||||
|
},
|
||||||
|
"unconstrained with profile": {
|
||||||
|
pod: createPodWithAppArmor(apparmor.ProfileRuntimeDefault),
|
||||||
|
psp: unconstrainedPSP,
|
||||||
|
shouldPass: true,
|
||||||
|
expectedProfile: apparmor.ProfileRuntimeDefault,
|
||||||
|
},
|
||||||
|
"unconstrained with default profile": {
|
||||||
|
pod: goodPod(),
|
||||||
|
psp: defaultedPSP,
|
||||||
|
shouldPass: true,
|
||||||
|
expectedProfile: apparmor.ProfileRuntimeDefault,
|
||||||
|
},
|
||||||
|
"AppArmor enforced with no profile": {
|
||||||
|
pod: goodPod(),
|
||||||
|
psp: appArmorPSP,
|
||||||
|
shouldPass: false,
|
||||||
|
},
|
||||||
|
"AppArmor enforced with default profile": {
|
||||||
|
pod: goodPod(),
|
||||||
|
psp: appArmorDefaultPSP,
|
||||||
|
shouldPass: true,
|
||||||
|
expectedProfile: apparmor.ProfileRuntimeDefault,
|
||||||
|
},
|
||||||
|
"AppArmor enforced with good profile": {
|
||||||
|
pod: createPodWithAppArmor(apparmor.ProfileNamePrefix + "foo"),
|
||||||
|
psp: appArmorDefaultPSP,
|
||||||
|
shouldPass: true,
|
||||||
|
expectedProfile: apparmor.ProfileNamePrefix + "foo",
|
||||||
|
},
|
||||||
|
"AppArmor enforced with local profile": {
|
||||||
|
pod: createPodWithAppArmor(apparmor.ProfileNamePrefix + "bar"),
|
||||||
|
psp: appArmorPSP,
|
||||||
|
shouldPass: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range tests {
|
||||||
|
testPSPAdmit(k, []*extensions.PodSecurityPolicy{v.psp}, v.pod, v.shouldPass, v.psp.Name, t)
|
||||||
|
|
||||||
|
if v.shouldPass {
|
||||||
|
assert.Equal(t, v.expectedProfile, apparmor.GetProfileName(v.pod, defaultContainerName), k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAdmitRunAsUser(t *testing.T) {
|
func TestAdmitRunAsUser(t *testing.T) {
|
||||||
createPodWithRunAsUser := func(user int64) *kapi.Pod {
|
createPodWithRunAsUser := func(user int64) *kapi.Pod {
|
||||||
pod := goodPod()
|
pod := goodPod()
|
||||||
@ -1212,6 +1296,7 @@ func goodPod() *kapi.Pod {
|
|||||||
SecurityContext: &kapi.PodSecurityContext{},
|
SecurityContext: &kapi.PodSecurityContext{},
|
||||||
Containers: []kapi.Container{
|
Containers: []kapi.Container{
|
||||||
{
|
{
|
||||||
|
Name: defaultContainerName,
|
||||||
SecurityContext: &kapi.SecurityContext{},
|
SecurityContext: &kapi.SecurityContext{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user