From 49124293c3c28476c69787b25051875705d2b3b1 Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Tue, 7 Nov 2023 10:33:12 -0600 Subject: [PATCH] Store constructed node/rbac/abac authorizers --- pkg/kubeapiserver/authorizer/config.go | 30 +++++++++----- pkg/kubeapiserver/authorizer/reload.go | 54 ++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 pkg/kubeapiserver/authorizer/reload.go diff --git a/pkg/kubeapiserver/authorizer/config.go b/pkg/kubeapiserver/authorizer/config.go index fd40423caa6..6eebee5e5df 100644 --- a/pkg/kubeapiserver/authorizer/config.go +++ b/pkg/kubeapiserver/authorizer/config.go @@ -74,6 +74,10 @@ func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, erro return nil, nil, fmt.Errorf("at least one authorization mode must be passed") } + r := &reloadableAuthorizerResolver{ + initialConfig: config, + } + var ( authorizers []authorizer.Authorizer ruleResolvers []authorizer.RuleResolver @@ -96,9 +100,9 @@ func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, erro config.VersionedInformerFactory.Core().V1().PersistentVolumes(), config.VersionedInformerFactory.Storage().V1().VolumeAttachments(), ) - nodeAuthorizer := node.NewAuthorizer(graph, nodeidentifier.NewDefaultNodeIdentifier(), bootstrappolicy.NodeRules()) - authorizers = append(authorizers, nodeAuthorizer) - ruleResolvers = append(ruleResolvers, nodeAuthorizer) + r.nodeAuthorizer = node.NewAuthorizer(graph, nodeidentifier.NewDefaultNodeIdentifier(), bootstrappolicy.NodeRules()) + authorizers = append(authorizers, r.nodeAuthorizer) + ruleResolvers = append(ruleResolvers, r.nodeAuthorizer) case authzconfig.AuthorizerType(modes.ModeAlwaysAllow): alwaysAllowAuthorizer := authorizerfactory.NewAlwaysAllowAuthorizer() @@ -109,12 +113,13 @@ func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, erro authorizers = append(authorizers, alwaysDenyAuthorizer) ruleResolvers = append(ruleResolvers, alwaysDenyAuthorizer) case authzconfig.AuthorizerType(modes.ModeABAC): - abacAuthorizer, err := abac.NewFromFile(config.PolicyFile) + var err error + r.abacAuthorizer, err = abac.NewFromFile(config.PolicyFile) if err != nil { return nil, nil, err } - authorizers = append(authorizers, abacAuthorizer) - ruleResolvers = append(ruleResolvers, abacAuthorizer) + authorizers = append(authorizers, r.abacAuthorizer) + ruleResolvers = append(ruleResolvers, r.abacAuthorizer) case authzconfig.AuthorizerType(modes.ModeWebhook): if config.WebhookRetryBackoff == nil { return nil, nil, errors.New("retry backoff parameters for authorization webhook has not been specified") @@ -146,20 +151,25 @@ func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, erro authorizers = append(authorizers, webhookAuthorizer) ruleResolvers = append(ruleResolvers, webhookAuthorizer) case authzconfig.AuthorizerType(modes.ModeRBAC): - rbacAuthorizer := rbac.New( + r.rbacAuthorizer = rbac.New( &rbac.RoleGetter{Lister: config.VersionedInformerFactory.Rbac().V1().Roles().Lister()}, &rbac.RoleBindingLister{Lister: config.VersionedInformerFactory.Rbac().V1().RoleBindings().Lister()}, &rbac.ClusterRoleGetter{Lister: config.VersionedInformerFactory.Rbac().V1().ClusterRoles().Lister()}, &rbac.ClusterRoleBindingLister{Lister: config.VersionedInformerFactory.Rbac().V1().ClusterRoleBindings().Lister()}, ) - authorizers = append(authorizers, rbacAuthorizer) - ruleResolvers = append(ruleResolvers, rbacAuthorizer) + authorizers = append(authorizers, r.rbacAuthorizer) + ruleResolvers = append(ruleResolvers, r.rbacAuthorizer) default: return nil, nil, fmt.Errorf("unknown authorization mode %s specified", configuredAuthorizer.Type) } } - return union.New(authorizers...), union.NewRuleResolvers(ruleResolvers...), nil + r.current.Store(&authorizerResolver{ + authorizer: union.New(authorizers...), + ruleResolver: union.NewRuleResolvers(ruleResolvers...), + }) + + return r, r, nil } // RepeatableAuthorizerTypes is the list of Authorizer that can be repeated in the Authorization Config diff --git a/pkg/kubeapiserver/authorizer/reload.go b/pkg/kubeapiserver/authorizer/reload.go new file mode 100644 index 00000000000..3b0faf90882 --- /dev/null +++ b/pkg/kubeapiserver/authorizer/reload.go @@ -0,0 +1,54 @@ +/* +Copyright 2024 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 authorizer + +import ( + "context" + "sync/atomic" + + "k8s.io/apiserver/pkg/authentication/user" + "k8s.io/apiserver/pkg/authorization/authorizer" + "k8s.io/kubernetes/pkg/auth/authorizer/abac" + "k8s.io/kubernetes/plugin/pkg/auth/authorizer/node" + "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac" +) + +type reloadableAuthorizerResolver struct { + // initialConfig holds the ReloadFile used to initiate background reloading, + // and information used to construct webhooks that isn't exposed in the authorization + // configuration file (dial function, backoff settings, etc) + initialConfig Config + + nodeAuthorizer *node.NodeAuthorizer + rbacAuthorizer *rbac.RBACAuthorizer + abacAuthorizer abac.PolicyList + + current atomic.Pointer[authorizerResolver] +} + +type authorizerResolver struct { + authorizer authorizer.Authorizer + ruleResolver authorizer.RuleResolver +} + +func (r *reloadableAuthorizerResolver) Authorize(ctx context.Context, a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) { + return r.current.Load().authorizer.Authorize(ctx, a) +} + +func (r *reloadableAuthorizerResolver) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) { + return r.current.Load().ruleResolver.RulesFor(user, namespace) +}