123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- /*
- Copyright 2016 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- // Package rbac implements the authorizer.Authorizer interface using roles base access control.
- package rbac
- import (
- "bytes"
- "fmt"
- "k8s.io/klog"
- rbacv1 "k8s.io/api/rbac/v1"
- "k8s.io/apimachinery/pkg/labels"
- utilerrors "k8s.io/apimachinery/pkg/util/errors"
- "k8s.io/apiserver/pkg/authentication/user"
- "k8s.io/apiserver/pkg/authorization/authorizer"
- rbaclisters "k8s.io/client-go/listers/rbac/v1"
- rbacv1helpers "k8s.io/kubernetes/pkg/apis/rbac/v1"
- rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
- )
- type RequestToRuleMapper interface {
- // RulesFor returns all known PolicyRules and any errors that happened while locating those rules.
- // Any rule returned is still valid, since rules are deny by default. If you can pass with the rules
- // 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) ([]rbacv1.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(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool)
- }
- type RBACAuthorizer struct {
- authorizationRuleResolver RequestToRuleMapper
- }
- // authorizingVisitor short-circuits once allowed, and collects any resolution errors encountered
- type authorizingVisitor struct {
- requestAttributes authorizer.Attributes
- allowed bool
- reason string
- errors []error
- }
- func (v *authorizingVisitor) visit(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool {
- if rule != nil && RuleAllows(v.requestAttributes, rule) {
- v.allowed = true
- v.reason = fmt.Sprintf("RBAC: allowed by %s", source.String())
- return false
- }
- if err != nil {
- v.errors = append(v.errors, err)
- }
- return true
- }
- func (r *RBACAuthorizer) Authorize(requestAttributes authorizer.Attributes) (authorizer.Decision, string, error) {
- ruleCheckingVisitor := &authorizingVisitor{requestAttributes: requestAttributes}
- r.authorizationRuleResolver.VisitRulesFor(requestAttributes.GetUser(), requestAttributes.GetNamespace(), ruleCheckingVisitor.visit)
- if ruleCheckingVisitor.allowed {
- return authorizer.DecisionAllow, ruleCheckingVisitor.reason, nil
- }
- // Build a detailed log of the denial.
- // Make the whole block conditional so we don't do a lot of string-building we won't use.
- if klog.V(5) {
- var operation string
- if requestAttributes.IsResourceRequest() {
- b := &bytes.Buffer{}
- b.WriteString(`"`)
- b.WriteString(requestAttributes.GetVerb())
- b.WriteString(`" resource "`)
- b.WriteString(requestAttributes.GetResource())
- if len(requestAttributes.GetAPIGroup()) > 0 {
- b.WriteString(`.`)
- b.WriteString(requestAttributes.GetAPIGroup())
- }
- if len(requestAttributes.GetSubresource()) > 0 {
- b.WriteString(`/`)
- b.WriteString(requestAttributes.GetSubresource())
- }
- b.WriteString(`"`)
- if len(requestAttributes.GetName()) > 0 {
- b.WriteString(` named "`)
- b.WriteString(requestAttributes.GetName())
- b.WriteString(`"`)
- }
- operation = b.String()
- } else {
- operation = fmt.Sprintf("%q nonResourceURL %q", requestAttributes.GetVerb(), requestAttributes.GetPath())
- }
- var scope string
- if ns := requestAttributes.GetNamespace(); len(ns) > 0 {
- scope = fmt.Sprintf("in namespace %q", ns)
- } else {
- scope = "cluster-wide"
- }
- klog.Infof("RBAC DENY: user %q groups %q cannot %s %s", requestAttributes.GetUser().GetName(), requestAttributes.GetUser().GetGroups(), operation, scope)
- }
- reason := ""
- if len(ruleCheckingVisitor.errors) > 0 {
- reason = fmt.Sprintf("RBAC: %v", utilerrors.NewAggregate(ruleCheckingVisitor.errors))
- }
- return authorizer.DecisionNoOpinion, reason, nil
- }
- func (r *RBACAuthorizer) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
- var (
- resourceRules []authorizer.ResourceRuleInfo
- nonResourceRules []authorizer.NonResourceRuleInfo
- )
- policyRules, err := r.authorizationRuleResolver.RulesFor(user, namespace)
- for _, policyRule := range policyRules {
- if len(policyRule.Resources) > 0 {
- r := authorizer.DefaultResourceRuleInfo{
- Verbs: policyRule.Verbs,
- APIGroups: policyRule.APIGroups,
- Resources: policyRule.Resources,
- ResourceNames: policyRule.ResourceNames,
- }
- var resourceRule authorizer.ResourceRuleInfo = &r
- resourceRules = append(resourceRules, resourceRule)
- }
- if len(policyRule.NonResourceURLs) > 0 {
- r := authorizer.DefaultNonResourceRuleInfo{
- Verbs: policyRule.Verbs,
- NonResourceURLs: policyRule.NonResourceURLs,
- }
- var nonResourceRule authorizer.NonResourceRuleInfo = &r
- nonResourceRules = append(nonResourceRules, nonResourceRule)
- }
- }
- return resourceRules, nonResourceRules, false, err
- }
- func New(roles rbacregistryvalidation.RoleGetter, roleBindings rbacregistryvalidation.RoleBindingLister, clusterRoles rbacregistryvalidation.ClusterRoleGetter, clusterRoleBindings rbacregistryvalidation.ClusterRoleBindingLister) *RBACAuthorizer {
- authorizer := &RBACAuthorizer{
- authorizationRuleResolver: rbacregistryvalidation.NewDefaultRuleResolver(
- roles, roleBindings, clusterRoles, clusterRoleBindings,
- ),
- }
- return authorizer
- }
- func RulesAllow(requestAttributes authorizer.Attributes, rules ...rbacv1.PolicyRule) bool {
- for i := range rules {
- if RuleAllows(requestAttributes, &rules[i]) {
- return true
- }
- }
- return false
- }
- func RuleAllows(requestAttributes authorizer.Attributes, rule *rbacv1.PolicyRule) bool {
- if requestAttributes.IsResourceRequest() {
- combinedResource := requestAttributes.GetResource()
- if len(requestAttributes.GetSubresource()) > 0 {
- combinedResource = requestAttributes.GetResource() + "/" + requestAttributes.GetSubresource()
- }
- return rbacv1helpers.VerbMatches(rule, requestAttributes.GetVerb()) &&
- rbacv1helpers.APIGroupMatches(rule, requestAttributes.GetAPIGroup()) &&
- rbacv1helpers.ResourceMatches(rule, combinedResource, requestAttributes.GetSubresource()) &&
- rbacv1helpers.ResourceNameMatches(rule, requestAttributes.GetName())
- }
- return rbacv1helpers.VerbMatches(rule, requestAttributes.GetVerb()) &&
- rbacv1helpers.NonResourceURLMatches(rule, requestAttributes.GetPath())
- }
- type RoleGetter struct {
- Lister rbaclisters.RoleLister
- }
- func (g *RoleGetter) GetRole(namespace, name string) (*rbacv1.Role, error) {
- return g.Lister.Roles(namespace).Get(name)
- }
- type RoleBindingLister struct {
- Lister rbaclisters.RoleBindingLister
- }
- func (l *RoleBindingLister) ListRoleBindings(namespace string) ([]*rbacv1.RoleBinding, error) {
- return l.Lister.RoleBindings(namespace).List(labels.Everything())
- }
- type ClusterRoleGetter struct {
- Lister rbaclisters.ClusterRoleLister
- }
- func (g *ClusterRoleGetter) GetClusterRole(name string) (*rbacv1.ClusterRole, error) {
- return g.Lister.Get(name)
- }
- type ClusterRoleBindingLister struct {
- Lister rbaclisters.ClusterRoleBindingLister
- }
- func (l *ClusterRoleBindingLister) ListClusterRoleBindings() ([]*rbacv1.ClusterRoleBinding, error) {
- return l.Lister.List(labels.Everything())
- }
|