1/*
2Copyright 2016 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package validation
18
19import (
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
37type 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.
53func 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
91type DefaultRuleResolver struct {
92	roleGetter               RoleGetter
93	roleBindingLister        RoleBindingLister
94	clusterRoleGetter        ClusterRoleGetter
95	clusterRoleBindingLister ClusterRoleBindingLister
96}
97
98func NewDefaultRuleResolver(roleGetter RoleGetter, roleBindingLister RoleBindingLister, clusterRoleGetter ClusterRoleGetter, clusterRoleBindingLister ClusterRoleBindingLister) *DefaultRuleResolver {
99	return &DefaultRuleResolver{roleGetter, roleBindingLister, clusterRoleGetter, clusterRoleBindingLister}
100}
101
102type RoleGetter interface {
103	GetRole(namespace, name string) (*rbacv1.Role, error)
104}
105
106type RoleBindingLister interface {
107	ListRoleBindings(namespace string) ([]*rbacv1.RoleBinding, error)
108}
109
110type ClusterRoleGetter interface {
111	GetClusterRole(name string) (*rbacv1.ClusterRole, error)
112}
113
114type ClusterRoleBindingLister interface {
115	ListClusterRoleBindings() ([]*rbacv1.ClusterRoleBinding, error)
116}
117
118func (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
124type ruleAccumulator struct {
125	rules  []rbacv1.PolicyRule
126	errors []error
127}
128
129func (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
139func 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
151type clusterRoleBindingDescriber struct {
152	binding *rbacv1.ClusterRoleBinding
153	subject *rbacv1.Subject
154}
155
156func (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
165type roleBindingDescriber struct {
166	binding *rbacv1.RoleBinding
167	subject *rbacv1.Subject
168}
169
170func (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
179func (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.
240func (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
263func 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
272func 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
281func 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.
307func 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
317func 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.
322type StaticRoles struct {
323	roles               []*rbacv1.Role
324	roleBindings        []*rbacv1.RoleBinding
325	clusterRoles        []*rbacv1.ClusterRole
326	clusterRoleBindings []*rbacv1.ClusterRoleBinding
327}
328
329func (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
341func (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
350func (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
366func (r *StaticRoles) ListClusterRoleBindings() ([]*rbacv1.ClusterRoleBinding, error) {
367	return r.clusterRoleBindings, nil
368}
369