...

Source file src/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/rbac.go

Documentation: k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac

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

View as plain text