mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 05:40:42 +00:00 
			
		
		
		
	Modify PodSecurityPolicy admission plugin to additionally allow authorizing via "use" verb in policy API group.
This commit is contained in:
		| @@ -5,7 +5,7 @@ metadata: | |||||||
|   name: restricted-psp-user |   name: restricted-psp-user | ||||||
| rules: | rules: | ||||||
| - apiGroups: | - apiGroups: | ||||||
|   - extensions |   - policy | ||||||
|   resources: |   resources: | ||||||
|   - podsecuritypolicies |   - podsecuritypolicies | ||||||
|   resourceNames: |   resourceNames: | ||||||
| @@ -20,7 +20,7 @@ metadata: | |||||||
|   name: privileged-psp-user |   name: privileged-psp-user | ||||||
| rules: | rules: | ||||||
| - apiGroups: | - apiGroups: | ||||||
|   - extensions |   - policy | ||||||
|   resources: |   resources: | ||||||
|   - podsecuritypolicies |   - podsecuritypolicies | ||||||
|   resourceNames: |   resourceNames: | ||||||
|   | |||||||
| @@ -33,6 +33,7 @@ import ( | |||||||
| 	"k8s.io/apiserver/pkg/authorization/authorizer" | 	"k8s.io/apiserver/pkg/authorization/authorizer" | ||||||
| 	api "k8s.io/kubernetes/pkg/apis/core" | 	api "k8s.io/kubernetes/pkg/apis/core" | ||||||
| 	"k8s.io/kubernetes/pkg/apis/extensions" | 	"k8s.io/kubernetes/pkg/apis/extensions" | ||||||
|  | 	"k8s.io/kubernetes/pkg/apis/policy" | ||||||
| 	informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion" | 	informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion" | ||||||
| 	extensionslisters "k8s.io/kubernetes/pkg/client/listers/extensions/internalversion" | 	extensionslisters "k8s.io/kubernetes/pkg/client/listers/extensions/internalversion" | ||||||
| 	kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission" | 	kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission" | ||||||
| @@ -354,11 +355,19 @@ func isAuthorizedForPolicy(user, sa user.Info, namespace, policyName string, aut | |||||||
| } | } | ||||||
|  |  | ||||||
| // authorizedForPolicy returns true if info is authorized to perform the "use" verb on the policy resource. | // authorizedForPolicy returns true if info is authorized to perform the "use" verb on the policy resource. | ||||||
|  | // TODO: check against only the policy group when PSP will be completely moved out of the extensions | ||||||
| func authorizedForPolicy(info user.Info, namespace string, policyName string, authz authorizer.Authorizer) bool { | func authorizedForPolicy(info user.Info, namespace string, policyName string, authz authorizer.Authorizer) bool { | ||||||
|  | 	// Check against extensions API group for backward compatibility | ||||||
|  | 	return authorizedForPolicyInAPIGroup(info, namespace, policyName, policy.GroupName, authz) || | ||||||
|  | 		authorizedForPolicyInAPIGroup(info, namespace, policyName, extensions.GroupName, authz) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // authorizedForPolicyInAPIGroup returns true if info is authorized to perform the "use" verb on the policy resource in the specified API group. | ||||||
|  | func authorizedForPolicyInAPIGroup(info user.Info, namespace, policyName, apiGroupName string, authz authorizer.Authorizer) bool { | ||||||
| 	if info == nil { | 	if info == nil { | ||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
| 	attr := buildAttributes(info, namespace, policyName) | 	attr := buildAttributes(info, namespace, policyName, apiGroupName) | ||||||
| 	decision, reason, err := authz.Authorize(attr) | 	decision, reason, err := authz.Authorize(attr) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		glog.V(5).Infof("cannot authorize for policy: %v,%v", reason, err) | 		glog.V(5).Infof("cannot authorize for policy: %v,%v", reason, err) | ||||||
| @@ -367,14 +376,14 @@ func authorizedForPolicy(info user.Info, namespace string, policyName string, au | |||||||
| } | } | ||||||
|  |  | ||||||
| // buildAttributes builds an attributes record for a SAR based on the user info and policy. | // buildAttributes builds an attributes record for a SAR based on the user info and policy. | ||||||
| func buildAttributes(info user.Info, namespace string, policyName string) authorizer.Attributes { | func buildAttributes(info user.Info, namespace, policyName, apiGroupName string) authorizer.Attributes { | ||||||
| 	// check against the namespace that the pod is being created in to allow per-namespace PSP grants. | 	// check against the namespace that the pod is being created in to allow per-namespace PSP grants. | ||||||
| 	attr := authorizer.AttributesRecord{ | 	attr := authorizer.AttributesRecord{ | ||||||
| 		User:            info, | 		User:            info, | ||||||
| 		Verb:            "use", | 		Verb:            "use", | ||||||
| 		Namespace:       namespace, | 		Namespace:       namespace, | ||||||
| 		Name:            policyName, | 		Name:            policyName, | ||||||
| 		APIGroup:        extensions.GroupName, | 		APIGroup:        apiGroupName, | ||||||
| 		Resource:        "podsecuritypolicies", | 		Resource:        "podsecuritypolicies", | ||||||
| 		ResourceRequest: true, | 		ResourceRequest: true, | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -37,6 +37,7 @@ import ( | |||||||
| 	kapi "k8s.io/kubernetes/pkg/apis/core" | 	kapi "k8s.io/kubernetes/pkg/apis/core" | ||||||
| 	"k8s.io/kubernetes/pkg/apis/core/helper" | 	"k8s.io/kubernetes/pkg/apis/core/helper" | ||||||
| 	"k8s.io/kubernetes/pkg/apis/extensions" | 	"k8s.io/kubernetes/pkg/apis/extensions" | ||||||
|  | 	"k8s.io/kubernetes/pkg/apis/policy" | ||||||
| 	informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion" | 	informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion" | ||||||
| 	"k8s.io/kubernetes/pkg/controller" | 	"k8s.io/kubernetes/pkg/controller" | ||||||
| 	"k8s.io/kubernetes/pkg/security/apparmor" | 	"k8s.io/kubernetes/pkg/security/apparmor" | ||||||
| @@ -71,6 +72,11 @@ type TestAuthorizer struct { | |||||||
| 	// usernameToNamespaceToAllowedPSPs contains the map of allowed PSPs. | 	// usernameToNamespaceToAllowedPSPs contains the map of allowed PSPs. | ||||||
| 	// if nil, all PSPs are allowed. | 	// if nil, all PSPs are allowed. | ||||||
| 	usernameToNamespaceToAllowedPSPs map[string]map[string]map[string]bool | 	usernameToNamespaceToAllowedPSPs map[string]map[string]map[string]bool | ||||||
|  | 	// allowedAPIGroupName specifies an API Group name that contains PSP resources. | ||||||
|  | 	// In order to be authorized, AttributesRecord must have this group name. | ||||||
|  | 	// When empty, API Group name isn't taken into account. | ||||||
|  | 	// TODO: remove this when PSP will be completely moved out of the extensions and we'll lookup only in "policy" group. | ||||||
|  | 	allowedAPIGroupName string | ||||||
| } | } | ||||||
|  |  | ||||||
| func (t *TestAuthorizer) Authorize(a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) { | func (t *TestAuthorizer) Authorize(a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) { | ||||||
| @@ -79,7 +85,8 @@ func (t *TestAuthorizer) Authorize(a authorizer.Attributes) (authorized authoriz | |||||||
| 	} | 	} | ||||||
| 	allowedInNamespace := t.usernameToNamespaceToAllowedPSPs[a.GetUser().GetName()][a.GetNamespace()][a.GetName()] | 	allowedInNamespace := t.usernameToNamespaceToAllowedPSPs[a.GetUser().GetName()][a.GetNamespace()][a.GetName()] | ||||||
| 	allowedClusterWide := t.usernameToNamespaceToAllowedPSPs[a.GetUser().GetName()][""][a.GetName()] | 	allowedClusterWide := t.usernameToNamespaceToAllowedPSPs[a.GetUser().GetName()][""][a.GetName()] | ||||||
| 	if allowedInNamespace || allowedClusterWide { | 	allowedAPIGroup := len(t.allowedAPIGroupName) == 0 || a.GetAPIGroup() == t.allowedAPIGroupName | ||||||
|  | 	if allowedAPIGroup && (allowedInNamespace || allowedClusterWide) { | ||||||
| 		return authorizer.DecisionAllow, "", nil | 		return authorizer.DecisionAllow, "", nil | ||||||
| 	} | 	} | ||||||
| 	return authorizer.DecisionNoOpinion, "", nil | 	return authorizer.DecisionNoOpinion, "", nil | ||||||
| @@ -1996,8 +2003,9 @@ func TestPolicyAuthorization(t *testing.T) { | |||||||
| 		expectedPolicy string | 		expectedPolicy string | ||||||
| 		inPolicies     []*extensions.PodSecurityPolicy | 		inPolicies     []*extensions.PodSecurityPolicy | ||||||
| 		allowed        map[string]map[string]map[string]bool | 		allowed        map[string]map[string]map[string]bool | ||||||
|  | 		allowedGroup   string | ||||||
| 	}{ | 	}{ | ||||||
| 		"policy allowed by user": { | 		"policy allowed by user (extensions API Group)": { | ||||||
| 			user: &user.DefaultInfo{Name: "user"}, | 			user: &user.DefaultInfo{Name: "user"}, | ||||||
| 			sa:   "sa", | 			sa:   "sa", | ||||||
| 			ns:   "test", | 			ns:   "test", | ||||||
| @@ -2009,7 +2017,7 @@ func TestPolicyAuthorization(t *testing.T) { | |||||||
| 			inPolicies:     []*extensions.PodSecurityPolicy{policyWithName("policy")}, | 			inPolicies:     []*extensions.PodSecurityPolicy{policyWithName("policy")}, | ||||||
| 			expectedPolicy: "policy", | 			expectedPolicy: "policy", | ||||||
| 		}, | 		}, | ||||||
| 		"policy allowed by sa": { | 		"policy allowed by sa (extensions API Group)": { | ||||||
| 			user: &user.DefaultInfo{Name: "user"}, | 			user: &user.DefaultInfo{Name: "user"}, | ||||||
| 			sa:   "sa", | 			sa:   "sa", | ||||||
| 			ns:   "test", | 			ns:   "test", | ||||||
| @@ -2021,6 +2029,32 @@ func TestPolicyAuthorization(t *testing.T) { | |||||||
| 			inPolicies:     []*extensions.PodSecurityPolicy{policyWithName("policy")}, | 			inPolicies:     []*extensions.PodSecurityPolicy{policyWithName("policy")}, | ||||||
| 			expectedPolicy: "policy", | 			expectedPolicy: "policy", | ||||||
| 		}, | 		}, | ||||||
|  | 		"policy allowed by user (policy API Group)": { | ||||||
|  | 			user: &user.DefaultInfo{Name: "user"}, | ||||||
|  | 			sa:   "sa", | ||||||
|  | 			ns:   "test", | ||||||
|  | 			allowed: map[string]map[string]map[string]bool{ | ||||||
|  | 				"user": { | ||||||
|  | 					"test": {"policy": true}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			inPolicies:     []*extensions.PodSecurityPolicy{policyWithName("policy")}, | ||||||
|  | 			expectedPolicy: "policy", | ||||||
|  | 			allowedGroup:   policy.GroupName, | ||||||
|  | 		}, | ||||||
|  | 		"policy allowed by sa (policy API Group)": { | ||||||
|  | 			user: &user.DefaultInfo{Name: "user"}, | ||||||
|  | 			sa:   "sa", | ||||||
|  | 			ns:   "test", | ||||||
|  | 			allowed: map[string]map[string]map[string]bool{ | ||||||
|  | 				serviceaccount.MakeUsername("test", "sa"): { | ||||||
|  | 					"test": {"policy": true}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			inPolicies:     []*extensions.PodSecurityPolicy{policyWithName("policy")}, | ||||||
|  | 			expectedPolicy: "policy", | ||||||
|  | 			allowedGroup:   policy.GroupName, | ||||||
|  | 		}, | ||||||
| 		"no policies allowed": { | 		"no policies allowed": { | ||||||
| 			user:           &user.DefaultInfo{Name: "user"}, | 			user:           &user.DefaultInfo{Name: "user"}, | ||||||
| 			sa:             "sa", | 			sa:             "sa", | ||||||
| @@ -2122,7 +2156,7 @@ func TestPolicyAuthorization(t *testing.T) { | |||||||
| 		var ( | 		var ( | ||||||
| 			oldPod     *kapi.Pod | 			oldPod     *kapi.Pod | ||||||
| 			shouldPass = v.expectedPolicy != "" | 			shouldPass = v.expectedPolicy != "" | ||||||
| 			authz      = &TestAuthorizer{usernameToNamespaceToAllowedPSPs: v.allowed} | 			authz      = &TestAuthorizer{usernameToNamespaceToAllowedPSPs: v.allowed, allowedAPIGroupName: v.allowedGroup} | ||||||
| 			canMutate  = true | 			canMutate  = true | ||||||
| 		) | 		) | ||||||
| 		pod := goodPod() | 		pod := goodPod() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user