mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-31 15:25:57 +00:00
Optimize PSP authorization
This commit is contained in:
parent
5337ff8009
commit
9673235583
@ -42,7 +42,6 @@ go_test(
|
||||
"//pkg/apis/core/helper:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/client/informers/informers_generated/internalversion:go_default_library",
|
||||
"//pkg/client/listers/extensions/internalversion:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//pkg/security/apparmor:go_default_library",
|
||||
"//pkg/security/podsecuritypolicy:go_default_library",
|
||||
@ -53,8 +52,8 @@ go_test(
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/serviceaccount:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||
],
|
||||
|
@ -49,19 +49,15 @@ const (
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
|
||||
plugin := NewPlugin(psp.NewSimpleStrategyFactory(), getMatchingPolicies, true)
|
||||
plugin := newPlugin(psp.NewSimpleStrategyFactory(), true)
|
||||
return plugin, nil
|
||||
})
|
||||
}
|
||||
|
||||
// PSPMatchFn allows plugging in how PSPs are matched against user information.
|
||||
type PSPMatchFn func(lister extensionslisters.PodSecurityPolicyLister, user user.Info, sa user.Info, authz authorizer.Authorizer, namespace string) ([]*extensions.PodSecurityPolicy, error)
|
||||
|
||||
// PodSecurityPolicyPlugin holds state for and implements the admission plugin.
|
||||
type PodSecurityPolicyPlugin struct {
|
||||
*admission.Handler
|
||||
strategyFactory psp.StrategyFactory
|
||||
pspMatcher PSPMatchFn
|
||||
failOnNoPolicies bool
|
||||
authz authorizer.Authorizer
|
||||
lister extensionslisters.PodSecurityPolicyLister
|
||||
@ -88,12 +84,11 @@ var _ admission.ValidationInterface = &PodSecurityPolicyPlugin{}
|
||||
var _ genericadmissioninit.WantsAuthorizer = &PodSecurityPolicyPlugin{}
|
||||
var _ kubeapiserveradmission.WantsInternalKubeInformerFactory = &PodSecurityPolicyPlugin{}
|
||||
|
||||
// NewPlugin creates a new PSP admission plugin.
|
||||
func NewPlugin(strategyFactory psp.StrategyFactory, pspMatcher PSPMatchFn, failOnNoPolicies bool) *PodSecurityPolicyPlugin {
|
||||
// newPlugin creates a new PSP admission plugin.
|
||||
func newPlugin(strategyFactory psp.StrategyFactory, failOnNoPolicies bool) *PodSecurityPolicyPlugin {
|
||||
return &PodSecurityPolicyPlugin{
|
||||
Handler: admission.NewHandler(admission.Create, admission.Update),
|
||||
strategyFactory: strategyFactory,
|
||||
pspMatcher: pspMatcher,
|
||||
failOnNoPolicies: failOnNoPolicies,
|
||||
}
|
||||
}
|
||||
@ -207,49 +202,60 @@ func (c *PodSecurityPolicyPlugin) computeSecurityContext(a admission.Attributes,
|
||||
saInfo = serviceaccount.UserInfo(a.GetNamespace(), pod.Spec.ServiceAccountName, "")
|
||||
}
|
||||
|
||||
matchedPolicies, err := c.pspMatcher(c.lister, a.GetUserInfo(), saInfo, c.authz, a.GetNamespace())
|
||||
policies, err := c.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
return nil, "", nil, err
|
||||
}
|
||||
|
||||
// if we have no policies and want to succeed then return. Otherwise we'll end up with no
|
||||
// providers and fail with "unable to validate against any pod security policy" below.
|
||||
if len(matchedPolicies) == 0 && !c.failOnNoPolicies {
|
||||
if len(policies) == 0 && !c.failOnNoPolicies {
|
||||
return pod, "", nil, nil
|
||||
}
|
||||
|
||||
// sort by name to make order deterministic
|
||||
// TODO(liggitt): add priority field to allow admins to bucket differently
|
||||
sort.SliceStable(matchedPolicies, func(i, j int) bool {
|
||||
return strings.Compare(matchedPolicies[i].Name, matchedPolicies[j].Name) < 0
|
||||
sort.SliceStable(policies, func(i, j int) bool {
|
||||
return strings.Compare(policies[i].Name, policies[j].Name) < 0
|
||||
})
|
||||
|
||||
providers, errs := c.createProvidersFromPolicies(matchedPolicies, pod.Namespace)
|
||||
logProviders(a, pod, providers, errs)
|
||||
providers, errs := c.createProvidersFromPolicies(policies, pod.Namespace)
|
||||
for _, err := range errs {
|
||||
glog.V(4).Infof("provider creation error: %v", err)
|
||||
}
|
||||
|
||||
if len(providers) == 0 {
|
||||
return nil, "", nil, fmt.Errorf("no providers available to validate pod request")
|
||||
}
|
||||
|
||||
// all containers in a single pod must validate under a single provider or we will reject the request
|
||||
validationErrs := field.ErrorList{}
|
||||
var (
|
||||
allowedMutatedPod *api.Pod
|
||||
allowingMutatingPSP string
|
||||
// Map of PSP name to associated validation errors.
|
||||
validationErrs = map[string]field.ErrorList{}
|
||||
)
|
||||
|
||||
for _, provider := range providers {
|
||||
podCopy := pod.DeepCopy()
|
||||
|
||||
if errs := assignSecurityContext(provider, podCopy, field.NewPath(fmt.Sprintf("provider %s: ", provider.GetPSPName()))); len(errs) > 0 {
|
||||
validationErrs = append(validationErrs, errs...)
|
||||
validationErrs[provider.GetPSPName()] = errs
|
||||
continue
|
||||
}
|
||||
|
||||
// the entire pod validated
|
||||
|
||||
mutated := !apiequality.Semantic.DeepEqual(pod, podCopy)
|
||||
if mutated && !specMutationAllowed {
|
||||
continue
|
||||
}
|
||||
|
||||
if !isAuthorizedForPolicy(a.GetUserInfo(), saInfo, a.GetNamespace(), provider.GetPSPName(), c.authz) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case apiequality.Semantic.DeepEqual(pod, podCopy):
|
||||
case !mutated:
|
||||
// if it validated without mutating anything, use this result
|
||||
return podCopy, provider.GetPSPName(), nil, nil
|
||||
|
||||
@ -261,11 +267,18 @@ func (c *PodSecurityPolicyPlugin) computeSecurityContext(a admission.Attributes,
|
||||
}
|
||||
}
|
||||
|
||||
if allowedMutatedPod == nil {
|
||||
return nil, "", validationErrs, nil
|
||||
if allowedMutatedPod != nil {
|
||||
return allowedMutatedPod, allowingMutatingPSP, nil, nil
|
||||
}
|
||||
|
||||
return allowedMutatedPod, allowingMutatingPSP, nil, nil
|
||||
// Pod is rejected. Filter the validation errors to only include errors from authorized PSPs.
|
||||
aggregate := field.ErrorList{}
|
||||
for psp, errs := range validationErrs {
|
||||
if isAuthorizedForPolicy(a.GetUserInfo(), saInfo, a.GetNamespace(), psp, c.authz) {
|
||||
aggregate = append(aggregate, errs...)
|
||||
}
|
||||
}
|
||||
return nil, "", aggregate, nil
|
||||
}
|
||||
|
||||
// assignSecurityContext creates a security context for each container in the pod
|
||||
@ -332,35 +345,18 @@ func (c *PodSecurityPolicyPlugin) createProvidersFromPolicies(psps []*extensions
|
||||
return providers, errs
|
||||
}
|
||||
|
||||
// getMatchingPolicies returns policies from the lister. For now this returns everything
|
||||
// in the future it can filter based on UserInfo and permissions.
|
||||
//
|
||||
// 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(lister extensionslisters.PodSecurityPolicyLister, user user.Info, sa user.Info, authz authorizer.Authorizer, namespace string) ([]*extensions.PodSecurityPolicy, error) {
|
||||
matchedPolicies := make([]*extensions.PodSecurityPolicy, 0)
|
||||
|
||||
list, err := lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, constraint := range list {
|
||||
if authorizedForPolicy(user, namespace, constraint, authz) || authorizedForPolicy(sa, namespace, constraint, authz) {
|
||||
matchedPolicies = append(matchedPolicies, constraint)
|
||||
}
|
||||
}
|
||||
|
||||
return matchedPolicies, nil
|
||||
func isAuthorizedForPolicy(user, sa user.Info, namespace, policyName string, authz authorizer.Authorizer) bool {
|
||||
// Check the service account first, as that is the more common use case.
|
||||
return authorizedForPolicy(sa, namespace, policyName, authz) ||
|
||||
authorizedForPolicy(user, namespace, policyName, authz)
|
||||
}
|
||||
|
||||
// authorizedForPolicy returns true if info is authorized to perform the "use" verb on the policy resource.
|
||||
func authorizedForPolicy(info user.Info, namespace string, policy *extensions.PodSecurityPolicy, authz authorizer.Authorizer) bool {
|
||||
func authorizedForPolicy(info user.Info, namespace string, policyName string, authz authorizer.Authorizer) bool {
|
||||
if info == nil {
|
||||
return false
|
||||
}
|
||||
attr := buildAttributes(info, namespace, policy)
|
||||
attr := buildAttributes(info, namespace, policyName)
|
||||
decision, reason, err := authz.Authorize(attr)
|
||||
if err != nil {
|
||||
glog.V(5).Infof("cannot authorize for policy: %v,%v", reason, err)
|
||||
@ -369,35 +365,16 @@ func authorizedForPolicy(info user.Info, namespace string, policy *extensions.Po
|
||||
}
|
||||
|
||||
// buildAttributes builds an attributes record for a SAR based on the user info and policy.
|
||||
func buildAttributes(info user.Info, namespace string, policy *extensions.PodSecurityPolicy) authorizer.Attributes {
|
||||
func buildAttributes(info user.Info, namespace string, policyName string) authorizer.Attributes {
|
||||
// check against the namespace that the pod is being created in to allow per-namespace PSP grants.
|
||||
attr := authorizer.AttributesRecord{
|
||||
User: info,
|
||||
Verb: "use",
|
||||
Namespace: namespace,
|
||||
Name: policy.Name,
|
||||
Name: policyName,
|
||||
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(a admission.Attributes, pod *api.Pod, providers []psp.Provider, providerCreationErrs []error) {
|
||||
for _, err := range providerCreationErrs {
|
||||
glog.V(4).Infof("provider creation error: %v", err)
|
||||
}
|
||||
|
||||
if len(providers) == 0 {
|
||||
glog.V(4).Infof("unable to validate pod %s (generate: %s) in namespace %s against any provider.", pod.Name, pod.GenerateName, a.GetNamespace())
|
||||
return
|
||||
}
|
||||
|
||||
names := make([]string, len(providers))
|
||||
for i, p := range providers {
|
||||
names[i] = p.GetPSPName()
|
||||
}
|
||||
glog.V(4).Infof("validating pod %s (generate: %s) in namespace %s against providers: %s", pod.Name, pod.GenerateName, a.GetNamespace(), strings.Join(names, ","))
|
||||
}
|
||||
|
@ -28,8 +28,8 @@ import (
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
kadmission "k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/authentication/serviceaccount"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
@ -37,7 +37,6 @@ import (
|
||||
"k8s.io/kubernetes/pkg/apis/core/helper"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
|
||||
extensionslisters "k8s.io/kubernetes/pkg/client/listers/extensions/internalversion"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
"k8s.io/kubernetes/pkg/security/apparmor"
|
||||
kpsp "k8s.io/kubernetes/pkg/security/podsecuritypolicy"
|
||||
@ -47,14 +46,21 @@ import (
|
||||
|
||||
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(lister extensionslisters.PodSecurityPolicyLister) *PodSecurityPolicyPlugin {
|
||||
// NewTestAdmission provides an admission plugin with test implementations of internal structs.
|
||||
func NewTestAdmission(psps []*extensions.PodSecurityPolicy, authz authorizer.Authorizer) *PodSecurityPolicyPlugin {
|
||||
informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
|
||||
store := informerFactory.Extensions().InternalVersion().PodSecurityPolicies().Informer().GetStore()
|
||||
for _, psp := range psps {
|
||||
store.Add(psp)
|
||||
}
|
||||
lister := informerFactory.Extensions().InternalVersion().PodSecurityPolicies().Lister()
|
||||
if authz == nil {
|
||||
authz = &TestAuthorizer{}
|
||||
}
|
||||
return &PodSecurityPolicyPlugin{
|
||||
Handler: kadmission.NewHandler(kadmission.Create, kadmission.Update),
|
||||
strategyFactory: kpsp.NewSimpleStrategyFactory(),
|
||||
pspMatcher: getMatchingPolicies,
|
||||
authz: &TestAuthorizer{},
|
||||
authz: authz,
|
||||
lister: lister,
|
||||
}
|
||||
}
|
||||
@ -434,7 +440,7 @@ func TestAdmitPreferNonmutating(t *testing.T) {
|
||||
}
|
||||
|
||||
for k, v := range tests {
|
||||
testPSPAdmitAdvanced(k, v.operation, v.psps, v.pod, v.podBeforeUpdate, v.shouldPassAdmit, v.shouldPassValidate, v.expectMutation, v.expectedPSP, t)
|
||||
testPSPAdmitAdvanced(k, v.operation, v.psps, nil, &user.DefaultInfo{}, v.pod, v.podBeforeUpdate, v.shouldPassAdmit, v.shouldPassValidate, v.expectMutation, v.expectedPSP, t)
|
||||
|
||||
if v.shouldPassAdmit {
|
||||
actualPodUser := (*int64)(nil)
|
||||
@ -461,7 +467,7 @@ func TestAdmitPreferNonmutating(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFailClosedOnInvalidPod(t *testing.T) {
|
||||
plugin := NewTestAdmission(nil)
|
||||
plugin := NewTestAdmission(nil, nil)
|
||||
pod := &v1.Pod{}
|
||||
attrs := kadmission.NewAttributesRecord(pod, nil, kapi.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, kapi.Resource("pods").WithVersion("version"), "", kadmission.Create, &user.DefaultInfo{})
|
||||
|
||||
@ -1785,22 +1791,14 @@ func TestAdmitSysctls(t *testing.T) {
|
||||
}
|
||||
|
||||
func testPSPAdmit(testCaseName string, psps []*extensions.PodSecurityPolicy, pod *kapi.Pod, shouldPassAdmit, shouldPassValidate bool, expectedPSP string, t *testing.T) {
|
||||
testPSPAdmitAdvanced(testCaseName, kadmission.Create, psps, pod, nil, shouldPassAdmit, shouldPassValidate, true, expectedPSP, t)
|
||||
testPSPAdmitAdvanced(testCaseName, kadmission.Create, psps, nil, &user.DefaultInfo{}, pod, nil, shouldPassAdmit, shouldPassValidate, true, expectedPSP, t)
|
||||
}
|
||||
|
||||
func testPSPAdmitAdvanced(testCaseName string, op kadmission.Operation, psps []*extensions.PodSecurityPolicy, pod, oldPod *kapi.Pod, shouldPassAdmit, shouldPassValidate bool, canMutate bool, expectedPSP string, t *testing.T) {
|
||||
informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
|
||||
store := informerFactory.Extensions().InternalVersion().PodSecurityPolicies().Informer().GetStore()
|
||||
|
||||
for _, psp := range psps {
|
||||
store.Add(psp)
|
||||
}
|
||||
|
||||
func testPSPAdmitAdvanced(testCaseName string, op kadmission.Operation, psps []*extensions.PodSecurityPolicy, authz authorizer.Authorizer, userInfo user.Info, pod, oldPod *kapi.Pod, shouldPassAdmit, shouldPassValidate bool, canMutate bool, expectedPSP string, t *testing.T) {
|
||||
originalPod := pod.DeepCopy()
|
||||
plugin := NewTestAdmission(psps, authz)
|
||||
|
||||
plugin := NewTestAdmission(informerFactory.Extensions().InternalVersion().PodSecurityPolicies().Lister())
|
||||
|
||||
attrs := kadmission.NewAttributesRecord(pod, oldPod, kapi.Kind("Pod").WithVersion("version"), "namespace", "", kapi.Resource("pods").WithVersion("version"), "", op, &user.DefaultInfo{})
|
||||
attrs := kadmission.NewAttributesRecord(pod, oldPod, kapi.Kind("Pod").WithVersion("version"), pod.Namespace, "", kapi.Resource("pods").WithVersion("version"), "", op, userInfo)
|
||||
err := plugin.Admit(attrs)
|
||||
|
||||
if shouldPassAdmit && err != nil {
|
||||
@ -1987,59 +1985,59 @@ func TestCreateProvidersFromConstraints(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMatchingPolicies(t *testing.T) {
|
||||
func TestPolicyAuthorization(t *testing.T) {
|
||||
policyWithName := func(name string) *extensions.PodSecurityPolicy {
|
||||
p := restrictivePSP()
|
||||
p := permissivePSP()
|
||||
p.Name = name
|
||||
return p
|
||||
}
|
||||
|
||||
tests := map[string]struct {
|
||||
user user.Info
|
||||
sa user.Info
|
||||
ns string
|
||||
expectedPolicies sets.String
|
||||
inPolicies []*extensions.PodSecurityPolicy
|
||||
allowed map[string]map[string]map[string]bool
|
||||
user user.Info
|
||||
sa string
|
||||
ns string
|
||||
expectedPolicy string
|
||||
inPolicies []*extensions.PodSecurityPolicy
|
||||
allowed map[string]map[string]map[string]bool
|
||||
}{
|
||||
"policy allowed by user": {
|
||||
user: &user.DefaultInfo{Name: "user"},
|
||||
sa: &user.DefaultInfo{Name: "sa"},
|
||||
sa: "sa",
|
||||
ns: "test",
|
||||
allowed: map[string]map[string]map[string]bool{
|
||||
"user": {
|
||||
"test": {"policy": true},
|
||||
},
|
||||
},
|
||||
inPolicies: []*extensions.PodSecurityPolicy{policyWithName("policy")},
|
||||
expectedPolicies: sets.NewString("policy"),
|
||||
inPolicies: []*extensions.PodSecurityPolicy{policyWithName("policy")},
|
||||
expectedPolicy: "policy",
|
||||
},
|
||||
"policy allowed by sa": {
|
||||
user: &user.DefaultInfo{Name: "user"},
|
||||
sa: &user.DefaultInfo{Name: "sa"},
|
||||
sa: "sa",
|
||||
ns: "test",
|
||||
allowed: map[string]map[string]map[string]bool{
|
||||
"sa": {
|
||||
serviceaccount.MakeUsername("test", "sa"): {
|
||||
"test": {"policy": true},
|
||||
},
|
||||
},
|
||||
inPolicies: []*extensions.PodSecurityPolicy{policyWithName("policy")},
|
||||
expectedPolicies: sets.NewString("policy"),
|
||||
inPolicies: []*extensions.PodSecurityPolicy{policyWithName("policy")},
|
||||
expectedPolicy: "policy",
|
||||
},
|
||||
"no policies allowed": {
|
||||
user: &user.DefaultInfo{Name: "user"},
|
||||
sa: &user.DefaultInfo{Name: "sa"},
|
||||
ns: "test",
|
||||
allowed: map[string]map[string]map[string]bool{},
|
||||
inPolicies: []*extensions.PodSecurityPolicy{policyWithName("policy")},
|
||||
expectedPolicies: sets.NewString(),
|
||||
user: &user.DefaultInfo{Name: "user"},
|
||||
sa: "sa",
|
||||
ns: "test",
|
||||
allowed: map[string]map[string]map[string]bool{},
|
||||
inPolicies: []*extensions.PodSecurityPolicy{policyWithName("policy")},
|
||||
expectedPolicy: "",
|
||||
},
|
||||
"multiple policies allowed": {
|
||||
user: &user.DefaultInfo{Name: "user"},
|
||||
sa: &user.DefaultInfo{Name: "sa"},
|
||||
sa: "sa",
|
||||
ns: "test",
|
||||
allowed: map[string]map[string]map[string]bool{
|
||||
"sa": {
|
||||
serviceaccount.MakeUsername("test", "sa"): {
|
||||
"test": {"policy1": true},
|
||||
"": {"policy4": true},
|
||||
"other": {"policy6": true},
|
||||
@ -2051,22 +2049,23 @@ func TestGetMatchingPolicies(t *testing.T) {
|
||||
},
|
||||
},
|
||||
inPolicies: []*extensions.PodSecurityPolicy{
|
||||
policyWithName("policy1"), // allowed by sa
|
||||
policyWithName("policy2"), // allowed by user
|
||||
policyWithName("policy3"), // not allowed
|
||||
policyWithName("policy4"), // allowed by sa at cluster level
|
||||
policyWithName("policy5"), // allowed by user at cluster level
|
||||
policyWithName("policy6"), // not allowed in this namespace
|
||||
policyWithName("policy7"), // not allowed in this namespace
|
||||
// Prefix to force checking these policies first.
|
||||
policyWithName("a_policy1"), // not allowed in this namespace
|
||||
policyWithName("a_policy2"), // not allowed in this namespace
|
||||
policyWithName("policy2"), // allowed by sa
|
||||
policyWithName("policy3"), // allowed by user
|
||||
policyWithName("policy4"), // not allowed
|
||||
policyWithName("policy5"), // allowed by sa at cluster level
|
||||
policyWithName("policy6"), // allowed by user at cluster level
|
||||
},
|
||||
expectedPolicies: sets.NewString("policy1", "policy2", "policy4", "policy5"),
|
||||
expectedPolicy: "policy2",
|
||||
},
|
||||
"policies are not allowed for nil user info": {
|
||||
user: nil,
|
||||
sa: &user.DefaultInfo{Name: "sa"},
|
||||
sa: "sa",
|
||||
ns: "test",
|
||||
allowed: map[string]map[string]map[string]bool{
|
||||
"sa": {
|
||||
serviceaccount.MakeUsername("test", "sa"): {
|
||||
"test": {"policy1": true},
|
||||
},
|
||||
"user": {
|
||||
@ -2079,14 +2078,14 @@ func TestGetMatchingPolicies(t *testing.T) {
|
||||
policyWithName("policy3"),
|
||||
},
|
||||
// only the policies for the sa are allowed when user info is nil
|
||||
expectedPolicies: sets.NewString("policy1"),
|
||||
expectedPolicy: "policy1",
|
||||
},
|
||||
"policies are not allowed for nil sa info": {
|
||||
user: &user.DefaultInfo{Name: "user"},
|
||||
sa: nil,
|
||||
sa: "",
|
||||
ns: "test",
|
||||
allowed: map[string]map[string]map[string]bool{
|
||||
"sa": {
|
||||
serviceaccount.MakeUsername("test", "sa"): {
|
||||
"test": {"policy1": true},
|
||||
},
|
||||
"user": {
|
||||
@ -2099,14 +2098,14 @@ func TestGetMatchingPolicies(t *testing.T) {
|
||||
policyWithName("policy3"),
|
||||
},
|
||||
// only the policies for the user are allowed when sa info is nil
|
||||
expectedPolicies: sets.NewString("policy2"),
|
||||
expectedPolicy: "policy2",
|
||||
},
|
||||
"policies are not allowed for nil sa and user info": {
|
||||
user: nil,
|
||||
sa: nil,
|
||||
sa: "",
|
||||
ns: "test",
|
||||
allowed: map[string]map[string]map[string]bool{
|
||||
"sa": {
|
||||
serviceaccount.MakeUsername("test", "sa"): {
|
||||
"test": {"policy1": true},
|
||||
},
|
||||
"user": {
|
||||
@ -2119,30 +2118,110 @@ func TestGetMatchingPolicies(t *testing.T) {
|
||||
policyWithName("policy3"),
|
||||
},
|
||||
// no policies are allowed if sa and user are both nil
|
||||
expectedPolicies: sets.NewString(),
|
||||
expectedPolicy: "",
|
||||
},
|
||||
}
|
||||
for k, v := range tests {
|
||||
informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
|
||||
pspInformer := informerFactory.Extensions().InternalVersion().PodSecurityPolicies()
|
||||
store := pspInformer.Informer().GetStore()
|
||||
for _, psp := range v.inPolicies {
|
||||
store.Add(psp)
|
||||
}
|
||||
var (
|
||||
oldPod *kapi.Pod
|
||||
shouldPass = v.expectedPolicy != ""
|
||||
authz = &TestAuthorizer{usernameToNamespaceToAllowedPSPs: v.allowed}
|
||||
canMutate = true
|
||||
)
|
||||
pod := goodPod()
|
||||
pod.Namespace = v.ns
|
||||
pod.Spec.ServiceAccountName = v.sa
|
||||
testPSPAdmitAdvanced(k, kadmission.Create, v.inPolicies, authz, v.user,
|
||||
pod, oldPod, shouldPass, shouldPass, canMutate, v.expectedPolicy, t)
|
||||
}
|
||||
}
|
||||
|
||||
authz := &TestAuthorizer{usernameToNamespaceToAllowedPSPs: v.allowed}
|
||||
allowedPolicies, err := getMatchingPolicies(pspInformer.Lister(), v.user, v.sa, authz, v.ns)
|
||||
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 TestPolicyAuthorizationErrors(t *testing.T) {
|
||||
policyWithName := func(name string) *extensions.PodSecurityPolicy {
|
||||
p := restrictivePSP()
|
||||
p.Name = name
|
||||
return p
|
||||
}
|
||||
|
||||
const (
|
||||
sa = "sa"
|
||||
ns = "test"
|
||||
userName = "user"
|
||||
)
|
||||
|
||||
tests := map[string]struct {
|
||||
priviliged bool
|
||||
inPolicies []*extensions.PodSecurityPolicy
|
||||
allowed map[string]map[string]map[string]bool
|
||||
expectValidationErrs int
|
||||
}{
|
||||
"policies not allowed": {
|
||||
allowed: map[string]map[string]map[string]bool{},
|
||||
inPolicies: []*extensions.PodSecurityPolicy{
|
||||
policyWithName("policy1"),
|
||||
policyWithName("policy2"),
|
||||
},
|
||||
expectValidationErrs: 0,
|
||||
},
|
||||
"policy allowed by user": {
|
||||
allowed: map[string]map[string]map[string]bool{
|
||||
"user": {
|
||||
"test": {"policy1": true},
|
||||
},
|
||||
},
|
||||
inPolicies: []*extensions.PodSecurityPolicy{
|
||||
policyWithName("policy1"),
|
||||
policyWithName("policy2"),
|
||||
},
|
||||
expectValidationErrs: 1,
|
||||
},
|
||||
"policy allowed by service account": {
|
||||
allowed: map[string]map[string]map[string]bool{
|
||||
serviceaccount.MakeUsername("test", "sa"): {
|
||||
"test": {"policy2": true},
|
||||
},
|
||||
},
|
||||
inPolicies: []*extensions.PodSecurityPolicy{
|
||||
policyWithName("policy1"),
|
||||
policyWithName("policy2"),
|
||||
},
|
||||
expectValidationErrs: 1,
|
||||
},
|
||||
"multiple policies allowed": {
|
||||
allowed: map[string]map[string]map[string]bool{
|
||||
"user": {
|
||||
"test": {"policy1": true},
|
||||
},
|
||||
serviceaccount.MakeUsername("test", "sa"): {
|
||||
"test": {"policy2": true},
|
||||
},
|
||||
},
|
||||
inPolicies: []*extensions.PodSecurityPolicy{
|
||||
policyWithName("policy1"),
|
||||
policyWithName("policy2"),
|
||||
},
|
||||
expectValidationErrs: 2,
|
||||
},
|
||||
}
|
||||
for desc, tc := range tests {
|
||||
t.Run(desc, func(t *testing.T) {
|
||||
var (
|
||||
authz = &TestAuthorizer{usernameToNamespaceToAllowedPSPs: tc.allowed}
|
||||
privileged = true
|
||||
)
|
||||
pod := goodPod()
|
||||
pod.Namespace = ns
|
||||
pod.Spec.ServiceAccountName = sa
|
||||
pod.Spec.Containers[0].SecurityContext.Privileged = &privileged
|
||||
|
||||
plugin := NewTestAdmission(tc.inPolicies, authz)
|
||||
attrs := kadmission.NewAttributesRecord(pod, nil, kapi.Kind("Pod").WithVersion("version"), ns, "", kapi.Resource("pods").WithVersion("version"), "", kadmission.Create, &user.DefaultInfo{Name: userName})
|
||||
|
||||
allowedPod, _, validationErrs, err := plugin.computeSecurityContext(attrs, pod, true)
|
||||
assert.Nil(t, allowedPod)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, validationErrs, tc.expectValidationErrs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -2217,6 +2296,8 @@ func permissivePSP() *extensions.PodSecurityPolicy {
|
||||
func goodPod() *kapi.Pod {
|
||||
return &kapi.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod",
|
||||
Namespace: "namespace",
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
Spec: kapi.PodSpec{
|
||||
|
Loading…
Reference in New Issue
Block a user