-
Notifications
You must be signed in to change notification settings - Fork 38.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Switch to pointer to policy rule, visit and short circuit during authorization #44449
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,6 +39,10 @@ type AuthorizationRuleResolver interface { | |
// PolicyRules may not be complete, but it contains all retrievable rules. This is done because policy rules are purely additive and policy determinations | ||
// can be made on the basis of those rules that are found. | ||
RulesFor(user user.Info, namespace string) ([]rbac.PolicyRule, error) | ||
|
||
// VisitRulesFor invokes visitor() with each rule that applies to a given user in a given namespace, and each error encountered resolving those rules. | ||
// If visitor() returns false, visiting is short-circuited. | ||
VisitRulesFor(user user.Info, namespace string, visitor func(rule *rbac.PolicyRule, err error) bool) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A word about the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. other than it being a resolution error, not sure what else to say There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I meant the |
||
} | ||
|
||
// ConfirmNoEscalation determines if the roles for a given user in a given namespace encompass the provided role. | ||
|
@@ -93,46 +97,76 @@ type ClusterRoleBindingLister interface { | |
} | ||
|
||
func (r *DefaultRuleResolver) RulesFor(user user.Info, namespace string) ([]rbac.PolicyRule, error) { | ||
policyRules := []rbac.PolicyRule{} | ||
errorlist := []error{} | ||
visitor := &ruleAccumulator{} | ||
r.VisitRulesFor(user, namespace, visitor.visit) | ||
return visitor.rules, utilerrors.NewAggregate(visitor.errors) | ||
} | ||
|
||
if clusterRoleBindings, err := r.clusterRoleBindingLister.ListClusterRoleBindings(); err != nil { | ||
errorlist = append(errorlist, err) | ||
type ruleAccumulator struct { | ||
rules []rbac.PolicyRule | ||
errors []error | ||
} | ||
|
||
func (r *ruleAccumulator) visit(rule *rbac.PolicyRule, err error) bool { | ||
if rule != nil { | ||
r.rules = append(r.rules, *rule) | ||
} | ||
if err != nil { | ||
r.errors = append(r.errors, err) | ||
} | ||
return true | ||
} | ||
|
||
func (r *DefaultRuleResolver) VisitRulesFor(user user.Info, namespace string, visitor func(rule *rbac.PolicyRule, err error) bool) { | ||
if clusterRoleBindings, err := r.clusterRoleBindingLister.ListClusterRoleBindings(); err != nil { | ||
if !visitor(nil, err) { | ||
return | ||
} | ||
} else { | ||
for _, clusterRoleBinding := range clusterRoleBindings { | ||
if !appliesTo(user, clusterRoleBinding.Subjects, "") { | ||
continue | ||
} | ||
rules, err := r.GetRoleReferenceRules(clusterRoleBinding.RoleRef, "") | ||
if err != nil { | ||
errorlist = append(errorlist, err) | ||
if !visitor(nil, err) { | ||
return | ||
} | ||
continue | ||
} | ||
policyRules = append(policyRules, rules...) | ||
for i := range rules { | ||
if !visitor(&rules[i], nil) { | ||
return | ||
} | ||
} | ||
} | ||
} | ||
|
||
if len(namespace) > 0 { | ||
if roleBindings, err := r.roleBindingLister.ListRoleBindings(namespace); err != nil { | ||
errorlist = append(errorlist, err) | ||
|
||
if !visitor(nil, err) { | ||
return | ||
} | ||
} else { | ||
for _, roleBinding := range roleBindings { | ||
if !appliesTo(user, roleBinding.Subjects, namespace) { | ||
continue | ||
} | ||
rules, err := r.GetRoleReferenceRules(roleBinding.RoleRef, namespace) | ||
if err != nil { | ||
errorlist = append(errorlist, err) | ||
if !visitor(nil, err) { | ||
return | ||
} | ||
continue | ||
} | ||
policyRules = append(policyRules, rules...) | ||
for i := range rules { | ||
if !visitor(&rules[i], nil) { | ||
return | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
return policyRules, utilerrors.NewAggregate(errorlist) | ||
} | ||
|
||
// GetRoleReferenceRules attempts to resolve the RoleBinding or ClusterRoleBinding. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ import ( | |
|
||
"bytes" | ||
|
||
utilerrors "k8s.io/apimachinery/pkg/util/errors" | ||
"k8s.io/apiserver/pkg/authentication/user" | ||
"k8s.io/apiserver/pkg/authorization/authorizer" | ||
"k8s.io/kubernetes/pkg/apis/rbac" | ||
|
@@ -36,15 +37,41 @@ type RequestToRuleMapper interface { | |
// supplied, you do not have to fail the request. If you cannot, you should indicate the error along | ||
// with your denial. | ||
RulesFor(subject user.Info, namespace string) ([]rbac.PolicyRule, error) | ||
|
||
// VisitRulesFor invokes visitor() with each rule that applies to a given user in a given namespace, | ||
// and each error encountered resolving those rules. Rule may be nil if err is non-nil. | ||
// If visitor() returns false, visiting is short-circuited. | ||
VisitRulesFor(user user.Info, namespace string, visitor func(rule *rbac.PolicyRule, err error) bool) | ||
} | ||
|
||
type RBACAuthorizer struct { | ||
authorizationRuleResolver RequestToRuleMapper | ||
} | ||
|
||
// authorizingVisitor short-circuits once allowed, and collects any resolution errors encountered | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. correct grammar?! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not seeing the mistake... |
||
type authorizingVisitor struct { | ||
requestAttributes authorizer.Attributes | ||
|
||
allowed bool | ||
errors []error | ||
} | ||
|
||
func (v *authorizingVisitor) visit(rule *rbac.PolicyRule, err error) bool { | ||
if rule != nil && RuleAllows(v.requestAttributes, rule) { | ||
v.allowed = true | ||
return false | ||
} | ||
if err != nil { | ||
v.errors = append(v.errors, err) | ||
} | ||
return true | ||
} | ||
|
||
func (r *RBACAuthorizer) Authorize(requestAttributes authorizer.Attributes) (bool, string, error) { | ||
rules, ruleResolutionError := r.authorizationRuleResolver.RulesFor(requestAttributes.GetUser(), requestAttributes.GetNamespace()) | ||
if RulesAllow(requestAttributes, rules...) { | ||
ruleCheckingVisitor := &authorizingVisitor{requestAttributes: requestAttributes} | ||
|
||
r.authorizationRuleResolver.VisitRulesFor(requestAttributes.GetUser(), requestAttributes.GetNamespace(), ruleCheckingVisitor.visit) | ||
if ruleCheckingVisitor.allowed { | ||
return true, "", nil | ||
} | ||
|
||
|
@@ -88,8 +115,8 @@ func (r *RBACAuthorizer) Authorize(requestAttributes authorizer.Attributes) (boo | |
} | ||
|
||
reason := "" | ||
if ruleResolutionError != nil { | ||
reason = fmt.Sprintf("%v", ruleResolutionError) | ||
if len(ruleCheckingVisitor.errors) > 0 { | ||
reason = fmt.Sprintf("%v", utilerrors.NewAggregate(ruleCheckingVisitor.errors)) | ||
} | ||
return false, reason, nil | ||
} | ||
|
@@ -104,16 +131,16 @@ func New(roles rbacregistryvalidation.RoleGetter, roleBindings rbacregistryvalid | |
} | ||
|
||
func RulesAllow(requestAttributes authorizer.Attributes, rules ...rbac.PolicyRule) bool { | ||
for _, rule := range rules { | ||
if RuleAllows(requestAttributes, rule) { | ||
for i := range rules { | ||
if RuleAllows(requestAttributes, &rules[i]) { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} | ||
|
||
func RuleAllows(requestAttributes authorizer.Attributes, rule rbac.PolicyRule) bool { | ||
func RuleAllows(requestAttributes authorizer.Attributes, rule *rbac.PolicyRule) bool { | ||
if requestAttributes.IsResourceRequest() { | ||
resource := requestAttributes.GetResource() | ||
if len(requestAttributes.GetSubresource()) > 0 { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type the function please.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can't without adding a package dependency between
pkg/registry/rbac/validation
andplugin/pkg/auth/authorizer/rbac
, two level interface problem