From ef40aa95724ed6b84f8d7aa8fd6ead5a6ac5cf13 Mon Sep 17 00:00:00 2001 From: Eric Chiang Date: Wed, 25 May 2016 14:20:41 -0700 Subject: [PATCH] pkg/master: enable certificates API and add rbac authorizer --- cmd/kube-apiserver/app/server.go | 36 ++++++++ hack/verify-flags/known-flags.txt | 1 + pkg/apiserver/authz.go | 33 ++++++- pkg/genericapiserver/genericapiserver.go | 2 + .../options/server_run_options.go | 1 + pkg/master/import_known_versions.go | 1 + pkg/master/master.go | 87 ++++++++++++++++++- pkg/master/master_test.go | 2 + plugin/pkg/auth/authorizer/rbac/rbac.go | 79 +++++++++++++++++ plugin/pkg/auth/authorizer/rbac/rbac_test.go | 59 +++++++++++++ 10 files changed, 299 insertions(+), 2 deletions(-) create mode 100644 plugin/pkg/auth/authorizer/rbac/rbac.go create mode 100644 plugin/pkg/auth/authorizer/rbac/rbac_test.go diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index 5ba29fac31509..e479146be16a2 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -46,6 +46,15 @@ import ( kubeletclient "k8s.io/kubernetes/pkg/kubelet/client" "k8s.io/kubernetes/pkg/master" "k8s.io/kubernetes/pkg/registry/cachesize" + "k8s.io/kubernetes/pkg/registry/clusterrole" + clusterroleetcd "k8s.io/kubernetes/pkg/registry/clusterrole/etcd" + "k8s.io/kubernetes/pkg/registry/clusterrolebinding" + clusterrolebindingetcd "k8s.io/kubernetes/pkg/registry/clusterrolebinding/etcd" + "k8s.io/kubernetes/pkg/registry/generic" + "k8s.io/kubernetes/pkg/registry/role" + roleetcd "k8s.io/kubernetes/pkg/registry/role/etcd" + "k8s.io/kubernetes/pkg/registry/rolebinding" + rolebindingetcd "k8s.io/kubernetes/pkg/registry/rolebinding/etcd" "k8s.io/kubernetes/pkg/serviceaccount" ) @@ -198,6 +207,32 @@ func Run(s *options.APIServer) error { } authorizationModeNames := strings.Split(s.AuthorizationMode, ",") + + modeEnabled := func(mode string) bool { + for _, m := range authorizationModeNames { + if m == mode { + return true + } + } + return false + } + + if modeEnabled(apiserver.ModeRBAC) { + mustGetRESTOptions := func(resource string) generic.RESTOptions { + s, err := storageFactory.New(api.Resource(resource)) + if err != nil { + glog.Fatalf("Unable to get %s storage: %v", resource, err) + } + return generic.RESTOptions{Storage: s, Decorator: generic.UndecoratedStorage} + } + + // For initial bootstrapping go directly to etcd to avoid privillege escalation check. + s.AuthorizationConfig.RBACRoleRegistry = role.NewRegistry(roleetcd.NewREST(mustGetRESTOptions("roles"))) + s.AuthorizationConfig.RBACRoleBindingRegistry = rolebinding.NewRegistry(rolebindingetcd.NewREST(mustGetRESTOptions("rolebindings"))) + s.AuthorizationConfig.RBACClusterRoleRegistry = clusterrole.NewRegistry(clusterroleetcd.NewREST(mustGetRESTOptions("clusterroles"))) + s.AuthorizationConfig.RBACClusterRoleBindingRegistry = clusterrolebinding.NewRegistry(clusterrolebindingetcd.NewREST(mustGetRESTOptions("clusterrolebindings"))) + } + authorizer, err := apiserver.NewAuthorizerFromAuthorizationConfig(authorizationModeNames, s.AuthorizationConfig) if err != nil { glog.Fatalf("Invalid Authorization Config: %v", err) @@ -216,6 +251,7 @@ func Run(s *options.APIServer) error { genericConfig.Authenticator = authenticator genericConfig.SupportsBasicAuth = len(s.BasicAuthFile) > 0 genericConfig.Authorizer = authorizer + genericConfig.AuthorizerRBACSuperUser = s.AuthorizationConfig.RBACSuperUser genericConfig.AdmissionControl = admissionController genericConfig.APIResourceConfigSource = storageFactory.APIResourceConfigSource genericConfig.MasterServiceNamespace = s.MasterServiceNamespace diff --git a/hack/verify-flags/known-flags.txt b/hack/verify-flags/known-flags.txt index c722de3cf486b..87569c61d2fa6 100644 --- a/hack/verify-flags/known-flags.txt +++ b/hack/verify-flags/known-flags.txt @@ -22,6 +22,7 @@ authentication-token-webhook-cache-ttl authentication-token-webhook-config-file authorization-mode authorization-policy-file +authorization-rbac-super-user authorization-webhook-config-file authorization-webhook-cache-authorized-ttl authorization-webhook-cache-unauthorized-ttl diff --git a/pkg/apiserver/authz.go b/pkg/apiserver/authz.go index 78be39a6764cd..d12ae6b861c2e 100644 --- a/pkg/apiserver/authz.go +++ b/pkg/apiserver/authz.go @@ -24,6 +24,11 @@ import ( "k8s.io/kubernetes/pkg/auth/authorizer" "k8s.io/kubernetes/pkg/auth/authorizer/abac" "k8s.io/kubernetes/pkg/auth/authorizer/union" + "k8s.io/kubernetes/pkg/registry/clusterrole" + "k8s.io/kubernetes/pkg/registry/clusterrolebinding" + "k8s.io/kubernetes/pkg/registry/role" + "k8s.io/kubernetes/pkg/registry/rolebinding" + "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac" "k8s.io/kubernetes/plugin/pkg/auth/authorizer/webhook" ) @@ -63,10 +68,11 @@ const ( ModeAlwaysDeny string = "AlwaysDeny" ModeABAC string = "ABAC" ModeWebhook string = "Webhook" + ModeRBAC string = "RBAC" ) // Keep this list in sync with constant list above. -var AuthorizationModeChoices = []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC, ModeWebhook} +var AuthorizationModeChoices = []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC, ModeWebhook, ModeRBAC} type AuthorizationConfig struct { // Options for ModeABAC @@ -82,6 +88,16 @@ type AuthorizationConfig struct { WebhookCacheAuthorizedTTL time.Duration // TTL for caching of unauthorized responses from the webhook server. WebhookCacheUnauthorizedTTL time.Duration + + // Options for RBAC + + // User which can bootstrap role policies + RBACSuperUser string + + RBACClusterRoleRegistry clusterrole.Registry + RBACClusterRoleBindingRegistry clusterrolebinding.Registry + RBACRoleRegistry role.Registry + RBACRoleBindingRegistry rolebinding.Registry } // NewAuthorizerFromAuthorizationConfig returns the right sort of union of multiple authorizer.Authorizer objects @@ -126,6 +142,18 @@ func NewAuthorizerFromAuthorizationConfig(authorizationModes []string, config Au return nil, err } authorizers = append(authorizers, webhookAuthorizer) + case ModeRBAC: + rbacAuthorizer, err := rbac.New( + config.RBACRoleRegistry, + config.RBACRoleBindingRegistry, + config.RBACClusterRoleRegistry, + config.RBACClusterRoleBindingRegistry, + config.RBACSuperUser, + ) + if err != nil { + return nil, err + } + authorizers = append(authorizers, rbacAuthorizer) default: return nil, fmt.Errorf("Unknown authorization mode %s specified", authorizationMode) } @@ -138,6 +166,9 @@ func NewAuthorizerFromAuthorizationConfig(authorizationModes []string, config Au if !authorizerMap[ModeWebhook] && config.WebhookConfigFile != "" { return nil, errors.New("Cannot specify --authorization-webhook-config-file without mode Webhook") } + if !authorizerMap[ModeRBAC] && config.RBACSuperUser != "" { + return nil, errors.New("Cannot specify --authorization-rbac-super-user without mode RBAC") + } return union.New(authorizers...), nil } diff --git a/pkg/genericapiserver/genericapiserver.go b/pkg/genericapiserver/genericapiserver.go index 3e50b2b55ffa5..6face444df216 100644 --- a/pkg/genericapiserver/genericapiserver.go +++ b/pkg/genericapiserver/genericapiserver.go @@ -118,6 +118,8 @@ type Config struct { Authorizer authorizer.Authorizer AdmissionControl admission.Interface MasterServiceNamespace string + // TODO(ericchiang): Determine if policy escalation checks should be an admission controller. + AuthorizerRBACSuperUser string // Map requests to contexts. Exported so downstream consumers can provider their own mappers RequestContextMapper api.RequestContextMapper diff --git a/pkg/genericapiserver/options/server_run_options.go b/pkg/genericapiserver/options/server_run_options.go index 64226143c12cd..a1c6b26111f53 100644 --- a/pkg/genericapiserver/options/server_run_options.go +++ b/pkg/genericapiserver/options/server_run_options.go @@ -227,6 +227,7 @@ func (s *ServerRunOptions) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&s.AuthorizationConfig.WebhookConfigFile, "authorization-webhook-config-file", s.AuthorizationConfig.WebhookConfigFile, "File with webhook configuration in kubeconfig format, used with --authorization-mode=Webhook. The API server will query the remote service to determine access on the API server's secure port.") fs.DurationVar(&s.AuthorizationConfig.WebhookCacheAuthorizedTTL, "authorization-webhook-cache-authorized-ttl", s.AuthorizationConfig.WebhookCacheAuthorizedTTL, "The duration to cache 'authorized' responses from the webhook authorizer. Default is 5m.") fs.DurationVar(&s.AuthorizationConfig.WebhookCacheUnauthorizedTTL, "authorization-webhook-cache-unauthorized-ttl", s.AuthorizationConfig.WebhookCacheUnauthorizedTTL, "The duration to cache 'unauthorized' responses from the webhook authorizer. Default is 30s.") + fs.StringVar(&s.AuthorizationConfig.RBACSuperUser, "authorization-rbac-super-user", s.AuthorizationConfig.RBACSuperUser, "If specified, a username which avoids RBAC authorization checks and role binding privilege escalation checks, to be used with --authorization-mode=RBAC.") fs.StringVar(&s.BasicAuthFile, "basic-auth-file", s.BasicAuthFile, "If set, the file that will be used to admit requests to the secure port of the API server via http basic authentication.") diff --git a/pkg/master/import_known_versions.go b/pkg/master/import_known_versions.go index 990f569da3df6..f7ad207fae300 100644 --- a/pkg/master/import_known_versions.go +++ b/pkg/master/import_known_versions.go @@ -29,6 +29,7 @@ import ( _ "k8s.io/kubernetes/pkg/apis/componentconfig/install" _ "k8s.io/kubernetes/pkg/apis/extensions/install" _ "k8s.io/kubernetes/pkg/apis/policy/install" + _ "k8s.io/kubernetes/pkg/apis/rbac/install" ) func init() { diff --git a/pkg/master/master.go b/pkg/master/master.go index ccc96b050d358..736e53603286a 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -43,12 +43,21 @@ import ( extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" "k8s.io/kubernetes/pkg/apis/policy" policyapiv1alpha1 "k8s.io/kubernetes/pkg/apis/policy/v1alpha1" + "k8s.io/kubernetes/pkg/apis/rbac" + rbacapi "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1" + rbacvalidation "k8s.io/kubernetes/pkg/apis/rbac/validation" "k8s.io/kubernetes/pkg/apiserver" apiservermetrics "k8s.io/kubernetes/pkg/apiserver/metrics" "k8s.io/kubernetes/pkg/genericapiserver" "k8s.io/kubernetes/pkg/healthz" kubeletclient "k8s.io/kubernetes/pkg/kubelet/client" "k8s.io/kubernetes/pkg/master/ports" + "k8s.io/kubernetes/pkg/registry/clusterrole" + clusterroleetcd "k8s.io/kubernetes/pkg/registry/clusterrole/etcd" + clusterrolepolicybased "k8s.io/kubernetes/pkg/registry/clusterrole/policybased" + "k8s.io/kubernetes/pkg/registry/clusterrolebinding" + clusterrolebindingetcd "k8s.io/kubernetes/pkg/registry/clusterrolebinding/etcd" + clusterrolebindingpolicybased "k8s.io/kubernetes/pkg/registry/clusterrolebinding/policybased" "k8s.io/kubernetes/pkg/registry/componentstatus" configmapetcd "k8s.io/kubernetes/pkg/registry/configmap/etcd" controlleretcd "k8s.io/kubernetes/pkg/registry/controller/etcd" @@ -75,6 +84,12 @@ import ( podtemplateetcd "k8s.io/kubernetes/pkg/registry/podtemplate/etcd" replicasetetcd "k8s.io/kubernetes/pkg/registry/replicaset/etcd" resourcequotaetcd "k8s.io/kubernetes/pkg/registry/resourcequota/etcd" + "k8s.io/kubernetes/pkg/registry/role" + roleetcd "k8s.io/kubernetes/pkg/registry/role/etcd" + rolepolicybased "k8s.io/kubernetes/pkg/registry/role/policybased" + "k8s.io/kubernetes/pkg/registry/rolebinding" + rolebindingetcd "k8s.io/kubernetes/pkg/registry/rolebinding/etcd" + rolebindingpolicybased "k8s.io/kubernetes/pkg/registry/rolebinding/policybased" secretetcd "k8s.io/kubernetes/pkg/registry/secret/etcd" "k8s.io/kubernetes/pkg/registry/service" etcdallocator "k8s.io/kubernetes/pkg/registry/service/allocator/etcd" @@ -411,6 +426,39 @@ func (m *Master) InstallAPIs(c *Config) { allGroups = append(allGroups, group) } + + if c.APIResourceConfigSource.AnyResourcesForVersionEnabled(rbacapi.SchemeGroupVersion) { + rbacResources := m.getRBACResources(c) + rbacGroupMeta := registered.GroupOrDie(rbac.GroupName) + + // Hard code preferred group version to rbac/v1alpha1 + rbacGroupMeta.GroupVersion = rbacapi.SchemeGroupVersion + + apiGroupInfo := genericapiserver.APIGroupInfo{ + GroupMeta: *rbacGroupMeta, + VersionedResourcesStorageMap: map[string]map[string]rest.Storage{ + "v1alpha1": rbacResources, + }, + OptionsExternalVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion, + Scheme: api.Scheme, + ParameterCodec: api.ParameterCodec, + NegotiatedSerializer: api.Codecs, + } + apiGroupsInfo = append(apiGroupsInfo, apiGroupInfo) + + rbacGVForDiscovery := unversioned.GroupVersionForDiscovery{ + GroupVersion: rbacGroupMeta.GroupVersion.String(), + Version: rbacGroupMeta.GroupVersion.Version, + } + group := unversioned.APIGroup{ + Name: rbacGroupMeta.GroupVersion.Group, + Versions: []unversioned.GroupVersionForDiscovery{rbacGVForDiscovery}, + PreferredVersion: rbacGVForDiscovery, + } + allGroups = append(allGroups, group) + + } + if err := m.InstallAPIGroups(apiGroupsInfo); err != nil { glog.Fatalf("Error in registering group versions: %v", err) } @@ -908,6 +956,43 @@ func (m *Master) getAppsResources(c *Config) map[string]rest.Storage { return storage } +func (m *Master) getRBACResources(c *Config) map[string]rest.Storage { + version := rbacapi.SchemeGroupVersion + + once := new(sync.Once) + var authorizationRuleResolver rbacvalidation.AuthorizationRuleResolver + newRuleValidator := func() rbacvalidation.AuthorizationRuleResolver { + once.Do(func() { + authorizationRuleResolver = rbacvalidation.NewDefaultRuleResolver( + role.NewRegistry(roleetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("roles")))), + rolebinding.NewRegistry(rolebindingetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("rolebindings")))), + clusterrole.NewRegistry(clusterroleetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("clusterroles")))), + clusterrolebinding.NewRegistry(clusterrolebindingetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("clusterrolebindings")))), + ) + }) + return authorizationRuleResolver + } + + storage := map[string]rest.Storage{} + if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("roles")) { + rolesStorage := roleetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("roles"))) + storage["roles"] = rolepolicybased.NewStorage(rolesStorage, newRuleValidator(), c.AuthorizerRBACSuperUser) + } + if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("rolebindings")) { + roleBindingsStorage := rolebindingetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("rolebindings"))) + storage["rolebindings"] = rolebindingpolicybased.NewStorage(roleBindingsStorage, newRuleValidator(), c.AuthorizerRBACSuperUser) + } + if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("clusterroles")) { + clusterRolesStorage := clusterroleetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("clusterroles"))) + storage["clusterroles"] = clusterrolepolicybased.NewStorage(clusterRolesStorage, newRuleValidator(), c.AuthorizerRBACSuperUser) + } + if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("clusterrolebindings")) { + clusterRoleBindingsStorage := clusterrolebindingetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("clusterrolebindings"))) + storage["clusterrolebindings"] = clusterrolebindingpolicybased.NewStorage(clusterRoleBindingsStorage, newRuleValidator(), c.AuthorizerRBACSuperUser) + } + return storage +} + // findExternalAddress returns ExternalIP of provided node with fallback to LegacyHostIP. func findExternalAddress(node *api.Node) (string, error) { var fallback string @@ -960,7 +1045,7 @@ func (m *Master) IsTunnelSyncHealthy(req *http.Request) error { func DefaultAPIResourceConfigSource() *genericapiserver.ResourceConfig { ret := genericapiserver.NewResourceConfig() - ret.EnableVersions(apiv1.SchemeGroupVersion, extensionsapiv1beta1.SchemeGroupVersion, batchapiv1.SchemeGroupVersion, autoscalingapiv1.SchemeGroupVersion, appsapi.SchemeGroupVersion, policyapiv1alpha1.SchemeGroupVersion) + ret.EnableVersions(apiv1.SchemeGroupVersion, extensionsapiv1beta1.SchemeGroupVersion, batchapiv1.SchemeGroupVersion, autoscalingapiv1.SchemeGroupVersion, appsapi.SchemeGroupVersion, policyapiv1alpha1.SchemeGroupVersion, rbacapi.SchemeGroupVersion) // all extensions resources except these are disabled by default ret.EnableResources( diff --git a/pkg/master/master_test.go b/pkg/master/master_test.go index 28f724016b3a9..7ed52a0e49824 100644 --- a/pkg/master/master_test.go +++ b/pkg/master/master_test.go @@ -43,6 +43,7 @@ import ( batchapiv2alpha1 "k8s.io/kubernetes/pkg/apis/batch/v2alpha1" "k8s.io/kubernetes/pkg/apis/extensions" extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" + "k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/apiserver" "k8s.io/kubernetes/pkg/genericapiserver" "k8s.io/kubernetes/pkg/kubelet/client" @@ -89,6 +90,7 @@ func setUp(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert. resourceEncoding.SetVersionEncoding(batch.GroupName, *testapi.Batch.GroupVersion(), unversioned.GroupVersion{Group: batch.GroupName, Version: runtime.APIVersionInternal}) resourceEncoding.SetVersionEncoding(apps.GroupName, *testapi.Apps.GroupVersion(), unversioned.GroupVersion{Group: apps.GroupName, Version: runtime.APIVersionInternal}) resourceEncoding.SetVersionEncoding(extensions.GroupName, *testapi.Extensions.GroupVersion(), unversioned.GroupVersion{Group: extensions.GroupName, Version: runtime.APIVersionInternal}) + resourceEncoding.SetVersionEncoding(rbac.GroupName, *testapi.Rbac.GroupVersion(), unversioned.GroupVersion{Group: rbac.GroupName, Version: runtime.APIVersionInternal}) storageFactory := genericapiserver.NewDefaultStorageFactory(storageConfig, testapi.StorageMediaType(), api.Codecs, resourceEncoding, DefaultAPIResourceConfigSource()) config.StorageFactory = storageFactory diff --git a/plugin/pkg/auth/authorizer/rbac/rbac.go b/plugin/pkg/auth/authorizer/rbac/rbac.go new file mode 100644 index 0000000000000..9fa1ec47ca634 --- /dev/null +++ b/plugin/pkg/auth/authorizer/rbac/rbac.go @@ -0,0 +1,79 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 rbac implements the authorizer.Authorizer interface using roles base access control. +package rbac + +import ( + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/rbac" + "k8s.io/kubernetes/pkg/apis/rbac/validation" + "k8s.io/kubernetes/pkg/auth/authorizer" + "k8s.io/kubernetes/pkg/auth/user" + "k8s.io/kubernetes/pkg/registry/clusterrole" + "k8s.io/kubernetes/pkg/registry/clusterrolebinding" + "k8s.io/kubernetes/pkg/registry/role" + "k8s.io/kubernetes/pkg/registry/rolebinding" +) + +type RBACAuthorizer struct { + superUser string + + authorizationRuleResolver validation.AuthorizationRuleResolver +} + +func (r *RBACAuthorizer) Authorize(attr authorizer.Attributes) error { + if r.superUser != "" && attr.GetUserName() == r.superUser { + return nil + } + + userInfo := &user.DefaultInfo{ + Name: attr.GetUserName(), + Groups: attr.GetGroups(), + } + + ctx := api.WithNamespace(api.WithUser(api.NewContext(), userInfo), attr.GetNamespace()) + + // Frame the authorization request as a privilege escalation check. + var requestedRule rbac.PolicyRule + if attr.IsResourceRequest() { + requestedRule = rbac.PolicyRule{ + Verbs: []string{attr.GetVerb()}, + APIGroups: []string{attr.GetAPIGroup()}, // TODO(ericchiang): add api version here too? + Resources: []string{attr.GetResource()}, + ResourceNames: []string{attr.GetName()}, + } + } else { + requestedRule = rbac.PolicyRule{ + NonResourceURLs: []string{attr.GetPath()}, + } + } + + return validation.ConfirmNoEscalation(ctx, r.authorizationRuleResolver, []rbac.PolicyRule{requestedRule}) +} + +func New(roleRegistry role.Registry, roleBindingRegistry rolebinding.Registry, clusterRoleRegistry clusterrole.Registry, clusterRoleBindingRegistry clusterrolebinding.Registry, superUser string) (*RBACAuthorizer, error) { + authorizer := &RBACAuthorizer{ + superUser: superUser, + authorizationRuleResolver: validation.NewDefaultRuleResolver( + roleRegistry, + roleBindingRegistry, + clusterRoleRegistry, + clusterRoleBindingRegistry, + ), + } + return authorizer, nil +} diff --git a/plugin/pkg/auth/authorizer/rbac/rbac_test.go b/plugin/pkg/auth/authorizer/rbac/rbac_test.go new file mode 100644 index 0000000000000..66ad85fb1a0c7 --- /dev/null +++ b/plugin/pkg/auth/authorizer/rbac/rbac_test.go @@ -0,0 +1,59 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +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 rbac + +import ( + "testing" + + "k8s.io/kubernetes/pkg/api/testapi" + "k8s.io/kubernetes/pkg/apis/rbac" + "k8s.io/kubernetes/pkg/registry/clusterrole" + clusterroleetcd "k8s.io/kubernetes/pkg/registry/clusterrole/etcd" + "k8s.io/kubernetes/pkg/registry/clusterrolebinding" + clusterrolebindingetcd "k8s.io/kubernetes/pkg/registry/clusterrolebinding/etcd" + "k8s.io/kubernetes/pkg/registry/generic" + "k8s.io/kubernetes/pkg/registry/role" + roleetcd "k8s.io/kubernetes/pkg/registry/role/etcd" + "k8s.io/kubernetes/pkg/registry/rolebinding" + rolebindingetcd "k8s.io/kubernetes/pkg/registry/rolebinding/etcd" + "k8s.io/kubernetes/pkg/storage/etcd" + "k8s.io/kubernetes/pkg/storage/etcd/etcdtest" + etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing" +) + +func TestNew(t *testing.T) { + // NOTE(ericchiang): Can't get this strategy to do reads. Get cryptic "client: etcd cluster is unavailable or misconfigured" + // Writes work fine, so use to test storing initial data. + server := etcdtesting.NewEtcdTestClientServer(t) + defer server.Terminate(t) + + codec := testapi.Groups[rbac.GroupName].StorageCodec() + getRESTOptions := func(resource string) generic.RESTOptions { + cacheSize := etcdtest.DeserializationCacheSize + storage := etcd.NewEtcdStorage(server.Client, codec, resource, false, cacheSize) + return generic.RESTOptions{Storage: storage, Decorator: generic.UndecoratedStorage} + } + + roleRegistry := role.NewRegistry(roleetcd.NewREST(getRESTOptions("roles"))) + roleBindingRegistry := rolebinding.NewRegistry(rolebindingetcd.NewREST(getRESTOptions("rolebindings"))) + clusterRoleRegistry := clusterrole.NewRegistry(clusterroleetcd.NewREST(getRESTOptions("clusterroles"))) + clusterRoleBindingRegistry := clusterrolebinding.NewRegistry(clusterrolebindingetcd.NewREST(getRESTOptions("clusterrolebindings"))) + _, err := New(roleRegistry, roleBindingRegistry, clusterRoleRegistry, clusterRoleBindingRegistry, "") + if err != nil { + t.Fatalf("failed to create authorizer: %v", err) + } +}