...

Source file src/k8s.io/kubernetes/pkg/registry/rbac/validation/rule.go

Documentation: k8s.io/kubernetes/pkg/registry/rbac/validation

     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 validation
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"strings"
    24  
    25  	"k8s.io/klog/v2"
    26  
    27  	rbacv1 "k8s.io/api/rbac/v1"
    28  	utilerrors "k8s.io/apimachinery/pkg/util/errors"
    29  	"k8s.io/apimachinery/pkg/util/sets"
    30  	"k8s.io/apiserver/pkg/authentication/serviceaccount"
    31  	"k8s.io/apiserver/pkg/authentication/user"
    32  	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
    33  	"k8s.io/component-helpers/auth/rbac/validation"
    34  	rbacv1helpers "k8s.io/kubernetes/pkg/apis/rbac/v1"
    35  )
    36  
    37  type AuthorizationRuleResolver interface {
    38  	// GetRoleReferenceRules attempts to resolve the role reference of a RoleBinding or ClusterRoleBinding.  The passed namespace should be the namespace
    39  	// of the role binding, the empty string if a cluster role binding.
    40  	GetRoleReferenceRules(roleRef rbacv1.RoleRef, namespace string) ([]rbacv1.PolicyRule, error)
    41  
    42  	// RulesFor returns the list of rules that apply to a given user in a given namespace and error.  If an error is returned, the slice of
    43  	// PolicyRules may not be complete, but it contains all retrievable rules.  This is done because policy rules are purely additive and policy determinations
    44  	// can be made on the basis of those rules that are found.
    45  	RulesFor(user user.Info, namespace string) ([]rbacv1.PolicyRule, error)
    46  
    47  	// VisitRulesFor invokes visitor() with each rule that applies to a given user in a given namespace, and each error encountered resolving those rules.
    48  	// If visitor() returns false, visiting is short-circuited.
    49  	VisitRulesFor(user user.Info, namespace string, visitor func(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool)
    50  }
    51  
    52  // ConfirmNoEscalation determines if the roles for a given user in a given namespace encompass the provided role.
    53  func ConfirmNoEscalation(ctx context.Context, ruleResolver AuthorizationRuleResolver, rules []rbacv1.PolicyRule) error {
    54  	ruleResolutionErrors := []error{}
    55  
    56  	user, ok := genericapirequest.UserFrom(ctx)
    57  	if !ok {
    58  		return fmt.Errorf("no user on context")
    59  	}
    60  	namespace, _ := genericapirequest.NamespaceFrom(ctx)
    61  
    62  	ownerRules, err := ruleResolver.RulesFor(user, namespace)
    63  	if err != nil {
    64  		// As per AuthorizationRuleResolver contract, this may return a non fatal error with an incomplete list of policies. Log the error and continue.
    65  		klog.V(1).Infof("non-fatal error getting local rules for %v: %v", user, err)
    66  		ruleResolutionErrors = append(ruleResolutionErrors, err)
    67  	}
    68  
    69  	ownerRightsCover, missingRights := validation.Covers(ownerRules, rules)
    70  	if !ownerRightsCover {
    71  		compactMissingRights := missingRights
    72  		if compact, err := CompactRules(missingRights); err == nil {
    73  			compactMissingRights = compact
    74  		}
    75  
    76  		missingDescriptions := sets.NewString()
    77  		for _, missing := range compactMissingRights {
    78  			missingDescriptions.Insert(rbacv1helpers.CompactString(missing))
    79  		}
    80  
    81  		msg := fmt.Sprintf("user %q (groups=%q) is attempting to grant RBAC permissions not currently held:\n%s", user.GetName(), user.GetGroups(), strings.Join(missingDescriptions.List(), "\n"))
    82  		if len(ruleResolutionErrors) > 0 {
    83  			msg = msg + fmt.Sprintf("; resolution errors: %v", ruleResolutionErrors)
    84  		}
    85  
    86  		return errors.New(msg)
    87  	}
    88  	return nil
    89  }
    90  
    91  type DefaultRuleResolver struct {
    92  	roleGetter               RoleGetter
    93  	roleBindingLister        RoleBindingLister
    94  	clusterRoleGetter        ClusterRoleGetter
    95  	clusterRoleBindingLister ClusterRoleBindingLister
    96  }
    97  
    98  func NewDefaultRuleResolver(roleGetter RoleGetter, roleBindingLister RoleBindingLister, clusterRoleGetter ClusterRoleGetter, clusterRoleBindingLister ClusterRoleBindingLister) *DefaultRuleResolver {
    99  	return &DefaultRuleResolver{roleGetter, roleBindingLister, clusterRoleGetter, clusterRoleBindingLister}
   100  }
   101  
   102  type RoleGetter interface {
   103  	GetRole(namespace, name string) (*rbacv1.Role, error)
   104  }
   105  
   106  type RoleBindingLister interface {
   107  	ListRoleBindings(namespace string) ([]*rbacv1.RoleBinding, error)
   108  }
   109  
   110  type ClusterRoleGetter interface {
   111  	GetClusterRole(name string) (*rbacv1.ClusterRole, error)
   112  }
   113  
   114  type ClusterRoleBindingLister interface {
   115  	ListClusterRoleBindings() ([]*rbacv1.ClusterRoleBinding, error)
   116  }
   117  
   118  func (r *DefaultRuleResolver) RulesFor(user user.Info, namespace string) ([]rbacv1.PolicyRule, error) {
   119  	visitor := &ruleAccumulator{}
   120  	r.VisitRulesFor(user, namespace, visitor.visit)
   121  	return visitor.rules, utilerrors.NewAggregate(visitor.errors)
   122  }
   123  
   124  type ruleAccumulator struct {
   125  	rules  []rbacv1.PolicyRule
   126  	errors []error
   127  }
   128  
   129  func (r *ruleAccumulator) visit(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool {
   130  	if rule != nil {
   131  		r.rules = append(r.rules, *rule)
   132  	}
   133  	if err != nil {
   134  		r.errors = append(r.errors, err)
   135  	}
   136  	return true
   137  }
   138  
   139  func describeSubject(s *rbacv1.Subject, bindingNamespace string) string {
   140  	switch s.Kind {
   141  	case rbacv1.ServiceAccountKind:
   142  		if len(s.Namespace) > 0 {
   143  			return fmt.Sprintf("%s %q", s.Kind, s.Name+"/"+s.Namespace)
   144  		}
   145  		return fmt.Sprintf("%s %q", s.Kind, s.Name+"/"+bindingNamespace)
   146  	default:
   147  		return fmt.Sprintf("%s %q", s.Kind, s.Name)
   148  	}
   149  }
   150  
   151  type clusterRoleBindingDescriber struct {
   152  	binding *rbacv1.ClusterRoleBinding
   153  	subject *rbacv1.Subject
   154  }
   155  
   156  func (d *clusterRoleBindingDescriber) String() string {
   157  	return fmt.Sprintf("ClusterRoleBinding %q of %s %q to %s",
   158  		d.binding.Name,
   159  		d.binding.RoleRef.Kind,
   160  		d.binding.RoleRef.Name,
   161  		describeSubject(d.subject, ""),
   162  	)
   163  }
   164  
   165  type roleBindingDescriber struct {
   166  	binding *rbacv1.RoleBinding
   167  	subject *rbacv1.Subject
   168  }
   169  
   170  func (d *roleBindingDescriber) String() string {
   171  	return fmt.Sprintf("RoleBinding %q of %s %q to %s",
   172  		d.binding.Name+"/"+d.binding.Namespace,
   173  		d.binding.RoleRef.Kind,
   174  		d.binding.RoleRef.Name,
   175  		describeSubject(d.subject, d.binding.Namespace),
   176  	)
   177  }
   178  
   179  func (r *DefaultRuleResolver) VisitRulesFor(user user.Info, namespace string, visitor func(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool) {
   180  	if clusterRoleBindings, err := r.clusterRoleBindingLister.ListClusterRoleBindings(); err != nil {
   181  		if !visitor(nil, nil, err) {
   182  			return
   183  		}
   184  	} else {
   185  		sourceDescriber := &clusterRoleBindingDescriber{}
   186  		for _, clusterRoleBinding := range clusterRoleBindings {
   187  			subjectIndex, applies := appliesTo(user, clusterRoleBinding.Subjects, "")
   188  			if !applies {
   189  				continue
   190  			}
   191  			rules, err := r.GetRoleReferenceRules(clusterRoleBinding.RoleRef, "")
   192  			if err != nil {
   193  				if !visitor(nil, nil, err) {
   194  					return
   195  				}
   196  				continue
   197  			}
   198  			sourceDescriber.binding = clusterRoleBinding
   199  			sourceDescriber.subject = &clusterRoleBinding.Subjects[subjectIndex]
   200  			for i := range rules {
   201  				if !visitor(sourceDescriber, &rules[i], nil) {
   202  					return
   203  				}
   204  			}
   205  		}
   206  	}
   207  
   208  	if len(namespace) > 0 {
   209  		if roleBindings, err := r.roleBindingLister.ListRoleBindings(namespace); err != nil {
   210  			if !visitor(nil, nil, err) {
   211  				return
   212  			}
   213  		} else {
   214  			sourceDescriber := &roleBindingDescriber{}
   215  			for _, roleBinding := range roleBindings {
   216  				subjectIndex, applies := appliesTo(user, roleBinding.Subjects, namespace)
   217  				if !applies {
   218  					continue
   219  				}
   220  				rules, err := r.GetRoleReferenceRules(roleBinding.RoleRef, namespace)
   221  				if err != nil {
   222  					if !visitor(nil, nil, err) {
   223  						return
   224  					}
   225  					continue
   226  				}
   227  				sourceDescriber.binding = roleBinding
   228  				sourceDescriber.subject = &roleBinding.Subjects[subjectIndex]
   229  				for i := range rules {
   230  					if !visitor(sourceDescriber, &rules[i], nil) {
   231  						return
   232  					}
   233  				}
   234  			}
   235  		}
   236  	}
   237  }
   238  
   239  // GetRoleReferenceRules attempts to resolve the RoleBinding or ClusterRoleBinding.
   240  func (r *DefaultRuleResolver) GetRoleReferenceRules(roleRef rbacv1.RoleRef, bindingNamespace string) ([]rbacv1.PolicyRule, error) {
   241  	switch roleRef.Kind {
   242  	case "Role":
   243  		role, err := r.roleGetter.GetRole(bindingNamespace, roleRef.Name)
   244  		if err != nil {
   245  			return nil, err
   246  		}
   247  		return role.Rules, nil
   248  
   249  	case "ClusterRole":
   250  		clusterRole, err := r.clusterRoleGetter.GetClusterRole(roleRef.Name)
   251  		if err != nil {
   252  			return nil, err
   253  		}
   254  		return clusterRole.Rules, nil
   255  
   256  	default:
   257  		return nil, fmt.Errorf("unsupported role reference kind: %q", roleRef.Kind)
   258  	}
   259  }
   260  
   261  // appliesTo returns whether any of the bindingSubjects applies to the specified subject,
   262  // and if true, the index of the first subject that applies
   263  func appliesTo(user user.Info, bindingSubjects []rbacv1.Subject, namespace string) (int, bool) {
   264  	for i, bindingSubject := range bindingSubjects {
   265  		if appliesToUser(user, bindingSubject, namespace) {
   266  			return i, true
   267  		}
   268  	}
   269  	return 0, false
   270  }
   271  
   272  func has(set []string, ele string) bool {
   273  	for _, s := range set {
   274  		if s == ele {
   275  			return true
   276  		}
   277  	}
   278  	return false
   279  }
   280  
   281  func appliesToUser(user user.Info, subject rbacv1.Subject, namespace string) bool {
   282  	switch subject.Kind {
   283  	case rbacv1.UserKind:
   284  		return user.GetName() == subject.Name
   285  
   286  	case rbacv1.GroupKind:
   287  		return has(user.GetGroups(), subject.Name)
   288  
   289  	case rbacv1.ServiceAccountKind:
   290  		// default the namespace to namespace we're working in if its available.  This allows rolebindings that reference
   291  		// SAs in th local namespace to avoid having to qualify them.
   292  		saNamespace := namespace
   293  		if len(subject.Namespace) > 0 {
   294  			saNamespace = subject.Namespace
   295  		}
   296  		if len(saNamespace) == 0 {
   297  			return false
   298  		}
   299  		// use a more efficient comparison for RBAC checking
   300  		return serviceaccount.MatchesUsername(saNamespace, subject.Name, user.GetName())
   301  	default:
   302  		return false
   303  	}
   304  }
   305  
   306  // NewTestRuleResolver returns a rule resolver from lists of role objects.
   307  func NewTestRuleResolver(roles []*rbacv1.Role, roleBindings []*rbacv1.RoleBinding, clusterRoles []*rbacv1.ClusterRole, clusterRoleBindings []*rbacv1.ClusterRoleBinding) (AuthorizationRuleResolver, *StaticRoles) {
   308  	r := StaticRoles{
   309  		roles:               roles,
   310  		roleBindings:        roleBindings,
   311  		clusterRoles:        clusterRoles,
   312  		clusterRoleBindings: clusterRoleBindings,
   313  	}
   314  	return newMockRuleResolver(&r), &r
   315  }
   316  
   317  func newMockRuleResolver(r *StaticRoles) AuthorizationRuleResolver {
   318  	return NewDefaultRuleResolver(r, r, r, r)
   319  }
   320  
   321  // StaticRoles is a rule resolver that resolves from lists of role objects.
   322  type StaticRoles struct {
   323  	roles               []*rbacv1.Role
   324  	roleBindings        []*rbacv1.RoleBinding
   325  	clusterRoles        []*rbacv1.ClusterRole
   326  	clusterRoleBindings []*rbacv1.ClusterRoleBinding
   327  }
   328  
   329  func (r *StaticRoles) GetRole(namespace, name string) (*rbacv1.Role, error) {
   330  	if len(namespace) == 0 {
   331  		return nil, errors.New("must provide namespace when getting role")
   332  	}
   333  	for _, role := range r.roles {
   334  		if role.Namespace == namespace && role.Name == name {
   335  			return role, nil
   336  		}
   337  	}
   338  	return nil, errors.New("role not found")
   339  }
   340  
   341  func (r *StaticRoles) GetClusterRole(name string) (*rbacv1.ClusterRole, error) {
   342  	for _, clusterRole := range r.clusterRoles {
   343  		if clusterRole.Name == name {
   344  			return clusterRole, nil
   345  		}
   346  	}
   347  	return nil, errors.New("clusterrole not found")
   348  }
   349  
   350  func (r *StaticRoles) ListRoleBindings(namespace string) ([]*rbacv1.RoleBinding, error) {
   351  	if len(namespace) == 0 {
   352  		return nil, errors.New("must provide namespace when listing role bindings")
   353  	}
   354  
   355  	roleBindingList := []*rbacv1.RoleBinding{}
   356  	for _, roleBinding := range r.roleBindings {
   357  		if roleBinding.Namespace != namespace {
   358  			continue
   359  		}
   360  		// TODO(ericchiang): need to implement label selectors?
   361  		roleBindingList = append(roleBindingList, roleBinding)
   362  	}
   363  	return roleBindingList, nil
   364  }
   365  
   366  func (r *StaticRoles) ListClusterRoleBindings() ([]*rbacv1.ClusterRoleBinding, error) {
   367  	return r.clusterRoleBindings, nil
   368  }
   369  

View as plain text