k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/plugin/pkg/auth/authorizer/rbac/rbac.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package rbac implements the authorizer.Authorizer interface using roles base access control. 18 package rbac 19 20 import ( 21 "bytes" 22 "context" 23 "fmt" 24 25 "k8s.io/klog/v2" 26 27 rbacv1 "k8s.io/api/rbac/v1" 28 "k8s.io/apimachinery/pkg/labels" 29 utilerrors "k8s.io/apimachinery/pkg/util/errors" 30 "k8s.io/apiserver/pkg/authentication/user" 31 "k8s.io/apiserver/pkg/authorization/authorizer" 32 rbaclisters "k8s.io/client-go/listers/rbac/v1" 33 rbacv1helpers "k8s.io/kubernetes/pkg/apis/rbac/v1" 34 rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation" 35 ) 36 37 type RequestToRuleMapper interface { 38 // RulesFor returns all known PolicyRules and any errors that happened while locating those rules. 39 // Any rule returned is still valid, since rules are deny by default. If you can pass with the rules 40 // supplied, you do not have to fail the request. If you cannot, you should indicate the error along 41 // with your denial. 42 RulesFor(subject user.Info, namespace string) ([]rbacv1.PolicyRule, error) 43 44 // VisitRulesFor invokes visitor() with each rule that applies to a given user in a given namespace, 45 // and each error encountered resolving those rules. Rule may be nil if err is non-nil. 46 // If visitor() returns false, visiting is short-circuited. 47 VisitRulesFor(user user.Info, namespace string, visitor func(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool) 48 } 49 50 type RBACAuthorizer struct { 51 authorizationRuleResolver RequestToRuleMapper 52 } 53 54 // authorizingVisitor short-circuits once allowed, and collects any resolution errors encountered 55 type authorizingVisitor struct { 56 requestAttributes authorizer.Attributes 57 58 allowed bool 59 reason string 60 errors []error 61 } 62 63 func (v *authorizingVisitor) visit(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool { 64 if rule != nil && RuleAllows(v.requestAttributes, rule) { 65 v.allowed = true 66 v.reason = fmt.Sprintf("RBAC: allowed by %s", source.String()) 67 return false 68 } 69 if err != nil { 70 v.errors = append(v.errors, err) 71 } 72 return true 73 } 74 75 func (r *RBACAuthorizer) Authorize(ctx context.Context, requestAttributes authorizer.Attributes) (authorizer.Decision, string, error) { 76 ruleCheckingVisitor := &authorizingVisitor{requestAttributes: requestAttributes} 77 78 r.authorizationRuleResolver.VisitRulesFor(requestAttributes.GetUser(), requestAttributes.GetNamespace(), ruleCheckingVisitor.visit) 79 if ruleCheckingVisitor.allowed { 80 return authorizer.DecisionAllow, ruleCheckingVisitor.reason, nil 81 } 82 83 // Build a detailed log of the denial. 84 // Make the whole block conditional so we don't do a lot of string-building we won't use. 85 if klogV := klog.V(5); klogV.Enabled() { 86 var operation string 87 if requestAttributes.IsResourceRequest() { 88 b := &bytes.Buffer{} 89 b.WriteString(`"`) 90 b.WriteString(requestAttributes.GetVerb()) 91 b.WriteString(`" resource "`) 92 b.WriteString(requestAttributes.GetResource()) 93 if len(requestAttributes.GetAPIGroup()) > 0 { 94 b.WriteString(`.`) 95 b.WriteString(requestAttributes.GetAPIGroup()) 96 } 97 if len(requestAttributes.GetSubresource()) > 0 { 98 b.WriteString(`/`) 99 b.WriteString(requestAttributes.GetSubresource()) 100 } 101 b.WriteString(`"`) 102 if len(requestAttributes.GetName()) > 0 { 103 b.WriteString(` named "`) 104 b.WriteString(requestAttributes.GetName()) 105 b.WriteString(`"`) 106 } 107 operation = b.String() 108 } else { 109 operation = fmt.Sprintf("%q nonResourceURL %q", requestAttributes.GetVerb(), requestAttributes.GetPath()) 110 } 111 112 var scope string 113 if ns := requestAttributes.GetNamespace(); len(ns) > 0 { 114 scope = fmt.Sprintf("in namespace %q", ns) 115 } else { 116 scope = "cluster-wide" 117 } 118 119 klogV.Infof("RBAC: no rules authorize user %q with groups %q to %s %s", requestAttributes.GetUser().GetName(), requestAttributes.GetUser().GetGroups(), operation, scope) 120 } 121 122 reason := "" 123 if len(ruleCheckingVisitor.errors) > 0 { 124 reason = fmt.Sprintf("RBAC: %v", utilerrors.NewAggregate(ruleCheckingVisitor.errors)) 125 } 126 return authorizer.DecisionNoOpinion, reason, nil 127 } 128 129 func (r *RBACAuthorizer) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) { 130 var ( 131 resourceRules []authorizer.ResourceRuleInfo 132 nonResourceRules []authorizer.NonResourceRuleInfo 133 ) 134 135 policyRules, err := r.authorizationRuleResolver.RulesFor(user, namespace) 136 for _, policyRule := range policyRules { 137 if len(policyRule.Resources) > 0 { 138 r := authorizer.DefaultResourceRuleInfo{ 139 Verbs: policyRule.Verbs, 140 APIGroups: policyRule.APIGroups, 141 Resources: policyRule.Resources, 142 ResourceNames: policyRule.ResourceNames, 143 } 144 var resourceRule authorizer.ResourceRuleInfo = &r 145 resourceRules = append(resourceRules, resourceRule) 146 } 147 if len(policyRule.NonResourceURLs) > 0 { 148 r := authorizer.DefaultNonResourceRuleInfo{ 149 Verbs: policyRule.Verbs, 150 NonResourceURLs: policyRule.NonResourceURLs, 151 } 152 var nonResourceRule authorizer.NonResourceRuleInfo = &r 153 nonResourceRules = append(nonResourceRules, nonResourceRule) 154 } 155 } 156 return resourceRules, nonResourceRules, false, err 157 } 158 159 func New(roles rbacregistryvalidation.RoleGetter, roleBindings rbacregistryvalidation.RoleBindingLister, clusterRoles rbacregistryvalidation.ClusterRoleGetter, clusterRoleBindings rbacregistryvalidation.ClusterRoleBindingLister) *RBACAuthorizer { 160 authorizer := &RBACAuthorizer{ 161 authorizationRuleResolver: rbacregistryvalidation.NewDefaultRuleResolver( 162 roles, roleBindings, clusterRoles, clusterRoleBindings, 163 ), 164 } 165 return authorizer 166 } 167 168 func RulesAllow(requestAttributes authorizer.Attributes, rules ...rbacv1.PolicyRule) bool { 169 for i := range rules { 170 if RuleAllows(requestAttributes, &rules[i]) { 171 return true 172 } 173 } 174 175 return false 176 } 177 178 func RuleAllows(requestAttributes authorizer.Attributes, rule *rbacv1.PolicyRule) bool { 179 if requestAttributes.IsResourceRequest() { 180 combinedResource := requestAttributes.GetResource() 181 if len(requestAttributes.GetSubresource()) > 0 { 182 combinedResource = requestAttributes.GetResource() + "/" + requestAttributes.GetSubresource() 183 } 184 185 return rbacv1helpers.VerbMatches(rule, requestAttributes.GetVerb()) && 186 rbacv1helpers.APIGroupMatches(rule, requestAttributes.GetAPIGroup()) && 187 rbacv1helpers.ResourceMatches(rule, combinedResource, requestAttributes.GetSubresource()) && 188 rbacv1helpers.ResourceNameMatches(rule, requestAttributes.GetName()) 189 } 190 191 return rbacv1helpers.VerbMatches(rule, requestAttributes.GetVerb()) && 192 rbacv1helpers.NonResourceURLMatches(rule, requestAttributes.GetPath()) 193 } 194 195 type RoleGetter struct { 196 Lister rbaclisters.RoleLister 197 } 198 199 func (g *RoleGetter) GetRole(namespace, name string) (*rbacv1.Role, error) { 200 return g.Lister.Roles(namespace).Get(name) 201 } 202 203 type RoleBindingLister struct { 204 Lister rbaclisters.RoleBindingLister 205 } 206 207 func (l *RoleBindingLister) ListRoleBindings(namespace string) ([]*rbacv1.RoleBinding, error) { 208 return l.Lister.RoleBindings(namespace).List(labels.Everything()) 209 } 210 211 type ClusterRoleGetter struct { 212 Lister rbaclisters.ClusterRoleLister 213 } 214 215 func (g *ClusterRoleGetter) GetClusterRole(name string) (*rbacv1.ClusterRole, error) { 216 return g.Lister.Get(name) 217 } 218 219 type ClusterRoleBindingLister struct { 220 Lister rbaclisters.ClusterRoleBindingLister 221 } 222 223 func (l *ClusterRoleBindingLister) ListClusterRoleBindings() ([]*rbacv1.ClusterRoleBinding, error) { 224 return l.Lister.List(labels.Everything()) 225 }