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 @@
+
+
+
+
+
+
+
+
+
+
+
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
+```
+
+
+[]()
+
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{