Commit 3d4fa8a1 authored by Slava Semushin's avatar Slava Semushin

Modify PodSecurityPolicy admission plugin to additionally allow authorizing via…

Modify PodSecurityPolicy admission plugin to additionally allow authorizing via "use" verb in policy API group.
parent a4222bd8
...@@ -5,7 +5,7 @@ metadata: ...@@ -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: ...@@ -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 ( ...@@ -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 ...@@ -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 ...@@ -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 ( ...@@ -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 { ...@@ -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 ...@@ -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,33 @@ func TestPolicyAuthorization(t *testing.T) { ...@@ -1996,8 +2003,33 @@ 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"},
sa: "sa",
ns: "test",
allowed: map[string]map[string]map[string]bool{
"user": {
"test": {"policy": true},
},
},
inPolicies: []*extensions.PodSecurityPolicy{policyWithName("policy")},
expectedPolicy: "policy",
},
"policy allowed by sa (extensions 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",
},
"policy allowed by user (policy API Group)": {
user: &user.DefaultInfo{Name: "user"}, user: &user.DefaultInfo{Name: "user"},
sa: "sa", sa: "sa",
ns: "test", ns: "test",
...@@ -2008,8 +2040,9 @@ func TestPolicyAuthorization(t *testing.T) { ...@@ -2008,8 +2040,9 @@ func TestPolicyAuthorization(t *testing.T) {
}, },
inPolicies: []*extensions.PodSecurityPolicy{policyWithName("policy")}, inPolicies: []*extensions.PodSecurityPolicy{policyWithName("policy")},
expectedPolicy: "policy", expectedPolicy: "policy",
allowedGroup: policy.GroupName,
}, },
"policy allowed by sa": { "policy allowed by sa (policy API Group)": {
user: &user.DefaultInfo{Name: "user"}, user: &user.DefaultInfo{Name: "user"},
sa: "sa", sa: "sa",
ns: "test", ns: "test",
...@@ -2020,6 +2053,7 @@ func TestPolicyAuthorization(t *testing.T) { ...@@ -2020,6 +2053,7 @@ func TestPolicyAuthorization(t *testing.T) {
}, },
inPolicies: []*extensions.PodSecurityPolicy{policyWithName("policy")}, inPolicies: []*extensions.PodSecurityPolicy{policyWithName("policy")},
expectedPolicy: "policy", expectedPolicy: "policy",
allowedGroup: policy.GroupName,
}, },
"no policies allowed": { "no policies allowed": {
user: &user.DefaultInfo{Name: "user"}, user: &user.DefaultInfo{Name: "user"},
...@@ -2122,7 +2156,7 @@ func TestPolicyAuthorization(t *testing.T) { ...@@ -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()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment