pkg/master: enable certificates API and add rbac authorizer

This commit is contained in:
Eric Chiang 2016-05-25 14:20:41 -07:00
parent 6f312f145d
commit ef40aa9572
10 changed files with 299 additions and 2 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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.")

View File

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

View File

@ -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: &registered.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(

View File

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

View File

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

View File

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