Merge pull request #34047 from deads2k/rbac-11-informer-cache

Automatic merge from submit-queue

Run rbac authorizer from cache

RBAC authorization can be run very effectively out of a cache.  The cache is a normal reflector backed cache (shared informer).

I've split this into three parts:
 1. slim down the authorizer interfaces
 1. boilerplate for adding rbac shared informers and associated listers which conform to the new interfaces
 1. wiring

@liggitt @ericchiang @kubernetes/sig-auth
This commit is contained in:
Kubernetes Submit Queue 2016-10-14 08:12:28 -07:00 committed by GitHub
commit c3742a8fbe
18 changed files with 606 additions and 210 deletions

View File

@ -39,7 +39,6 @@ import (
"k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apiserver" "k8s.io/kubernetes/pkg/apiserver"
"k8s.io/kubernetes/pkg/apiserver/authenticator" "k8s.io/kubernetes/pkg/apiserver/authenticator"
"k8s.io/kubernetes/pkg/apiserver/openapi" "k8s.io/kubernetes/pkg/apiserver/openapi"
@ -52,20 +51,10 @@ import (
generatedopenapi "k8s.io/kubernetes/pkg/generated/openapi" generatedopenapi "k8s.io/kubernetes/pkg/generated/openapi"
"k8s.io/kubernetes/pkg/genericapiserver" "k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/genericapiserver/authorizer" "k8s.io/kubernetes/pkg/genericapiserver/authorizer"
genericoptions "k8s.io/kubernetes/pkg/genericapiserver/options"
genericvalidation "k8s.io/kubernetes/pkg/genericapiserver/validation" genericvalidation "k8s.io/kubernetes/pkg/genericapiserver/validation"
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client" kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
"k8s.io/kubernetes/pkg/master" "k8s.io/kubernetes/pkg/master"
"k8s.io/kubernetes/pkg/registry/cachesize" "k8s.io/kubernetes/pkg/registry/cachesize"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/registry/rbac/clusterrole"
clusterroleetcd "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/etcd"
"k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding"
clusterrolebindingetcd "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/etcd"
"k8s.io/kubernetes/pkg/registry/rbac/role"
roleetcd "k8s.io/kubernetes/pkg/registry/rbac/role/etcd"
"k8s.io/kubernetes/pkg/registry/rbac/rolebinding"
rolebindingetcd "k8s.io/kubernetes/pkg/registry/rbac/rolebinding/etcd"
"k8s.io/kubernetes/pkg/serviceaccount" "k8s.io/kubernetes/pkg/serviceaccount"
"k8s.io/kubernetes/pkg/util/wait" "k8s.io/kubernetes/pkg/util/wait"
authenticatorunion "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/union" authenticatorunion "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/union"
@ -253,48 +242,7 @@ func Run(s *options.APIServer) error {
glog.Fatalf("Invalid Authentication Config: %v", err) glog.Fatalf("Invalid Authentication Config: %v", err)
} }
authorizationModeNames := strings.Split(s.AuthorizationMode, ",")
modeEnabled := func(mode string) bool {
for _, m := range authorizationModeNames {
if m == mode {
return true
}
}
return false
}
authorizationConfig := authorizer.AuthorizationConfig{
PolicyFile: s.AuthorizationPolicyFile,
WebhookConfigFile: s.AuthorizationWebhookConfigFile,
WebhookCacheAuthorizedTTL: s.AuthorizationWebhookCacheAuthorizedTTL,
WebhookCacheUnauthorizedTTL: s.AuthorizationWebhookCacheUnauthorizedTTL,
RBACSuperUser: s.AuthorizationRBACSuperUser,
}
if modeEnabled(genericoptions.ModeRBAC) {
mustGetRESTOptions := func(resource string) generic.RESTOptions {
config, err := storageFactory.NewConfig(rbac.Resource(resource))
if err != nil {
glog.Fatalf("Unable to get %s storage: %v", resource, err)
}
return generic.RESTOptions{StorageConfig: config, Decorator: generic.UndecoratedStorage, ResourcePrefix: storageFactory.ResourcePrefix(rbac.Resource(resource))}
}
// For initial bootstrapping go directly to etcd to avoid privillege escalation check.
authorizationConfig.RBACRoleRegistry = role.NewRegistry(roleetcd.NewREST(mustGetRESTOptions("roles")))
authorizationConfig.RBACRoleBindingRegistry = rolebinding.NewRegistry(rolebindingetcd.NewREST(mustGetRESTOptions("rolebindings")))
authorizationConfig.RBACClusterRoleRegistry = clusterrole.NewRegistry(clusterroleetcd.NewREST(mustGetRESTOptions("clusterroles")))
authorizationConfig.RBACClusterRoleBindingRegistry = clusterrolebinding.NewRegistry(clusterrolebindingetcd.NewREST(mustGetRESTOptions("clusterrolebindings")))
}
apiAuthorizer, err := authorizer.NewAuthorizerFromAuthorizationConfig(authorizationModeNames, authorizationConfig)
if err != nil {
glog.Fatalf("Invalid Authorization Config: %v", err)
}
admissionControlPluginNames := strings.Split(s.AdmissionControl, ",")
privilegedLoopbackToken := uuid.NewRandom().String() privilegedLoopbackToken := uuid.NewRandom().String()
selfClientConfig, err := s.NewSelfClientConfig(privilegedLoopbackToken) selfClientConfig, err := s.NewSelfClientConfig(privilegedLoopbackToken)
if err != nil { if err != nil {
glog.Fatalf("Failed to create clientset: %v", err) glog.Fatalf("Failed to create clientset: %v", err)
@ -303,6 +251,23 @@ func Run(s *options.APIServer) error {
if err != nil { if err != nil {
glog.Errorf("Failed to create clientset: %v", err) glog.Errorf("Failed to create clientset: %v", err)
} }
sharedInformers := informers.NewSharedInformerFactory(client, 10*time.Minute)
authorizationConfig := authorizer.AuthorizationConfig{
PolicyFile: s.AuthorizationPolicyFile,
WebhookConfigFile: s.AuthorizationWebhookConfigFile,
WebhookCacheAuthorizedTTL: s.AuthorizationWebhookCacheAuthorizedTTL,
WebhookCacheUnauthorizedTTL: s.AuthorizationWebhookCacheUnauthorizedTTL,
RBACSuperUser: s.AuthorizationRBACSuperUser,
InformerFactory: sharedInformers,
}
authorizationModeNames := strings.Split(s.AuthorizationMode, ",")
apiAuthorizer, err := authorizer.NewAuthorizerFromAuthorizationConfig(authorizationModeNames, authorizationConfig)
if err != nil {
glog.Fatalf("Invalid Authorization Config: %v", err)
}
admissionControlPluginNames := strings.Split(s.AdmissionControl, ",")
// TODO(dims): We probably need to add an option "EnableLoopbackToken" // TODO(dims): We probably need to add an option "EnableLoopbackToken"
if apiAuthenticator != nil { if apiAuthenticator != nil {
@ -321,7 +286,6 @@ func Run(s *options.APIServer) error {
apiAuthorizer = authorizerunion.New(tokenAuthorizer, apiAuthorizer) apiAuthorizer = authorizerunion.New(tokenAuthorizer, apiAuthorizer)
} }
sharedInformers := informers.NewSharedInformerFactory(client, 10*time.Minute)
pluginInitializer := admission.NewPluginInitializer(sharedInformers, apiAuthorizer) pluginInitializer := admission.NewPluginInitializer(sharedInformers, apiAuthorizer)
admissionController, err := admission.NewFromPlugins(client, admissionControlPluginNames, s.AdmissionControlConfigFile, pluginInitializer) admissionController, err := admission.NewFromPlugins(client, admissionControlPluginNames, s.AdmissionControlConfigFile, pluginInitializer)

View File

@ -32,7 +32,6 @@ import (
"k8s.io/kubernetes/pkg/admission" "k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apiserver/authenticator" "k8s.io/kubernetes/pkg/apiserver/authenticator"
apiserveropenapi "k8s.io/kubernetes/pkg/apiserver/openapi" apiserveropenapi "k8s.io/kubernetes/pkg/apiserver/openapi"
authorizerunion "k8s.io/kubernetes/pkg/auth/authorizer/union" authorizerunion "k8s.io/kubernetes/pkg/auth/authorizer/union"
@ -41,19 +40,10 @@ import (
"k8s.io/kubernetes/pkg/generated/openapi" "k8s.io/kubernetes/pkg/generated/openapi"
"k8s.io/kubernetes/pkg/genericapiserver" "k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/genericapiserver/authorizer" "k8s.io/kubernetes/pkg/genericapiserver/authorizer"
genericoptions "k8s.io/kubernetes/pkg/genericapiserver/options"
genericvalidation "k8s.io/kubernetes/pkg/genericapiserver/validation" genericvalidation "k8s.io/kubernetes/pkg/genericapiserver/validation"
"k8s.io/kubernetes/pkg/registry/cachesize" "k8s.io/kubernetes/pkg/registry/cachesize"
"k8s.io/kubernetes/pkg/registry/generic" "k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/registry/generic/registry" "k8s.io/kubernetes/pkg/registry/generic/registry"
"k8s.io/kubernetes/pkg/registry/rbac/clusterrole"
clusterroleetcd "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/etcd"
"k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding"
clusterrolebindingetcd "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/etcd"
"k8s.io/kubernetes/pkg/registry/rbac/role"
roleetcd "k8s.io/kubernetes/pkg/registry/rbac/role/etcd"
"k8s.io/kubernetes/pkg/registry/rbac/rolebinding"
rolebindingetcd "k8s.io/kubernetes/pkg/registry/rbac/rolebinding/etcd"
"k8s.io/kubernetes/pkg/routes" "k8s.io/kubernetes/pkg/routes"
"k8s.io/kubernetes/pkg/util/wait" "k8s.io/kubernetes/pkg/util/wait"
authenticatorunion "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/union" authenticatorunion "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/union"
@ -143,48 +133,7 @@ func Run(s *options.ServerRunOptions) error {
glog.Fatalf("Invalid Authentication Config: %v", err) glog.Fatalf("Invalid Authentication Config: %v", err)
} }
authorizationModeNames := strings.Split(s.AuthorizationMode, ",")
modeEnabled := func(mode string) bool {
for _, m := range authorizationModeNames {
if m == mode {
return true
}
}
return false
}
authorizationConfig := authorizer.AuthorizationConfig{
PolicyFile: s.AuthorizationPolicyFile,
WebhookConfigFile: s.AuthorizationWebhookConfigFile,
WebhookCacheAuthorizedTTL: s.AuthorizationWebhookCacheAuthorizedTTL,
WebhookCacheUnauthorizedTTL: s.AuthorizationWebhookCacheUnauthorizedTTL,
RBACSuperUser: s.AuthorizationRBACSuperUser,
}
if modeEnabled(genericoptions.ModeRBAC) {
mustGetRESTOptions := func(resource string) generic.RESTOptions {
config, err := storageFactory.NewConfig(rbac.Resource(resource))
if err != nil {
glog.Fatalf("Unable to get %s storage: %v", resource, err)
}
return generic.RESTOptions{StorageConfig: config, Decorator: generic.UndecoratedStorage, ResourcePrefix: storageFactory.ResourcePrefix(rbac.Resource(resource))}
}
// For initial bootstrapping go directly to etcd to avoid privillege escalation check.
authorizationConfig.RBACRoleRegistry = role.NewRegistry(roleetcd.NewREST(mustGetRESTOptions("roles")))
authorizationConfig.RBACRoleBindingRegistry = rolebinding.NewRegistry(rolebindingetcd.NewREST(mustGetRESTOptions("rolebindings")))
authorizationConfig.RBACClusterRoleRegistry = clusterrole.NewRegistry(clusterroleetcd.NewREST(mustGetRESTOptions("clusterroles")))
authorizationConfig.RBACClusterRoleBindingRegistry = clusterrolebinding.NewRegistry(clusterrolebindingetcd.NewREST(mustGetRESTOptions("clusterrolebindings")))
}
apiAuthorizer, err := authorizer.NewAuthorizerFromAuthorizationConfig(authorizationModeNames, authorizationConfig)
if err != nil {
glog.Fatalf("Invalid Authorization Config: %v", err)
}
admissionControlPluginNames := strings.Split(s.AdmissionControl, ",")
privilegedLoopbackToken := uuid.NewRandom().String() privilegedLoopbackToken := uuid.NewRandom().String()
selfClientConfig, err := s.NewSelfClientConfig(privilegedLoopbackToken) selfClientConfig, err := s.NewSelfClientConfig(privilegedLoopbackToken)
if err != nil { if err != nil {
glog.Fatalf("Failed to create clientset: %v", err) glog.Fatalf("Failed to create clientset: %v", err)
@ -193,6 +142,23 @@ func Run(s *options.ServerRunOptions) error {
if err != nil { if err != nil {
glog.Errorf("Failed to create clientset: %v", err) glog.Errorf("Failed to create clientset: %v", err)
} }
sharedInformers := informers.NewSharedInformerFactory(client, 10*time.Minute)
authorizationConfig := authorizer.AuthorizationConfig{
PolicyFile: s.AuthorizationPolicyFile,
WebhookConfigFile: s.AuthorizationWebhookConfigFile,
WebhookCacheAuthorizedTTL: s.AuthorizationWebhookCacheAuthorizedTTL,
WebhookCacheUnauthorizedTTL: s.AuthorizationWebhookCacheUnauthorizedTTL,
RBACSuperUser: s.AuthorizationRBACSuperUser,
InformerFactory: sharedInformers,
}
authorizationModeNames := strings.Split(s.AuthorizationMode, ",")
apiAuthorizer, err := authorizer.NewAuthorizerFromAuthorizationConfig(authorizationModeNames, authorizationConfig)
if err != nil {
glog.Fatalf("Invalid Authorization Config: %v", err)
}
admissionControlPluginNames := strings.Split(s.AdmissionControl, ",")
// TODO(dims): We probably need to add an option "EnableLoopbackToken" // TODO(dims): We probably need to add an option "EnableLoopbackToken"
if apiAuthenticator != nil { if apiAuthenticator != nil {
@ -211,7 +177,6 @@ func Run(s *options.ServerRunOptions) error {
apiAuthorizer = authorizerunion.New(tokenAuthorizer, apiAuthorizer) apiAuthorizer = authorizerunion.New(tokenAuthorizer, apiAuthorizer)
} }
sharedInformers := informers.NewSharedInformerFactory(client, 10*time.Minute)
pluginInitializer := admission.NewPluginInitializer(sharedInformers, apiAuthorizer) pluginInitializer := admission.NewPluginInitializer(sharedInformers, apiAuthorizer)
admissionController, err := admission.NewFromPlugins(client, admissionControlPluginNames, s.AdmissionControlConfigFile, pluginInitializer) admissionController, err := admission.NewFromPlugins(client, admissionControlPluginNames, s.AdmissionControlConfigFile, pluginInitializer)

View File

@ -32,7 +32,7 @@ import (
type AuthorizationRuleResolver interface { type AuthorizationRuleResolver interface {
// GetRoleReferenceRules attempts to resolve the role reference of a RoleBinding or ClusterRoleBinding. The passed namespace should be the namepsace // GetRoleReferenceRules attempts to resolve the role reference of a RoleBinding or ClusterRoleBinding. The passed namespace should be the namepsace
// of the role binding, the empty string if a cluster role binding. // of the role binding, the empty string if a cluster role binding.
GetRoleReferenceRules(ctx api.Context, roleRef rbac.RoleRef, namespace string) ([]rbac.PolicyRule, error) GetRoleReferenceRules(roleRef rbac.RoleRef, namespace string) ([]rbac.PolicyRule, error)
// RulesFor returns the list of rules that apply to a given user in a given namespace and error. If an error is returned, the slice of // RulesFor returns the list of rules that apply to a given user in a given namespace and error. If an error is returned, the slice of
// PolicyRules may not be complete, but it contains all retrievable rules. This is done because policy rules are purely additive and policy determinations // PolicyRules may not be complete, but it contains all retrievable rules. This is done because policy rules are purely additive and policy determinations
@ -77,35 +77,34 @@ func NewDefaultRuleResolver(roleGetter RoleGetter, roleBindingLister RoleBinding
} }
type RoleGetter interface { type RoleGetter interface {
GetRole(ctx api.Context, id string) (*rbac.Role, error) GetRole(namespace, name string) (*rbac.Role, error)
} }
type RoleBindingLister interface { type RoleBindingLister interface {
ListRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.RoleBindingList, error) ListRoleBindings(namespace string) ([]*rbac.RoleBinding, error)
} }
type ClusterRoleGetter interface { type ClusterRoleGetter interface {
GetClusterRole(ctx api.Context, id string) (*rbac.ClusterRole, error) GetClusterRole(name string) (*rbac.ClusterRole, error)
} }
type ClusterRoleBindingLister interface { type ClusterRoleBindingLister interface {
ListClusterRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.ClusterRoleBindingList, error) ListClusterRoleBindings() ([]*rbac.ClusterRoleBinding, error)
} }
func (r *DefaultRuleResolver) RulesFor(user user.Info, namespace string) ([]rbac.PolicyRule, error) { func (r *DefaultRuleResolver) RulesFor(user user.Info, namespace string) ([]rbac.PolicyRule, error) {
policyRules := []rbac.PolicyRule{} policyRules := []rbac.PolicyRule{}
errorlist := []error{} errorlist := []error{}
ctx := api.NewContext() if clusterRoleBindings, err := r.clusterRoleBindingLister.ListClusterRoleBindings(); err != nil {
if clusterRoleBindings, err := r.clusterRoleBindingLister.ListClusterRoleBindings(ctx, &api.ListOptions{}); err != nil {
errorlist = append(errorlist, err) errorlist = append(errorlist, err)
} else { } else {
for _, clusterRoleBinding := range clusterRoleBindings.Items { for _, clusterRoleBinding := range clusterRoleBindings {
if !appliesTo(user, clusterRoleBinding.Subjects, "") { if !appliesTo(user, clusterRoleBinding.Subjects, "") {
continue continue
} }
rules, err := r.GetRoleReferenceRules(ctx, clusterRoleBinding.RoleRef, "") rules, err := r.GetRoleReferenceRules(clusterRoleBinding.RoleRef, "")
if err != nil { if err != nil {
errorlist = append(errorlist, err) errorlist = append(errorlist, err)
continue continue
@ -115,17 +114,15 @@ func (r *DefaultRuleResolver) RulesFor(user user.Info, namespace string) ([]rbac
} }
if len(namespace) > 0 { if len(namespace) > 0 {
ctx := api.WithNamespace(api.NewContext(), namespace) if roleBindings, err := r.roleBindingLister.ListRoleBindings(namespace); err != nil {
if roleBindings, err := r.roleBindingLister.ListRoleBindings(ctx, &api.ListOptions{}); err != nil {
errorlist = append(errorlist, err) errorlist = append(errorlist, err)
} else { } else {
for _, roleBinding := range roleBindings.Items { for _, roleBinding := range roleBindings {
if !appliesTo(user, roleBinding.Subjects, namespace) { if !appliesTo(user, roleBinding.Subjects, namespace) {
continue continue
} }
rules, err := r.GetRoleReferenceRules(ctx, roleBinding.RoleRef, namespace) rules, err := r.GetRoleReferenceRules(roleBinding.RoleRef, namespace)
if err != nil { if err != nil {
errorlist = append(errorlist, err) errorlist = append(errorlist, err)
continue continue
@ -139,17 +136,17 @@ func (r *DefaultRuleResolver) RulesFor(user user.Info, namespace string) ([]rbac
} }
// GetRoleReferenceRules attempts to resolve the RoleBinding or ClusterRoleBinding. // GetRoleReferenceRules attempts to resolve the RoleBinding or ClusterRoleBinding.
func (r *DefaultRuleResolver) GetRoleReferenceRules(ctx api.Context, roleRef rbac.RoleRef, bindingNamespace string) ([]rbac.PolicyRule, error) { func (r *DefaultRuleResolver) GetRoleReferenceRules(roleRef rbac.RoleRef, bindingNamespace string) ([]rbac.PolicyRule, error) {
switch kind := rbac.RoleRefGroupKind(roleRef); kind { switch kind := rbac.RoleRefGroupKind(roleRef); kind {
case rbac.Kind("Role"): case rbac.Kind("Role"):
role, err := r.roleGetter.GetRole(api.WithNamespace(ctx, bindingNamespace), roleRef.Name) role, err := r.roleGetter.GetRole(bindingNamespace, roleRef.Name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return role.Rules, nil return role.Rules, nil
case rbac.Kind("ClusterRole"): case rbac.Kind("ClusterRole"):
clusterRole, err := r.clusterRoleGetter.GetClusterRole(api.WithNamespace(ctx, ""), roleRef.Name) clusterRole, err := r.clusterRoleGetter.GetClusterRole(roleRef.Name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -193,7 +190,7 @@ func appliesToUser(user user.Info, subject rbac.Subject, namespace string) bool
} }
// NewTestRuleResolver returns a rule resolver from lists of role objects. // NewTestRuleResolver returns a rule resolver from lists of role objects.
func NewTestRuleResolver(roles []rbac.Role, roleBindings []rbac.RoleBinding, clusterRoles []rbac.ClusterRole, clusterRoleBindings []rbac.ClusterRoleBinding) AuthorizationRuleResolver { func NewTestRuleResolver(roles []*rbac.Role, roleBindings []*rbac.RoleBinding, clusterRoles []*rbac.ClusterRole, clusterRoleBindings []*rbac.ClusterRoleBinding) AuthorizationRuleResolver {
r := staticRoles{ r := staticRoles{
roles: roles, roles: roles,
roleBindings: roleBindings, roleBindings: roleBindings,
@ -208,62 +205,49 @@ func newMockRuleResolver(r *staticRoles) AuthorizationRuleResolver {
} }
type staticRoles struct { type staticRoles struct {
roles []rbac.Role roles []*rbac.Role
roleBindings []rbac.RoleBinding roleBindings []*rbac.RoleBinding
clusterRoles []rbac.ClusterRole clusterRoles []*rbac.ClusterRole
clusterRoleBindings []rbac.ClusterRoleBinding clusterRoleBindings []*rbac.ClusterRoleBinding
} }
func (r *staticRoles) GetRole(ctx api.Context, id string) (*rbac.Role, error) { func (r *staticRoles) GetRole(namespace, name string) (*rbac.Role, error) {
namespace, ok := api.NamespaceFrom(ctx) if len(namespace) == 0 {
if !ok || namespace == "" {
return nil, errors.New("must provide namespace when getting role") return nil, errors.New("must provide namespace when getting role")
} }
for _, role := range r.roles { for _, role := range r.roles {
if role.Namespace == namespace && role.Name == id { if role.Namespace == namespace && role.Name == name {
return &role, nil return role, nil
} }
} }
return nil, errors.New("role not found") return nil, errors.New("role not found")
} }
func (r *staticRoles) GetClusterRole(ctx api.Context, id string) (*rbac.ClusterRole, error) { func (r *staticRoles) GetClusterRole(name string) (*rbac.ClusterRole, error) {
namespace, ok := api.NamespaceFrom(ctx)
if ok && namespace != "" {
return nil, errors.New("cannot provide namespace when getting cluster role")
}
for _, clusterRole := range r.clusterRoles { for _, clusterRole := range r.clusterRoles {
if clusterRole.Namespace == namespace && clusterRole.Name == id { if clusterRole.Name == name {
return &clusterRole, nil return clusterRole, nil
} }
} }
return nil, errors.New("role not found") return nil, errors.New("role not found")
} }
func (r *staticRoles) ListRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.RoleBindingList, error) { func (r *staticRoles) ListRoleBindings(namespace string) ([]*rbac.RoleBinding, error) {
namespace, ok := api.NamespaceFrom(ctx) if len(namespace) == 0 {
if !ok || namespace == "" {
return nil, errors.New("must provide namespace when listing role bindings") return nil, errors.New("must provide namespace when listing role bindings")
} }
roleBindingList := new(rbac.RoleBindingList) roleBindingList := []*rbac.RoleBinding{}
for _, roleBinding := range r.roleBindings { for _, roleBinding := range r.roleBindings {
if roleBinding.Namespace != namespace { if roleBinding.Namespace != namespace {
continue continue
} }
// TODO(ericchiang): need to implement label selectors? // TODO(ericchiang): need to implement label selectors?
roleBindingList.Items = append(roleBindingList.Items, roleBinding) roleBindingList = append(roleBindingList, roleBinding)
} }
return roleBindingList, nil return roleBindingList, nil
} }
func (r *staticRoles) ListClusterRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.ClusterRoleBindingList, error) { func (r *staticRoles) ListClusterRoleBindings() ([]*rbac.ClusterRoleBinding, error) {
namespace, ok := api.NamespaceFrom(ctx) return r.clusterRoleBindings, nil
if ok && namespace != "" {
return nil, errors.New("cannot list cluster role bindings from within a namespace")
}
clusterRoleBindings := new(rbac.ClusterRoleBindingList)
clusterRoleBindings.Items = make([]rbac.ClusterRoleBinding, len(r.clusterRoleBindings))
copy(clusterRoleBindings.Items, r.clusterRoleBindings)
return clusterRoleBindings, nil
} }

View File

@ -73,13 +73,13 @@ func TestDefaultRuleResolver(t *testing.T) {
} }
staticRoles1 := staticRoles{ staticRoles1 := staticRoles{
roles: []rbac.Role{ roles: []*rbac.Role{
{ {
ObjectMeta: api.ObjectMeta{Namespace: "namespace1", Name: "readthings"}, ObjectMeta: api.ObjectMeta{Namespace: "namespace1", Name: "readthings"},
Rules: []rbac.PolicyRule{ruleReadPods, ruleReadServices}, Rules: []rbac.PolicyRule{ruleReadPods, ruleReadServices},
}, },
}, },
clusterRoles: []rbac.ClusterRole{ clusterRoles: []*rbac.ClusterRole{
{ {
ObjectMeta: api.ObjectMeta{Name: "cluster-admin"}, ObjectMeta: api.ObjectMeta{Name: "cluster-admin"},
Rules: []rbac.PolicyRule{ruleAdmin}, Rules: []rbac.PolicyRule{ruleAdmin},
@ -89,7 +89,7 @@ func TestDefaultRuleResolver(t *testing.T) {
Rules: []rbac.PolicyRule{ruleWriteNodes}, Rules: []rbac.PolicyRule{ruleWriteNodes},
}, },
}, },
roleBindings: []rbac.RoleBinding{ roleBindings: []*rbac.RoleBinding{
{ {
ObjectMeta: api.ObjectMeta{Namespace: "namespace1"}, ObjectMeta: api.ObjectMeta{Namespace: "namespace1"},
Subjects: []rbac.Subject{ Subjects: []rbac.Subject{
@ -99,7 +99,7 @@ func TestDefaultRuleResolver(t *testing.T) {
RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "readthings"}, RoleRef: rbac.RoleRef{APIGroup: rbac.GroupName, Kind: "Role", Name: "readthings"},
}, },
}, },
clusterRoleBindings: []rbac.ClusterRoleBinding{ clusterRoleBindings: []*rbac.ClusterRoleBinding{
{ {
Subjects: []rbac.Subject{ Subjects: []rbac.Subject{
{Kind: rbac.UserKind, Name: "admin"}, {Kind: rbac.UserKind, Name: "admin"},

234
pkg/client/cache/listers_rbac.go vendored Normal file
View File

@ -0,0 +1,234 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cache
import (
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/labels"
)
// TODO: generate these classes and methods for all resources of interest using
// a script. Can use "go generate" once 1.4 is supported by all users.
// Lister makes an Index have the List method. The Stores must contain only the expected type
// Example:
// s := cache.NewStore()
// lw := cache.ListWatch{Client: c, FieldSelector: sel, Resource: "pods"}
// r := cache.NewReflector(lw, &rbac.ClusterRole{}, s).Run()
// l := clusterRoleLister{s}
// l.List()
func NewClusterRoleLister(indexer Indexer) ClusterRoleLister {
return &clusterRoleLister{indexer: indexer}
}
func NewClusterRoleBindingLister(indexer Indexer) ClusterRoleBindingLister {
return &clusterRoleBindingLister{indexer: indexer}
}
func NewRoleLister(indexer Indexer) RoleLister {
return &roleLister{indexer: indexer}
}
func NewRoleBindingLister(indexer Indexer) RoleBindingLister {
return &roleBindingLister{indexer: indexer}
}
// these interfaces are used by the rbac authorizer
type authorizerClusterRoleGetter interface {
GetClusterRole(name string) (*rbac.ClusterRole, error)
}
type authorizerClusterRoleBindingLister interface {
ListClusterRoleBindings() ([]*rbac.ClusterRoleBinding, error)
}
type authorizerRoleGetter interface {
GetRole(namespace, name string) (*rbac.Role, error)
}
type authorizerRoleBindingLister interface {
ListRoleBindings(namespace string) ([]*rbac.RoleBinding, error)
}
type ClusterRoleLister interface {
authorizerClusterRoleGetter
List(selector labels.Selector) (ret []*rbac.ClusterRole, err error)
Get(name string) (*rbac.ClusterRole, error)
}
type clusterRoleLister struct {
indexer Indexer
}
func (s *clusterRoleLister) List(selector labels.Selector) (ret []*rbac.ClusterRole, err error) {
err = ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*rbac.ClusterRole))
})
return ret, err
}
func (s clusterRoleLister) Get(name string) (*rbac.ClusterRole, error) {
obj, exists, err := s.indexer.GetByKey(name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(rbac.Resource("clusterrole"), name)
}
return obj.(*rbac.ClusterRole), nil
}
func (s clusterRoleLister) GetClusterRole(name string) (*rbac.ClusterRole, error) {
return s.Get(name)
}
type ClusterRoleBindingLister interface {
authorizerClusterRoleBindingLister
List(selector labels.Selector) (ret []*rbac.ClusterRoleBinding, err error)
Get(name string) (*rbac.ClusterRoleBinding, error)
}
type clusterRoleBindingLister struct {
indexer Indexer
}
func (s *clusterRoleBindingLister) List(selector labels.Selector) (ret []*rbac.ClusterRoleBinding, err error) {
err = ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*rbac.ClusterRoleBinding))
})
return ret, err
}
func (s clusterRoleBindingLister) Get(name string) (*rbac.ClusterRoleBinding, error) {
obj, exists, err := s.indexer.GetByKey(name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(rbac.Resource("clusterrolebinding"), name)
}
return obj.(*rbac.ClusterRoleBinding), nil
}
func (s clusterRoleBindingLister) ListClusterRoleBindings() ([]*rbac.ClusterRoleBinding, error) {
return s.List(labels.Everything())
}
type RoleLister interface {
authorizerRoleGetter
List(selector labels.Selector) (ret []*rbac.Role, err error)
Roles(namespace string) RoleNamespaceLister
}
type RoleNamespaceLister interface {
List(selector labels.Selector) (ret []*rbac.Role, err error)
Get(name string) (*rbac.Role, error)
}
type roleLister struct {
indexer Indexer
}
func (s *roleLister) List(selector labels.Selector) (ret []*rbac.Role, err error) {
err = ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*rbac.Role))
})
return ret, err
}
func (s *roleLister) Roles(namespace string) RoleNamespaceLister {
return roleNamespaceLister{indexer: s.indexer, namespace: namespace}
}
func (s roleLister) GetRole(namespace, name string) (*rbac.Role, error) {
return s.Roles(namespace).Get(name)
}
type roleNamespaceLister struct {
indexer Indexer
namespace string
}
func (s roleNamespaceLister) List(selector labels.Selector) (ret []*rbac.Role, err error) {
err = ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*rbac.Role))
})
return ret, err
}
func (s roleNamespaceLister) Get(name string) (*rbac.Role, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(rbac.Resource("role"), name)
}
return obj.(*rbac.Role), nil
}
type RoleBindingLister interface {
authorizerRoleBindingLister
List(selector labels.Selector) (ret []*rbac.RoleBinding, err error)
RoleBindings(namespace string) RoleBindingNamespaceLister
}
type RoleBindingNamespaceLister interface {
List(selector labels.Selector) (ret []*rbac.RoleBinding, err error)
Get(name string) (*rbac.RoleBinding, error)
}
type roleBindingLister struct {
indexer Indexer
}
func (s *roleBindingLister) List(selector labels.Selector) (ret []*rbac.RoleBinding, err error) {
err = ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*rbac.RoleBinding))
})
return ret, err
}
func (s *roleBindingLister) RoleBindings(namespace string) RoleBindingNamespaceLister {
return roleBindingNamespaceLister{indexer: s.indexer, namespace: namespace}
}
func (s roleBindingLister) ListRoleBindings(namespace string) ([]*rbac.RoleBinding, error) {
return s.RoleBindings(namespace).List(labels.Everything())
}
type roleBindingNamespaceLister struct {
indexer Indexer
namespace string
}
func (s roleBindingNamespaceLister) List(selector labels.Selector) (ret []*rbac.RoleBinding, err error) {
err = ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*rbac.RoleBinding))
})
return ret, err
}
func (s roleBindingNamespaceLister) Get(name string) (*rbac.RoleBinding, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(rbac.Resource("rolebinding"), name)
}
return obj.(*rbac.RoleBinding), nil
}

View File

@ -40,6 +40,11 @@ type SharedInformerFactory interface {
DaemonSets() DaemonSetInformer DaemonSets() DaemonSetInformer
Deployments() DeploymentInformer Deployments() DeploymentInformer
ReplicaSets() ReplicaSetInformer ReplicaSets() ReplicaSetInformer
ClusterRoles() ClusterRoleInformer
ClusterRoleBindings() ClusterRoleBindingInformer
Roles() RoleInformer
RoleBindings() RoleBindingInformer
} }
type sharedInformerFactory struct { type sharedInformerFactory struct {
@ -112,3 +117,19 @@ func (f *sharedInformerFactory) Deployments() DeploymentInformer {
func (f *sharedInformerFactory) ReplicaSets() ReplicaSetInformer { func (f *sharedInformerFactory) ReplicaSets() ReplicaSetInformer {
return &replicaSetInformer{sharedInformerFactory: f} return &replicaSetInformer{sharedInformerFactory: f}
} }
func (f *sharedInformerFactory) ClusterRoles() ClusterRoleInformer {
return &clusterRoleInformer{sharedInformerFactory: f}
}
func (f *sharedInformerFactory) ClusterRoleBindings() ClusterRoleBindingInformer {
return &clusterRoleBindingInformer{sharedInformerFactory: f}
}
func (f *sharedInformerFactory) Roles() RoleInformer {
return &roleInformer{sharedInformerFactory: f}
}
func (f *sharedInformerFactory) RoleBindings() RoleBindingInformer {
return &roleBindingInformer{sharedInformerFactory: f}
}

View File

@ -0,0 +1,187 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package informers
import (
"reflect"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/client/cache"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/watch"
)
type ClusterRoleInformer interface {
Informer() cache.SharedIndexInformer
Lister() cache.ClusterRoleLister
}
type clusterRoleInformer struct {
*sharedInformerFactory
}
func (f *clusterRoleInformer) Informer() cache.SharedIndexInformer {
f.lock.Lock()
defer f.lock.Unlock()
informerType := reflect.TypeOf(&rbac.ClusterRole{})
informer, exists := f.informers[informerType]
if exists {
return informer
}
informer = cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options api.ListOptions) (runtime.Object, error) {
return f.client.Rbac().ClusterRoles().List(options)
},
WatchFunc: func(options api.ListOptions) (watch.Interface, error) {
return f.client.Rbac().ClusterRoles().Watch(options)
},
},
&rbac.ClusterRole{},
f.defaultResync,
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
)
f.informers[informerType] = informer
return informer
}
func (f *clusterRoleInformer) Lister() cache.ClusterRoleLister {
return cache.NewClusterRoleLister(f.Informer().GetIndexer())
}
type ClusterRoleBindingInformer interface {
Informer() cache.SharedIndexInformer
Lister() cache.ClusterRoleBindingLister
}
type clusterRoleBindingInformer struct {
*sharedInformerFactory
}
func (f *clusterRoleBindingInformer) Informer() cache.SharedIndexInformer {
f.lock.Lock()
defer f.lock.Unlock()
informerType := reflect.TypeOf(&rbac.ClusterRoleBinding{})
informer, exists := f.informers[informerType]
if exists {
return informer
}
informer = cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options api.ListOptions) (runtime.Object, error) {
return f.client.Rbac().ClusterRoleBindings().List(options)
},
WatchFunc: func(options api.ListOptions) (watch.Interface, error) {
return f.client.Rbac().ClusterRoleBindings().Watch(options)
},
},
&rbac.ClusterRoleBinding{},
f.defaultResync,
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
)
f.informers[informerType] = informer
return informer
}
func (f *clusterRoleBindingInformer) Lister() cache.ClusterRoleBindingLister {
return cache.NewClusterRoleBindingLister(f.Informer().GetIndexer())
}
type RoleInformer interface {
Informer() cache.SharedIndexInformer
Lister() cache.RoleLister
}
type roleInformer struct {
*sharedInformerFactory
}
func (f *roleInformer) Informer() cache.SharedIndexInformer {
f.lock.Lock()
defer f.lock.Unlock()
informerType := reflect.TypeOf(&rbac.Role{})
informer, exists := f.informers[informerType]
if exists {
return informer
}
informer = cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options api.ListOptions) (runtime.Object, error) {
return f.client.Rbac().Roles(api.NamespaceAll).List(options)
},
WatchFunc: func(options api.ListOptions) (watch.Interface, error) {
return f.client.Rbac().Roles(api.NamespaceAll).Watch(options)
},
},
&rbac.Role{},
f.defaultResync,
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
)
f.informers[informerType] = informer
return informer
}
func (f *roleInformer) Lister() cache.RoleLister {
return cache.NewRoleLister(f.Informer().GetIndexer())
}
type RoleBindingInformer interface {
Informer() cache.SharedIndexInformer
Lister() cache.RoleBindingLister
}
type roleBindingInformer struct {
*sharedInformerFactory
}
func (f *roleBindingInformer) Informer() cache.SharedIndexInformer {
f.lock.Lock()
defer f.lock.Unlock()
informerType := reflect.TypeOf(&rbac.RoleBinding{})
informer, exists := f.informers[informerType]
if exists {
return informer
}
informer = cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options api.ListOptions) (runtime.Object, error) {
return f.client.Rbac().RoleBindings(api.NamespaceAll).List(options)
},
WatchFunc: func(options api.ListOptions) (watch.Interface, error) {
return f.client.Rbac().RoleBindings(api.NamespaceAll).Watch(options)
},
},
&rbac.RoleBinding{},
f.defaultResync,
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
)
f.informers[informerType] = informer
return informer
}
func (f *roleBindingInformer) Lister() cache.RoleBindingLister {
return cache.NewRoleBindingLister(f.Informer().GetIndexer())
}

View File

@ -24,11 +24,8 @@ import (
"k8s.io/kubernetes/pkg/auth/authorizer" "k8s.io/kubernetes/pkg/auth/authorizer"
"k8s.io/kubernetes/pkg/auth/authorizer/abac" "k8s.io/kubernetes/pkg/auth/authorizer/abac"
"k8s.io/kubernetes/pkg/auth/authorizer/union" "k8s.io/kubernetes/pkg/auth/authorizer/union"
"k8s.io/kubernetes/pkg/controller/informers"
"k8s.io/kubernetes/pkg/genericapiserver/options" "k8s.io/kubernetes/pkg/genericapiserver/options"
"k8s.io/kubernetes/pkg/registry/rbac/clusterrole"
"k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding"
"k8s.io/kubernetes/pkg/registry/rbac/role"
"k8s.io/kubernetes/pkg/registry/rbac/rolebinding"
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac" "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/webhook" "k8s.io/kubernetes/plugin/pkg/auth/authorizer/webhook"
) )
@ -117,10 +114,7 @@ type AuthorizationConfig struct {
// User which can bootstrap role policies // User which can bootstrap role policies
RBACSuperUser string RBACSuperUser string
RBACClusterRoleRegistry clusterrole.Registry InformerFactory informers.SharedInformerFactory
RBACClusterRoleBindingRegistry clusterrolebinding.Registry
RBACRoleRegistry role.Registry
RBACRoleBindingRegistry rolebinding.Registry
} }
// NewAuthorizerFromAuthorizationConfig returns the right sort of union of multiple authorizer.Authorizer objects // NewAuthorizerFromAuthorizationConfig returns the right sort of union of multiple authorizer.Authorizer objects
@ -167,10 +161,10 @@ func NewAuthorizerFromAuthorizationConfig(authorizationModes []string, config Au
authorizers = append(authorizers, webhookAuthorizer) authorizers = append(authorizers, webhookAuthorizer)
case options.ModeRBAC: case options.ModeRBAC:
rbacAuthorizer := rbac.New( rbacAuthorizer := rbac.New(
config.RBACRoleRegistry, config.InformerFactory.Roles().Lister(),
config.RBACRoleBindingRegistry, config.InformerFactory.RoleBindings().Lister(),
config.RBACClusterRoleRegistry, config.InformerFactory.ClusterRoles().Lister(),
config.RBACClusterRoleBindingRegistry, config.InformerFactory.ClusterRoleBindings().Lister(),
config.RBACSuperUser, config.RBACSuperUser,
) )
authorizers = append(authorizers, rbacAuthorizer) authorizers = append(authorizers, rbacAuthorizer)

View File

@ -79,3 +79,12 @@ func (s *storage) DeleteClusterRole(ctx api.Context, name string) error {
_, err := s.Delete(ctx, name, nil) _, err := s.Delete(ctx, name, nil)
return err return err
} }
// AuthorizerAdapter adapts the registry to the authorizer interface
type AuthorizerAdapter struct {
Registry Registry
}
func (a AuthorizerAdapter) GetClusterRole(name string) (*rbac.ClusterRole, error) {
return a.Registry.GetClusterRole(api.NewContext(), name)
}

View File

@ -48,7 +48,7 @@ func (s *Storage) Create(ctx api.Context, obj runtime.Object) (runtime.Object, e
} }
clusterRoleBinding := obj.(*rbac.ClusterRoleBinding) clusterRoleBinding := obj.(*rbac.ClusterRoleBinding)
rules, err := s.ruleResolver.GetRoleReferenceRules(ctx, clusterRoleBinding.RoleRef, clusterRoleBinding.Namespace) rules, err := s.ruleResolver.GetRoleReferenceRules(clusterRoleBinding.RoleRef, clusterRoleBinding.Namespace)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -66,7 +66,7 @@ func (s *Storage) Update(ctx api.Context, name string, obj rest.UpdatedObjectInf
nonEscalatingInfo := wrapUpdatedObjectInfo(obj, func(ctx api.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) { nonEscalatingInfo := wrapUpdatedObjectInfo(obj, func(ctx api.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
clusterRoleBinding := obj.(*rbac.ClusterRoleBinding) clusterRoleBinding := obj.(*rbac.ClusterRoleBinding)
rules, err := s.ruleResolver.GetRoleReferenceRules(ctx, clusterRoleBinding.RoleRef, clusterRoleBinding.Namespace) rules, err := s.ruleResolver.GetRoleReferenceRules(clusterRoleBinding.RoleRef, clusterRoleBinding.Namespace)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -79,3 +79,21 @@ func (s *storage) DeleteClusterRoleBinding(ctx api.Context, name string) error {
_, err := s.Delete(ctx, name, nil) _, err := s.Delete(ctx, name, nil)
return err return err
} }
// AuthorizerAdapter adapts the registry to the authorizer interface
type AuthorizerAdapter struct {
Registry Registry
}
func (a AuthorizerAdapter) ListClusterRoleBindings() ([]*rbac.ClusterRoleBinding, error) {
list, err := a.Registry.ListClusterRoleBindings(api.NewContext(), &api.ListOptions{})
if err != nil {
return nil, err
}
ret := []*rbac.ClusterRoleBinding{}
for i := range list.Items {
ret = append(ret, &list.Items[i])
}
return ret, nil
}

View File

@ -71,10 +71,10 @@ func (p *RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource genericapi
newRuleValidator := func() rbacvalidation.AuthorizationRuleResolver { newRuleValidator := func() rbacvalidation.AuthorizationRuleResolver {
once.Do(func() { once.Do(func() {
authorizationRuleResolver = rbacvalidation.NewDefaultRuleResolver( authorizationRuleResolver = rbacvalidation.NewDefaultRuleResolver(
role.NewRegistry(roleetcd.NewREST(restOptionsGetter(rbac.Resource("roles")))), role.AuthorizerAdapter{Registry: role.NewRegistry(roleetcd.NewREST(restOptionsGetter(rbac.Resource("roles"))))},
rolebinding.NewRegistry(rolebindingetcd.NewREST(restOptionsGetter(rbac.Resource("rolebindings")))), rolebinding.AuthorizerAdapter{Registry: rolebinding.NewRegistry(rolebindingetcd.NewREST(restOptionsGetter(rbac.Resource("rolebindings"))))},
clusterrole.NewRegistry(clusterroleetcd.NewREST(restOptionsGetter(rbac.Resource("clusterroles")))), clusterrole.AuthorizerAdapter{Registry: clusterrole.NewRegistry(clusterroleetcd.NewREST(restOptionsGetter(rbac.Resource("clusterroles"))))},
clusterrolebinding.NewRegistry(clusterrolebindingetcd.NewREST(restOptionsGetter(rbac.Resource("clusterrolebindings")))), clusterrolebinding.AuthorizerAdapter{Registry: clusterrolebinding.NewRegistry(clusterrolebindingetcd.NewREST(restOptionsGetter(rbac.Resource("clusterrolebindings"))))},
) )
}) })
return authorizationRuleResolver return authorizationRuleResolver

View File

@ -79,3 +79,12 @@ func (s *storage) DeleteRole(ctx api.Context, name string) error {
_, err := s.Delete(ctx, name, nil) _, err := s.Delete(ctx, name, nil)
return err return err
} }
// AuthorizerAdapter adapts the registry to the authorizer interface
type AuthorizerAdapter struct {
Registry Registry
}
func (a AuthorizerAdapter) GetRole(namespace, name string) (*rbac.Role, error) {
return a.Registry.GetRole(api.WithNamespace(api.NewContext(), namespace), name)
}

View File

@ -48,7 +48,7 @@ func (s *Storage) Create(ctx api.Context, obj runtime.Object) (runtime.Object, e
} }
roleBinding := obj.(*rbac.RoleBinding) roleBinding := obj.(*rbac.RoleBinding)
rules, err := s.ruleResolver.GetRoleReferenceRules(ctx, roleBinding.RoleRef, roleBinding.Namespace) rules, err := s.ruleResolver.GetRoleReferenceRules(roleBinding.RoleRef, roleBinding.Namespace)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -66,7 +66,7 @@ func (s *Storage) Update(ctx api.Context, name string, obj rest.UpdatedObjectInf
nonEscalatingInfo := wrapUpdatedObjectInfo(obj, func(ctx api.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) { nonEscalatingInfo := wrapUpdatedObjectInfo(obj, func(ctx api.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
roleBinding := obj.(*rbac.RoleBinding) roleBinding := obj.(*rbac.RoleBinding)
rules, err := s.ruleResolver.GetRoleReferenceRules(ctx, roleBinding.RoleRef, roleBinding.Namespace) rules, err := s.ruleResolver.GetRoleReferenceRules(roleBinding.RoleRef, roleBinding.Namespace)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -80,3 +80,21 @@ func (s *storage) DeleteRoleBinding(ctx api.Context, name string) error {
_, err := s.Delete(ctx, name, nil) _, err := s.Delete(ctx, name, nil)
return err return err
} }
// AuthorizerAdapter adapts the registry to the authorizer interface
type AuthorizerAdapter struct {
Registry Registry
}
func (a AuthorizerAdapter) ListRoleBindings(namespace string) ([]*rbac.RoleBinding, error) {
list, err := a.Registry.ListRoleBindings(api.WithNamespace(api.NewContext(), namespace), &api.ListOptions{})
if err != nil {
return nil, err
}
ret := []*rbac.RoleBinding{}
for i := range list.Items {
ret = append(ret, &list.Items[i])
}
return ret, nil
}

View File

@ -22,10 +22,6 @@ import (
"k8s.io/kubernetes/pkg/apis/rbac/validation" "k8s.io/kubernetes/pkg/apis/rbac/validation"
"k8s.io/kubernetes/pkg/auth/authorizer" "k8s.io/kubernetes/pkg/auth/authorizer"
"k8s.io/kubernetes/pkg/auth/user" "k8s.io/kubernetes/pkg/auth/user"
"k8s.io/kubernetes/pkg/registry/rbac/clusterrole"
"k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding"
"k8s.io/kubernetes/pkg/registry/rbac/role"
"k8s.io/kubernetes/pkg/registry/rbac/rolebinding"
) )
type RequestToRuleMapper interface { type RequestToRuleMapper interface {
@ -55,14 +51,11 @@ func (r *RBACAuthorizer) Authorize(requestAttributes authorizer.Attributes) (boo
return false, "", ruleResolutionError return false, "", ruleResolutionError
} }
func New(roleRegistry role.Registry, roleBindingRegistry rolebinding.Registry, clusterRoleRegistry clusterrole.Registry, clusterRoleBindingRegistry clusterrolebinding.Registry, superUser string) *RBACAuthorizer { func New(roles validation.RoleGetter, roleBindings validation.RoleBindingLister, clusterRoles validation.ClusterRoleGetter, clusterRoleBindings validation.ClusterRoleBindingLister, superUser string) *RBACAuthorizer {
authorizer := &RBACAuthorizer{ authorizer := &RBACAuthorizer{
superUser: superUser, superUser: superUser,
authorizationRuleResolver: validation.NewDefaultRuleResolver( authorizationRuleResolver: validation.NewDefaultRuleResolver(
roleRegistry, roles, roleBindings, clusterRoles, clusterRoleBindings,
roleBindingRegistry,
clusterRoleRegistry,
clusterRoleBindingRegistry,
), ),
} }
return authorizer return authorizer

View File

@ -37,12 +37,12 @@ func newRule(verbs, apiGroups, resources, nonResourceURLs string) rbac.PolicyRul
} }
} }
func newRole(name, namespace string, rules ...rbac.PolicyRule) rbac.Role { func newRole(name, namespace string, rules ...rbac.PolicyRule) *rbac.Role {
return rbac.Role{ObjectMeta: api.ObjectMeta{Namespace: namespace, Name: name}, Rules: rules} return &rbac.Role{ObjectMeta: api.ObjectMeta{Namespace: namespace, Name: name}, Rules: rules}
} }
func newClusterRole(name string, rules ...rbac.PolicyRule) rbac.ClusterRole { func newClusterRole(name string, rules ...rbac.PolicyRule) *rbac.ClusterRole {
return rbac.ClusterRole{ObjectMeta: api.ObjectMeta{Name: name}, Rules: rules} return &rbac.ClusterRole{ObjectMeta: api.ObjectMeta{Name: name}, Rules: rules}
} }
const ( const (
@ -50,8 +50,8 @@ const (
bindToClusterRole uint16 = 0x1 bindToClusterRole uint16 = 0x1
) )
func newClusterRoleBinding(roleName string, subjects ...string) rbac.ClusterRoleBinding { func newClusterRoleBinding(roleName string, subjects ...string) *rbac.ClusterRoleBinding {
r := rbac.ClusterRoleBinding{ r := &rbac.ClusterRoleBinding{
ObjectMeta: api.ObjectMeta{}, ObjectMeta: api.ObjectMeta{},
RoleRef: rbac.RoleRef{ RoleRef: rbac.RoleRef{
APIGroup: rbac.GroupName, APIGroup: rbac.GroupName,
@ -68,8 +68,8 @@ func newClusterRoleBinding(roleName string, subjects ...string) rbac.ClusterRole
return r return r
} }
func newRoleBinding(namespace, roleName string, bindType uint16, subjects ...string) rbac.RoleBinding { func newRoleBinding(namespace, roleName string, bindType uint16, subjects ...string) *rbac.RoleBinding {
r := rbac.RoleBinding{ObjectMeta: api.ObjectMeta{Namespace: namespace}} r := &rbac.RoleBinding{ObjectMeta: api.ObjectMeta{Namespace: namespace}}
switch bindType { switch bindType {
case bindToRole: case bindToRole:
@ -117,10 +117,10 @@ func (d *defaultAttributes) GetPath() string { return "" }
func TestAuthorizer(t *testing.T) { func TestAuthorizer(t *testing.T) {
tests := []struct { tests := []struct {
roles []rbac.Role roles []*rbac.Role
roleBindings []rbac.RoleBinding roleBindings []*rbac.RoleBinding
clusterRoles []rbac.ClusterRole clusterRoles []*rbac.ClusterRole
clusterRoleBindings []rbac.ClusterRoleBinding clusterRoleBindings []*rbac.ClusterRoleBinding
superUser string superUser string
@ -128,10 +128,10 @@ func TestAuthorizer(t *testing.T) {
shouldFail []authorizer.Attributes shouldFail []authorizer.Attributes
}{ }{
{ {
clusterRoles: []rbac.ClusterRole{ clusterRoles: []*rbac.ClusterRole{
newClusterRole("admin", newRule("*", "*", "*", "*")), newClusterRole("admin", newRule("*", "*", "*", "*")),
}, },
roleBindings: []rbac.RoleBinding{ roleBindings: []*rbac.RoleBinding{
newRoleBinding("ns1", "admin", bindToClusterRole, "User:admin", "Group:admins"), newRoleBinding("ns1", "admin", bindToClusterRole, "User:admin", "Group:admins"),
}, },
shouldPass: []authorizer.Attributes{ shouldPass: []authorizer.Attributes{
@ -150,12 +150,12 @@ func TestAuthorizer(t *testing.T) {
}, },
{ {
// Non-resource-url tests // Non-resource-url tests
clusterRoles: []rbac.ClusterRole{ clusterRoles: []*rbac.ClusterRole{
newClusterRole("non-resource-url-getter", newRule("get", "", "", "/apis")), newClusterRole("non-resource-url-getter", newRule("get", "", "", "/apis")),
newClusterRole("non-resource-url", newRule("*", "", "", "/apis")), newClusterRole("non-resource-url", newRule("*", "", "", "/apis")),
newClusterRole("non-resource-url-prefix", newRule("get", "", "", "/apis/*")), newClusterRole("non-resource-url-prefix", newRule("get", "", "", "/apis/*")),
}, },
clusterRoleBindings: []rbac.ClusterRoleBinding{ clusterRoleBindings: []*rbac.ClusterRoleBinding{
newClusterRoleBinding("non-resource-url-getter", "User:foo", "Group:bar"), newClusterRoleBinding("non-resource-url-getter", "User:foo", "Group:bar"),
newClusterRoleBinding("non-resource-url", "User:admin", "Group:admin"), newClusterRoleBinding("non-resource-url", "User:admin", "Group:admin"),
newClusterRoleBinding("non-resource-url-prefix", "User:prefixed", "Group:prefixed"), newClusterRoleBinding("non-resource-url-prefix", "User:prefixed", "Group:prefixed"),
@ -191,10 +191,10 @@ func TestAuthorizer(t *testing.T) {
}, },
{ {
// test subresource resolution // test subresource resolution
clusterRoles: []rbac.ClusterRole{ clusterRoles: []*rbac.ClusterRole{
newClusterRole("admin", newRule("*", "*", "pods", "*")), newClusterRole("admin", newRule("*", "*", "pods", "*")),
}, },
roleBindings: []rbac.RoleBinding{ roleBindings: []*rbac.RoleBinding{
newRoleBinding("ns1", "admin", bindToClusterRole, "User:admin", "Group:admins"), newRoleBinding("ns1", "admin", bindToClusterRole, "User:admin", "Group:admins"),
}, },
shouldPass: []authorizer.Attributes{ shouldPass: []authorizer.Attributes{
@ -206,10 +206,10 @@ func TestAuthorizer(t *testing.T) {
}, },
{ {
// test subresource resolution // test subresource resolution
clusterRoles: []rbac.ClusterRole{ clusterRoles: []*rbac.ClusterRole{
newClusterRole("admin", newRule("*", "*", "pods/status", "*")), newClusterRole("admin", newRule("*", "*", "pods/status", "*")),
}, },
roleBindings: []rbac.RoleBinding{ roleBindings: []*rbac.RoleBinding{
newRoleBinding("ns1", "admin", bindToClusterRole, "User:admin", "Group:admins"), newRoleBinding("ns1", "admin", bindToClusterRole, "User:admin", "Group:admins"),
}, },
shouldPass: []authorizer.Attributes{ shouldPass: []authorizer.Attributes{

View File

@ -91,10 +91,10 @@ func newRBACAuthorizer(t *testing.T, superUser string, config *master.Config) au
return generic.RESTOptions{StorageConfig: storageConfig, Decorator: generic.UndecoratedStorage, ResourcePrefix: resource} return generic.RESTOptions{StorageConfig: storageConfig, Decorator: generic.UndecoratedStorage, ResourcePrefix: resource}
} }
roleRegistry := role.NewRegistry(roleetcd.NewREST(newRESTOptions("roles"))) roleRegistry := role.AuthorizerAdapter{Registry: role.NewRegistry(roleetcd.NewREST(newRESTOptions("roles")))}
roleBindingRegistry := rolebinding.NewRegistry(rolebindingetcd.NewREST(newRESTOptions("rolebindings"))) roleBindingRegistry := rolebinding.AuthorizerAdapter{Registry: rolebinding.NewRegistry(rolebindingetcd.NewREST(newRESTOptions("rolebindings")))}
clusterRoleRegistry := clusterrole.NewRegistry(clusterroleetcd.NewREST(newRESTOptions("clusterroles"))) clusterRoleRegistry := clusterrole.AuthorizerAdapter{Registry: clusterrole.NewRegistry(clusterroleetcd.NewREST(newRESTOptions("clusterroles")))}
clusterRoleBindingRegistry := clusterrolebinding.NewRegistry(clusterrolebindingetcd.NewREST(newRESTOptions("clusterrolebindings"))) clusterRoleBindingRegistry := clusterrolebinding.AuthorizerAdapter{Registry: clusterrolebinding.NewRegistry(clusterrolebindingetcd.NewREST(newRESTOptions("clusterrolebindings")))}
return rbac.New(roleRegistry, roleBindingRegistry, clusterRoleRegistry, clusterRoleBindingRegistry, superUser) return rbac.New(roleRegistry, roleBindingRegistry, clusterRoleRegistry, clusterRoleBindingRegistry, superUser)
} }