diff --git a/examples/podsecuritypolicy/rbac/README.md b/examples/podsecuritypolicy/rbac/README.md new file mode 100644 index 00000000000..5e692fc225d --- /dev/null +++ b/examples/podsecuritypolicy/rbac/README.md @@ -0,0 +1,221 @@ + + + + +WARNING +WARNING +WARNING +WARNING +WARNING + +

PLEASE NOTE: This document applies to the HEAD of the source tree

+ +If you are using a released version of Kubernetes, you should +refer to the docs that go with that version. + +Documentation for other releases can be found at +[releases.k8s.io](http://releases.k8s.io). + +-- + + + + + +## PSP RBAC Example + +This example demonstrates the usage of *PodSecurityPolicy* to control access to privileged containers +based on role and groups. + +### Prerequisites + +The server must be started to enable the appropriate APIs and flags + +1. allow privileged containers +1. allow security contexts +1. enable RBAC and accept any token +1. enable PodSecurityPolicies +1. use the PodSecurityPolicy admission controller + +If you are using the `local-up-cluster.sh` script you may enable these settings with the following syntax + +``` +PSP_ADMISSION=true ALLOW_PRIVILEGED=true ALLOW_SECURITY_CONTEXT=true ALLOW_ANY_TOKEN=true ENABLE_RBAC=true RUNTIME_CONFIG="extensions/v1beta1=true,extensions/v1beta1/podsecuritypolicy=true" hack/local-up-cluster.sh +``` + +### Using the protected port + +It is important to note that this example uses the following syntax to test with RBAC + +1. `--server=https://127.0.0.1:6443`: when performing requests this ensures that the protected port is used so +that RBAC will be enforced +1. `--token={user}/{group(s)}`: this syntax allows a request to specify the username and groups to use for +testing. It relies on the `ALLOW_ANY_TOKEN` setting. + +## Creating the policies, roles, and bindings + +### Policies + +The first step to enforcing cluster constraints via PSP is to create your policies. In this +example we will use two policies, `restricted` and `privileged`. For simplicity, the only difference +between these policies is the ability to run a privileged container. + +```yaml +apiVersion: extensions/v1beta1 +kind: PodSecurityPolicy +metadata: + name: privileged +spec: + fsGroup: + rule: RunAsAny + privileged: true + runAsUser: + rule: RunAsAny + seLinux: + rule: RunAsAny + supplementalGroups: + rule: RunAsAny + volumes: + - '*' +--- +apiVersion: extensions/v1beta1 +kind: PodSecurityPolicy +metadata: + name: restricted +spec: + fsGroup: + rule: RunAsAny + runAsUser: + rule: RunAsAny + seLinux: + rule: RunAsAny + supplementalGroups: + rule: RunAsAny + volumes: + - '*' + +``` + +To create these policies run + +``` +$ kubectl --server=https://127.0.0.1:6443 --token=foo/system:masters create -f examples/podsecuritypolicy/rbac/policies.yaml +podsecuritypolicy "privileged" created +podsecuritypolicy "restricted" created +``` + +### Roles and bindings + +In order to a `PodSecurityPolicy` a user must have the ability to perform the `use` verb on the policy. +The `use` verb is a special verb that grants access to use the policy while +not allowing any other access. This verb is specific to `PodSecurityPolicy`. +To enable the `use` access we will create cluster roles. In this example we will provide the roles: + +1. `restricted-psp-user`: this role allows the `use` verb on the `restricted` policy only +2. `privileged-psp-user`: this role allows the `use` verb on the `privileged` policy only + + +To associate roles with users we will use groups via a `RoleBinding`. This example uses +the following groups: + +1. `privileged`: this group is bound to the `privilegedPSP` role and `restrictedPSP` role which gives users +in this group access to both policies. +1. `restricted`: this group is bound to the `restrictedPSP` role +1. `system:authenticated`: this is a system group for any authenticated user. It is bound to the `edit` +role which is already provided by the cluster. + +To create these roles and bindings run + +``` +$ kubectl --server=https://127.0.0.1:6443 --token=foo/system:masters create -f examples/podsecuritypolicy/rbac/roles.yaml +clusterrole "restricted-psp-user" created +clusterrole "privileged-psp-user" created + +$ kubectl --server=https://127.0.0.1:6443 --token=foo/system:masters create -f examples/podsecuritypolicy/rbac/bindings.yaml +clusterrolebinding "privileged-psp-users" created +clusterrolebinding "restricted-psp-users" created +clusterrolebinding "edit" created +``` + +## Testing access + +### Restricted user can create non-privileged pods + +Create the pod + +``` +$ kubectl --server=https://127.0.0.1:6443 --token=foo/restricted-psp-users create -f examples/podsecuritypolicy/rbac/pod.yaml +pod "nginx" created +``` + +Check the PSP that allowed the pod + +``` +$ kubectl get pod nginx -o yaml | grep psp + kubernetes.io/psp: restricted +``` + +### Restricted user cannot create privileged pods + +Delete the existing pod + +``` +$ kubectl delete pod nginx +pod "nginx" deleted +``` + +Create the privileged pod + +``` +$ kubectl --server=https://127.0.0.1:6443 --token=foo/restricted-psp-users create -f examples/podsecuritypolicy/rbac/pod_priv.yaml +Error from server (Forbidden): error when creating "examples/podsecuritypolicy/rbac/pod_priv.yaml": pods "nginx" is forbidden: unable to validate against any pod security policy: [spec.containers[0].securityContext.privileged: Invalid value: true: Privileged containers are not allowed] +``` + +### Privileged user can create non-privileged pods + +``` +$ kubectl --server=https://127.0.0.1:6443 --token=foo/privileged-psp-users create -f examples/podsecuritypolicy/rbac/pod.yaml +pod "nginx" created +``` + +Check the PSP that allowed the pod. Note, this could be the `restricted` or `privileged` PSP since both allow +for the creation of non-privileged pods. + +``` +$ kubectl get pod nginx -o yaml | egrep "psp|privileged" + kubernetes.io/psp: privileged + privileged: false +``` + +### Privileged user can create privileged pods + +Delete the existing pod + +``` +$ kubectl delete pod nginx +pod "nginx" deleted +``` + +Create the privileged pod + +``` +$ kubectl --server=https://127.0.0.1:6443 --token=foo/privileged-psp-users create -f examples/podsecuritypolicy/rbac/pod_priv.yaml +pod "nginx" created +``` + +Check the PSP that allowed the pod. + +``` +$ kubectl get pod nginx -o yaml | egrep "psp|privileged" + kubernetes.io/psp: privileged + privileged: true +``` + + +[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/examples/podsecuritypolicy/rbac/README.md?pixel)]() + diff --git a/examples/podsecuritypolicy/rbac/bindings.yaml b/examples/podsecuritypolicy/rbac/bindings.yaml new file mode 100644 index 00000000000..8a2c7c9c92d --- /dev/null +++ b/examples/podsecuritypolicy/rbac/bindings.yaml @@ -0,0 +1,49 @@ +# privilegedPSP gives the privilegedPSP role +# to the group privileged. +apiVersion: rbac.authorization.k8s.io/v1alpha1 +kind: ClusterRoleBinding +metadata: + name: privileged-psp-users +subjects: +- kind: Group + apiVersion: rbac.authorization.k8s.io/v1alpha1 + name: privileged-psp-users +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: privileged-psp-user +--- +# restrictedPSP grants the restrictedPSP role to +# the groups restricted and privileged. +apiVersion: rbac.authorization.k8s.io/v1alpha1 +kind: ClusterRoleBinding +metadata: + name: restricted-psp-users +subjects: +- kind: Group + apiVersion: rbac.authorization.k8s.io/v1alpha1 + name: restricted-psp-users +- kind: Group + apiVersion: rbac.authorization.k8s.io/v1alpha1 + name: privileged-psp-users +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: restricted-psp-user +--- +# edit grants edit role to system:authenticated. +apiVersion: rbac.authorization.k8s.io/v1alpha1 +kind: ClusterRoleBinding +metadata: + name: edit +subjects: +- kind: Group + apiVersion: rbac.authorization.k8s.io/v1alpha1 + name: privileged-psp-users +- kind: Group + apiVersion: rbac.authorization.k8s.io/v1alpha1 + name: restricted-psp-users +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: edit diff --git a/examples/podsecuritypolicy/rbac/pod.yaml b/examples/podsecuritypolicy/rbac/pod.yaml new file mode 100644 index 00000000000..5b7b1efdcbf --- /dev/null +++ b/examples/podsecuritypolicy/rbac/pod.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx + labels: + name: nginx +spec: + containers: + - name: nginx + image: nginx + ports: + - containerPort: 80 diff --git a/examples/podsecuritypolicy/rbac/pod_priv.yaml b/examples/podsecuritypolicy/rbac/pod_priv.yaml new file mode 100644 index 00000000000..6c638c44998 --- /dev/null +++ b/examples/podsecuritypolicy/rbac/pod_priv.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx + labels: + name: nginx +spec: + containers: + - name: nginx + image: nginx + ports: + - containerPort: 80 + securityContext: + privileged: true diff --git a/examples/podsecuritypolicy/rbac/policies.yaml b/examples/podsecuritypolicy/rbac/policies.yaml new file mode 100644 index 00000000000..7519091b817 --- /dev/null +++ b/examples/podsecuritypolicy/rbac/policies.yaml @@ -0,0 +1,38 @@ +apiVersion: extensions/v1beta1 +kind: PodSecurityPolicy +metadata: + name: privileged +spec: + fsGroup: + rule: RunAsAny + privileged: true + runAsUser: + rule: RunAsAny + seLinux: + rule: RunAsAny + supplementalGroups: + rule: RunAsAny + volumes: + - '*' +--- +apiVersion: extensions/v1beta1 +kind: PodSecurityPolicy +metadata: + name: restricted +spec: + privileged: false + fsGroup: + rule: RunAsAny + runAsUser: + rule: MustRunAsNonRoot + seLinux: + rule: RunAsAny + supplementalGroups: + rule: RunAsAny + volumes: + - 'emptyDir' + - 'secret' + - 'downwardAPI' + - 'configMap' + - 'persistentVolumeClaim' + diff --git a/examples/podsecuritypolicy/rbac/roles.yaml b/examples/podsecuritypolicy/rbac/roles.yaml new file mode 100644 index 00000000000..553cc76cf93 --- /dev/null +++ b/examples/podsecuritypolicy/rbac/roles.yaml @@ -0,0 +1,33 @@ +# restrictedPSP grants access to use +# the restricted PSP. +apiVersion: rbac.authorization.k8s.io/v1alpha1 +kind: ClusterRole +metadata: + name: restricted-psp-user +rules: +- apiGroups: + - extensions + resources: + - podsecuritypolicies + resourceNames: + - restricted + verbs: + - use +--- +# privilegedPSP grants access to use the privileged +# PSP. +apiVersion: rbac.authorization.k8s.io/v1alpha1 +kind: ClusterRole +metadata: + name: privileged-psp-user +rules: +- apiGroups: + - extensions + resources: + - podsecuritypolicies + resourceNames: + - privileged + verbs: + - use + + diff --git a/hack/local-up-cluster.sh b/hack/local-up-cluster.sh index 34a46d46974..194bf3c8790 100755 --- a/hack/local-up-cluster.sh +++ b/hack/local-up-cluster.sh @@ -22,6 +22,7 @@ DOCKER=(docker ${DOCKER_OPTS}) DOCKERIZE_KUBELET=${DOCKERIZE_KUBELET:-""} ALLOW_PRIVILEGED=${ALLOW_PRIVILEGED:-""} ALLOW_SECURITY_CONTEXT=${ALLOW_SECURITY_CONTEXT:-""} +PSP_ADMISSION=${PSP_ADMISSION:-""} RUNTIME_CONFIG=${RUNTIME_CONFIG:-""} KUBELET_AUTHORIZATION_WEBHOOK=${KUBELET_AUTHORIZATION_WEBHOOK:-""} KUBELET_AUTHENTICATION_WEBHOOK=${KUBELET_AUTHENTICATION_WEBHOOK:-""} @@ -316,12 +317,17 @@ function set_service_accounts { } function start_apiserver { - # Admission Controllers to invoke prior to persisting objects in cluster + security_admission="" if [[ -z "${ALLOW_SECURITY_CONTEXT}" ]]; then - ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota,DefaultStorageClass - else - ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,DefaultStorageClass + security_admission=",SecurityContextDeny" fi + if [[ -n "${PSP_ADMISSION}" ]]; then + security_admission=",PodSecurityPolicy" + fi + + # Admission Controllers to invoke prior to persisting objects in cluster + ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,ServiceAccount${security_admission},ResourceQuota,DefaultStorageClass + # This is the default dir and filename where the apiserver will generate a self-signed cert # which should be able to be used as the CA to verify itself diff --git a/plugin/pkg/admission/security/podsecuritypolicy/BUILD b/plugin/pkg/admission/security/podsecuritypolicy/BUILD index e030461243a..29e1f347d8e 100644 --- a/plugin/pkg/admission/security/podsecuritypolicy/BUILD +++ b/plugin/pkg/admission/security/podsecuritypolicy/BUILD @@ -19,6 +19,7 @@ go_library( "//pkg/api:go_default_library", "//pkg/api/errors:go_default_library", "//pkg/apis/extensions:go_default_library", + "//pkg/auth/authorizer:go_default_library", "//pkg/auth/user:go_default_library", "//pkg/client/cache:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library", @@ -43,6 +44,7 @@ go_test( "//pkg/admission:go_default_library", "//pkg/api:go_default_library", "//pkg/apis/extensions:go_default_library", + "//pkg/auth/authorizer:go_default_library", "//pkg/auth/user:go_default_library", "//pkg/client/cache:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library", @@ -52,6 +54,7 @@ go_test( "//pkg/security/podsecuritypolicy/seccomp:go_default_library", "//pkg/security/podsecuritypolicy/util:go_default_library", "//pkg/util/diff:go_default_library", + "//pkg/util/sets:go_default_library", "//vendor:github.com/stretchr/testify/assert", ], ) diff --git a/plugin/pkg/admission/security/podsecuritypolicy/admission.go b/plugin/pkg/admission/security/podsecuritypolicy/admission.go index d105a8de4b2..287d483e6e5 100644 --- a/plugin/pkg/admission/security/podsecuritypolicy/admission.go +++ b/plugin/pkg/admission/security/podsecuritypolicy/admission.go @@ -23,10 +23,11 @@ import ( "github.com/golang/glog" - admission "k8s.io/kubernetes/pkg/admission" - api "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/admission" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/auth/authorizer" "k8s.io/kubernetes/pkg/auth/user" "k8s.io/kubernetes/pkg/client/cache" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" @@ -46,14 +47,14 @@ const ( func init() { admission.RegisterPlugin(PluginName, func(client clientset.Interface, config io.Reader) (admission.Interface, error) { - plugin := NewPlugin(client, psp.NewSimpleStrategyFactory(), getMatchingPolicies, false) + plugin := NewPlugin(client, psp.NewSimpleStrategyFactory(), getMatchingPolicies, true) plugin.Run() return plugin, nil }) } // PSPMatchFn allows plugging in how PSPs are matched against user information. -type PSPMatchFn func(store cache.Store, user user.Info, sa user.Info) ([]*extensions.PodSecurityPolicy, error) +type PSPMatchFn func(store cache.Store, user user.Info, sa user.Info, authz authorizer.Authorizer) ([]*extensions.PodSecurityPolicy, error) // podSecurityPolicyPlugin holds state for and implements the admission plugin. type podSecurityPolicyPlugin struct { @@ -62,13 +63,28 @@ type podSecurityPolicyPlugin struct { strategyFactory psp.StrategyFactory pspMatcher PSPMatchFn failOnNoPolicies bool + authz authorizer.Authorizer reflector *cache.Reflector stopChan chan struct{} store cache.Store } +// SetAuthorizer sets the authorizer. +func (plugin *podSecurityPolicyPlugin) SetAuthorizer(authz authorizer.Authorizer) { + plugin.authz = authz +} + +// Validate ensures an authorizer is set. +func (plugin *podSecurityPolicyPlugin) Validate() error { + if plugin.authz == nil { + return fmt.Errorf("%s requires an authorizer", PluginName) + } + return nil +} + var _ admission.Interface = &podSecurityPolicyPlugin{} +var _ admission.WantsAuthorizer = &podSecurityPolicyPlugin{} // NewPlugin creates a new PSP admission plugin. func NewPlugin(kclient clientset.Interface, strategyFactory psp.StrategyFactory, pspMatcher PSPMatchFn, failOnNoPolicies bool) *podSecurityPolicyPlugin { @@ -142,7 +158,7 @@ func (c *podSecurityPolicyPlugin) Admit(a admission.Attributes) error { saInfo = serviceaccount.UserInfo(a.GetNamespace(), pod.Spec.ServiceAccountName, "") } - matchedPolicies, err := c.pspMatcher(c.store, a.GetUserInfo(), saInfo) + matchedPolicies, err := c.pspMatcher(c.store, a.GetUserInfo(), saInfo, c.authz) if err != nil { return admission.NewForbidden(a, err) } @@ -287,7 +303,11 @@ func (c *podSecurityPolicyPlugin) createProvidersFromPolicies(psps []*extensions // getMatchingPolicies returns policies from the store. For now this returns everything // in the future it can filter based on UserInfo and permissions. -func getMatchingPolicies(store cache.Store, user user.Info, sa user.Info) ([]*extensions.PodSecurityPolicy, error) { +// +// TODO: this will likely need optimization since the initial implementation will +// always query for authorization. Needs scale testing and possibly checking against +// a cache. +func getMatchingPolicies(store cache.Store, user user.Info, sa user.Info, authz authorizer.Authorizer) ([]*extensions.PodSecurityPolicy, error) { matchedPolicies := make([]*extensions.PodSecurityPolicy, 0) for _, c := range store.List() { @@ -295,12 +315,42 @@ func getMatchingPolicies(store cache.Store, user user.Info, sa user.Info) ([]*ex if !ok { return nil, errors.NewInternalError(fmt.Errorf("error converting object from store to a pod security policy: %v", c)) } - matchedPolicies = append(matchedPolicies, constraint) + + if authorizedForPolicy(user, constraint, authz) || authorizedForPolicy(sa, constraint, authz) { + matchedPolicies = append(matchedPolicies, constraint) + } } return matchedPolicies, nil } +// authorizedForPolicy returns true if info is authorized to perform a "get" on policy. +func authorizedForPolicy(info user.Info, policy *extensions.PodSecurityPolicy, authz authorizer.Authorizer) bool { + // if no info exists then the API is being hit via the unsecured port. In this case + // authorize the request. + if info == nil { + return true + } + attr := buildAttributes(info, policy) + allowed, _, _ := authz.Authorize(attr) + return allowed +} + +// buildAttributes builds an attributes record for a SAR based on the user info and policy. +func buildAttributes(info user.Info, policy *extensions.PodSecurityPolicy) authorizer.Attributes { + // TODO consider checking against the namespace that the pod is being + // created in to allow per-namespace PSP definitions. + attr := authorizer.AttributesRecord{ + User: info, + Verb: "use", + Name: policy.Name, + APIGroup: extensions.GroupName, + Resource: "podsecuritypolicies", + ResourceRequest: true, + } + return attr +} + // logProviders logs what providers were found for the pod as well as any errors that were encountered // while creating providers. func logProviders(pod *api.Pod, providers []psp.Provider, providerCreationErrs []error) { diff --git a/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go b/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go index a4f17a280c7..ce21dcd6ea1 100644 --- a/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go +++ b/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go @@ -27,6 +27,7 @@ import ( kadmission "k8s.io/kubernetes/pkg/admission" kapi "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/auth/authorizer" "k8s.io/kubernetes/pkg/auth/user" "k8s.io/kubernetes/pkg/client/cache" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" @@ -36,10 +37,13 @@ import ( "k8s.io/kubernetes/pkg/security/podsecuritypolicy/seccomp" psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util" "k8s.io/kubernetes/pkg/util/diff" + "k8s.io/kubernetes/pkg/util/sets" ) const defaultContainerName = "test-c" +// NewTestAdmission provides an admission plugin with test implementations of internal structs. It uses +// an authorizer that always returns true. func NewTestAdmission(store cache.Store, kclient clientset.Interface) kadmission.Interface { return &podSecurityPolicyPlugin{ Handler: kadmission.NewHandler(kadmission.Create), @@ -47,9 +51,28 @@ func NewTestAdmission(store cache.Store, kclient clientset.Interface) kadmission store: store, strategyFactory: kpsp.NewSimpleStrategyFactory(), pspMatcher: getMatchingPolicies, + authz: &TestAuthorizer{}, } } +// TestAlwaysAllowedAuthorizer is a testing struct for testing that fulfills the authorizer interface. +type TestAuthorizer struct { + // disallowed contains names of disallowed policies. Map is keyed by user.Info.GetName() + disallowed map[string][]string +} + +func (t *TestAuthorizer) Authorize(a authorizer.Attributes) (authorized bool, reason string, err error) { + disallowedForUser, _ := t.disallowed[a.GetUser().GetName()] + for _, name := range disallowedForUser { + if a.GetName() == name { + return false, "", nil + } + } + return true, "", nil +} + +var _ authorizer.Authorizer = &TestAuthorizer{} + func useInitContainers(pod *kapi.Pod) *kapi.Pod { pod.Spec.InitContainers = pod.Spec.Containers pod.Spec.Containers = []kapi.Container{} @@ -1522,6 +1545,117 @@ func TestCreateProvidersFromConstraints(t *testing.T) { } } +func TestGetMatchingPolicies(t *testing.T) { + policyWithName := func(name string) *extensions.PodSecurityPolicy { + p := restrictivePSP() + p.Name = name + return p + } + + tests := map[string]struct { + user user.Info + sa user.Info + expectedPolicies sets.String + inPolicies []*extensions.PodSecurityPolicy + disallowedPolicies map[string][]string + }{ + "policy allowed by user": { + user: &user.DefaultInfo{Name: "user"}, + sa: &user.DefaultInfo{Name: "sa"}, + disallowedPolicies: map[string][]string{ + "sa": {"policy"}, + }, + inPolicies: []*extensions.PodSecurityPolicy{policyWithName("policy")}, + expectedPolicies: sets.NewString("policy"), + }, + "policy allowed by sa": { + user: &user.DefaultInfo{Name: "user"}, + sa: &user.DefaultInfo{Name: "sa"}, + disallowedPolicies: map[string][]string{ + "user": {"policy"}, + }, + inPolicies: []*extensions.PodSecurityPolicy{policyWithName("policy")}, + expectedPolicies: sets.NewString("policy"), + }, + "no policies allowed": { + user: &user.DefaultInfo{Name: "user"}, + sa: &user.DefaultInfo{Name: "sa"}, + disallowedPolicies: map[string][]string{ + "user": {"policy"}, + "sa": {"policy"}, + }, + inPolicies: []*extensions.PodSecurityPolicy{policyWithName("policy")}, + expectedPolicies: sets.NewString(), + }, + "multiple policies allowed": { + user: &user.DefaultInfo{Name: "user"}, + sa: &user.DefaultInfo{Name: "sa"}, + disallowedPolicies: map[string][]string{ + "user": {"policy1", "policy3"}, + "sa": {"policy2", "policy3"}, + }, + inPolicies: []*extensions.PodSecurityPolicy{ + policyWithName("policy1"), // allowed by sa + policyWithName("policy2"), // allowed by user + policyWithName("policy3"), // not allowed + }, + expectedPolicies: sets.NewString("policy1", "policy2"), + }, + "policies are allowed for nil user info": { + user: nil, + sa: &user.DefaultInfo{Name: "sa"}, + disallowedPolicies: map[string][]string{ + "user": {"policy1", "policy3"}, + "sa": {"policy2", "policy3"}, + }, + inPolicies: []*extensions.PodSecurityPolicy{ + policyWithName("policy1"), + policyWithName("policy2"), + policyWithName("policy3"), + }, + // all policies are allowed regardless of the permissions when user info is nil + // (ie. a request hitting the unsecure port) + expectedPolicies: sets.NewString("policy1", "policy2", "policy3"), + }, + "policies are allowed for nil sa info": { + user: &user.DefaultInfo{Name: "user"}, + sa: nil, + disallowedPolicies: map[string][]string{ + "user": {"policy1", "policy3"}, + "sa": {"policy2", "policy3"}, + }, + inPolicies: []*extensions.PodSecurityPolicy{ + policyWithName("policy1"), + policyWithName("policy2"), + policyWithName("policy3"), + }, + // all policies are allowed regardless of the permissions when sa info is nil + // (ie. a request hitting the unsecure port) + expectedPolicies: sets.NewString("policy1", "policy2", "policy3"), + }, + } + for k, v := range tests { + store := cache.NewStore(cache.MetaNamespaceKeyFunc) + for _, psp := range v.inPolicies { + store.Add(psp) + } + + authz := &TestAuthorizer{disallowed: v.disallowedPolicies} + allowedPolicies, err := getMatchingPolicies(store, v.user, v.sa, authz) + if err != nil { + t.Errorf("%s got unexpected error %#v", k, err) + continue + } + allowedPolicyNames := sets.NewString() + for _, p := range allowedPolicies { + allowedPolicyNames.Insert(p.Name) + } + if !v.expectedPolicies.Equal(allowedPolicyNames) { + t.Errorf("%s received unexpected policies. Expected %#v but got %#v", k, v.expectedPolicies.List(), allowedPolicyNames.List()) + } + } +} + func restrictivePSP() *extensions.PodSecurityPolicy { return &extensions.PodSecurityPolicy{ ObjectMeta: kapi.ObjectMeta{