Merge pull request #60145 from php-coder/psp_authz_via_policy_group

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

PSP plugin: allow authorizing via "use" verb in policy API group

**What this PR does / why we need it**:
In order to determine whether a service account/user has access to PSP, PodSecurityPolicy admission plugin tests whether a service account/user is authorized for "use" verb in `extensions` API group. As PSP is being migrated to `policy` API group, we need to support its new location. This PR adds such a support by checking in both API groups.

**Which issue(s) this PR fixes**:
Addressed to: https://github.com/kubernetes/features/issues/5
Follow-up to: https://github.com/kubernetes/kubernetes/pull/54933
This commit is contained in:
Kubernetes Submit Queue 2018-02-23 07:55:23 -08:00 committed by GitHub
commit aaeccd3d10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 54 additions and 9 deletions

View File

@ -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:

View File

@ -13,6 +13,7 @@ go_library(
deps = [ deps = [
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions:go_default_library",
"//pkg/apis/policy:go_default_library",
"//pkg/client/informers/informers_generated/internalversion:go_default_library", "//pkg/client/informers/informers_generated/internalversion:go_default_library",
"//pkg/client/listers/extensions/internalversion:go_default_library", "//pkg/client/listers/extensions/internalversion:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library", "//pkg/kubeapiserver/admission:go_default_library",
@ -40,6 +41,7 @@ go_test(
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/apis/core/helper:go_default_library", "//pkg/apis/core/helper:go_default_library",
"//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions:go_default_library",
"//pkg/apis/policy:go_default_library",
"//pkg/client/informers/informers_generated/internalversion:go_default_library", "//pkg/client/informers/informers_generated/internalversion:go_default_library",
"//pkg/controller:go_default_library", "//pkg/controller:go_default_library",
"//pkg/security/apparmor:go_default_library", "//pkg/security/apparmor:go_default_library",

View File

@ -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,
} }

View File

@ -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()