diff options
| author | vincentgoat <linguohui1@huawei.com> | 2023-04-13 16:58:31 +0800 |
|---|---|---|
| committer | vincentgoat <linguohui1@huawei.com> | 2023-05-29 21:28:21 +0800 |
| commit | a2ac5d60e9a4405729af640c95eeeb35200d3c54 (patch) | |
| tree | 68165401a4704a7f808659da5798d336dbd80d9f | |
| parent | udpate controller runtime vendor fake (diff) | |
| download | kubeedge-a2ac5d60e9a4405729af640c95eeeb35200d3c54.tar.gz | |
add vendor of K8s plugin auth
Signed-off-by: vincentgoat <linguohui1@huawei.com>
8 files changed, 1021 insertions, 1 deletions
@@ -58,6 +58,7 @@ require ( require ( github.com/pkg/errors v0.9.1 + gopkg.in/square/go-jose.v2 v2.5.1 gopkg.in/yaml.v3 v3.0.1 ) @@ -246,7 +247,6 @@ require ( gopkg.in/gorp.v1 v1.7.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect - gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect gopkg.in/warnings.v0 v0.1.1 // indirect diff --git a/vendor/k8s.io/component-helpers/auth/rbac/validation/policy_comparator.go b/vendor/k8s.io/component-helpers/auth/rbac/validation/policy_comparator.go new file mode 100644 index 000000000..7a0268b5e --- /dev/null +++ b/vendor/k8s.io/component-helpers/auth/rbac/validation/policy_comparator.go @@ -0,0 +1,173 @@ +/* +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 validation + +import ( + "strings" + + rbacv1 "k8s.io/api/rbac/v1" +) + +// Covers determines whether or not the ownerRules cover the servantRules in terms of allowed actions. +// It returns whether or not the ownerRules cover and a list of the rules that the ownerRules do not cover. +func Covers(ownerRules, servantRules []rbacv1.PolicyRule) (bool, []rbacv1.PolicyRule) { + // 1. Break every servantRule into individual rule tuples: group, verb, resource, resourceName + // 2. Compare the mini-rules against each owner rule. Because the breakdown is down to the most atomic level, we're guaranteed that each mini-servant rule will be either fully covered or not covered by a single owner rule + // 3. Any left over mini-rules means that we are not covered and we have a nice list of them. + // TODO: it might be nice to collapse the list down into something more human readable + + subrules := []rbacv1.PolicyRule{} + for _, servantRule := range servantRules { + subrules = append(subrules, BreakdownRule(servantRule)...) + } + + uncoveredRules := []rbacv1.PolicyRule{} + for _, subrule := range subrules { + covered := false + for _, ownerRule := range ownerRules { + if ruleCovers(ownerRule, subrule) { + covered = true + break + } + } + + if !covered { + uncoveredRules = append(uncoveredRules, subrule) + } + } + + return (len(uncoveredRules) == 0), uncoveredRules +} + +// BreadownRule takes a rule and builds an equivalent list of rules that each have at most one verb, one +// resource, and one resource name +func BreakdownRule(rule rbacv1.PolicyRule) []rbacv1.PolicyRule { + subrules := []rbacv1.PolicyRule{} + for _, group := range rule.APIGroups { + for _, resource := range rule.Resources { + for _, verb := range rule.Verbs { + if len(rule.ResourceNames) > 0 { + for _, resourceName := range rule.ResourceNames { + subrules = append(subrules, rbacv1.PolicyRule{APIGroups: []string{group}, Resources: []string{resource}, Verbs: []string{verb}, ResourceNames: []string{resourceName}}) + } + + } else { + subrules = append(subrules, rbacv1.PolicyRule{APIGroups: []string{group}, Resources: []string{resource}, Verbs: []string{verb}}) + } + + } + } + } + + // Non-resource URLs are unique because they only combine with verbs. + for _, nonResourceURL := range rule.NonResourceURLs { + for _, verb := range rule.Verbs { + subrules = append(subrules, rbacv1.PolicyRule{NonResourceURLs: []string{nonResourceURL}, Verbs: []string{verb}}) + } + } + + return subrules +} + +func has(set []string, ele string) bool { + for _, s := range set { + if s == ele { + return true + } + } + return false +} + +func hasAll(set, contains []string) bool { + owning := make(map[string]struct{}, len(set)) + for _, ele := range set { + owning[ele] = struct{}{} + } + for _, ele := range contains { + if _, ok := owning[ele]; !ok { + return false + } + } + return true +} + +func resourceCoversAll(setResources, coversResources []string) bool { + // if we have a star or an exact match on all resources, then we match + if has(setResources, rbacv1.ResourceAll) || hasAll(setResources, coversResources) { + return true + } + + for _, path := range coversResources { + // if we have an exact match, then we match. + if has(setResources, path) { + continue + } + // if we're not a subresource, then we definitely don't match. fail. + if !strings.Contains(path, "/") { + return false + } + tokens := strings.SplitN(path, "/", 2) + resourceToCheck := "*/" + tokens[1] + if !has(setResources, resourceToCheck) { + return false + } + } + + return true +} + +func nonResourceURLsCoversAll(set, covers []string) bool { + for _, path := range covers { + covered := false + for _, owner := range set { + if nonResourceURLCovers(owner, path) { + covered = true + break + } + } + if !covered { + return false + } + } + return true +} + +func nonResourceURLCovers(ownerPath, subPath string) bool { + if ownerPath == subPath { + return true + } + return strings.HasSuffix(ownerPath, "*") && strings.HasPrefix(subPath, strings.TrimRight(ownerPath, "*")) +} + +// ruleCovers determines whether the ownerRule (which may have multiple verbs, resources, and resourceNames) covers +// the subrule (which may only contain at most one verb, resource, and resourceName) +func ruleCovers(ownerRule, subRule rbacv1.PolicyRule) bool { + verbMatches := has(ownerRule.Verbs, rbacv1.VerbAll) || hasAll(ownerRule.Verbs, subRule.Verbs) + groupMatches := has(ownerRule.APIGroups, rbacv1.APIGroupAll) || hasAll(ownerRule.APIGroups, subRule.APIGroups) + resourceMatches := resourceCoversAll(ownerRule.Resources, subRule.Resources) + nonResourceURLMatches := nonResourceURLsCoversAll(ownerRule.NonResourceURLs, subRule.NonResourceURLs) + + resourceNameMatches := false + + if len(subRule.ResourceNames) == 0 { + resourceNameMatches = (len(ownerRule.ResourceNames) == 0) + } else { + resourceNameMatches = (len(ownerRule.ResourceNames) == 0) || hasAll(ownerRule.ResourceNames, subRule.ResourceNames) + } + + return verbMatches && groupMatches && resourceMatches && resourceNameMatches && nonResourceURLMatches +} diff --git a/vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/internal_version_adapter.go b/vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/internal_version_adapter.go new file mode 100644 index 000000000..bfb57242d --- /dev/null +++ b/vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/internal_version_adapter.go @@ -0,0 +1,39 @@ +/* +Copyright 2018 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 validation + +import ( + "context" + + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/kubernetes/pkg/apis/rbac" + rbacv1helpers "k8s.io/kubernetes/pkg/apis/rbac/v1" +) + +func ConfirmNoEscalationInternal(ctx context.Context, ruleResolver AuthorizationRuleResolver, inRules []rbac.PolicyRule) error { + rules := []rbacv1.PolicyRule{} + for i := range inRules { + v1Rule := rbacv1.PolicyRule{} + err := rbacv1helpers.Convert_rbac_PolicyRule_To_v1_PolicyRule(&inRules[i], &v1Rule, nil) + if err != nil { + return err + } + rules = append(rules, v1Rule) + } + + return ConfirmNoEscalation(ctx, ruleResolver, rules) +} diff --git a/vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/policy_compact.go b/vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/policy_compact.go new file mode 100644 index 000000000..182657b1c --- /dev/null +++ b/vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/policy_compact.go @@ -0,0 +1,89 @@ +/* +Copyright 2017 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 validation + +import ( + "reflect" + + rbacv1 "k8s.io/api/rbac/v1" +) + +type simpleResource struct { + Group string + Resource string + ResourceNameExist bool + ResourceName string +} + +// CompactRules combines rules that contain a single APIGroup/Resource, differ only by verb, and contain no other attributes. +// this is a fast check, and works well with the decomposed "missing rules" list from a Covers check. +func CompactRules(rules []rbacv1.PolicyRule) ([]rbacv1.PolicyRule, error) { + compacted := make([]rbacv1.PolicyRule, 0, len(rules)) + + simpleRules := map[simpleResource]*rbacv1.PolicyRule{} + for _, rule := range rules { + if resource, isSimple := isSimpleResourceRule(&rule); isSimple { + if existingRule, ok := simpleRules[resource]; ok { + // Add the new verbs to the existing simple resource rule + if existingRule.Verbs == nil { + existingRule.Verbs = []string{} + } + existingRule.Verbs = append(existingRule.Verbs, rule.Verbs...) + } else { + // Copy the rule to accumulate matching simple resource rules into + simpleRules[resource] = rule.DeepCopy() + } + } else { + compacted = append(compacted, rule) + } + } + + // Once we've consolidated the simple resource rules, add them to the compacted list + for _, simpleRule := range simpleRules { + compacted = append(compacted, *simpleRule) + } + + return compacted, nil +} + +// isSimpleResourceRule returns true if the given rule contains verbs, a single resource, a single API group, at most one Resource Name, and no other values +func isSimpleResourceRule(rule *rbacv1.PolicyRule) (simpleResource, bool) { + resource := simpleResource{} + + // If we have "complex" rule attributes, return early without allocations or expensive comparisons + if len(rule.ResourceNames) > 1 || len(rule.NonResourceURLs) > 0 { + return resource, false + } + // If we have multiple api groups or resources, return early + if len(rule.APIGroups) != 1 || len(rule.Resources) != 1 { + return resource, false + } + + // Test if this rule only contains APIGroups/Resources/Verbs/ResourceNames + simpleRule := &rbacv1.PolicyRule{APIGroups: rule.APIGroups, Resources: rule.Resources, Verbs: rule.Verbs, ResourceNames: rule.ResourceNames} + if !reflect.DeepEqual(simpleRule, rule) { + return resource, false + } + + if len(rule.ResourceNames) == 0 { + resource = simpleResource{Group: rule.APIGroups[0], Resource: rule.Resources[0], ResourceNameExist: false} + } else { + resource = simpleResource{Group: rule.APIGroups[0], Resource: rule.Resources[0], ResourceNameExist: true, ResourceName: rule.ResourceNames[0]} + } + + return resource, true +} diff --git a/vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/rule.go b/vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/rule.go new file mode 100644 index 000000000..603f56afb --- /dev/null +++ b/vendor/k8s.io/kubernetes/pkg/registry/rbac/validation/rule.go @@ -0,0 +1,368 @@ +/* +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 validation + +import ( + "context" + "errors" + "fmt" + "strings" + + "k8s.io/klog/v2" + + rbacv1 "k8s.io/api/rbac/v1" + utilerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apiserver/pkg/authentication/serviceaccount" + "k8s.io/apiserver/pkg/authentication/user" + genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/component-helpers/auth/rbac/validation" + rbacv1helpers "k8s.io/kubernetes/pkg/apis/rbac/v1" +) + +type AuthorizationRuleResolver interface { + // GetRoleReferenceRules attempts to resolve the role reference of a RoleBinding or ClusterRoleBinding. The passed namespace should be the namespace + // of the role binding, the empty string if a cluster role binding. + GetRoleReferenceRules(roleRef rbacv1.RoleRef, namespace string) ([]rbacv1.PolicyRule, error) + + // 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 + // 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) ([]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. + // 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) +} + +// ConfirmNoEscalation determines if the roles for a given user in a given namespace encompass the provided role. +func ConfirmNoEscalation(ctx context.Context, ruleResolver AuthorizationRuleResolver, rules []rbacv1.PolicyRule) error { + ruleResolutionErrors := []error{} + + user, ok := genericapirequest.UserFrom(ctx) + if !ok { + return fmt.Errorf("no user on context") + } + namespace, _ := genericapirequest.NamespaceFrom(ctx) + + ownerRules, err := ruleResolver.RulesFor(user, namespace) + if err != nil { + // As per AuthorizationRuleResolver contract, this may return a non fatal error with an incomplete list of policies. Log the error and continue. + klog.V(1).Infof("non-fatal error getting local rules for %v: %v", user, err) + ruleResolutionErrors = append(ruleResolutionErrors, err) + } + + ownerRightsCover, missingRights := validation.Covers(ownerRules, rules) + if !ownerRightsCover { + compactMissingRights := missingRights + if compact, err := CompactRules(missingRights); err == nil { + compactMissingRights = compact + } + + missingDescriptions := sets.NewString() + for _, missing := range compactMissingRights { + missingDescriptions.Insert(rbacv1helpers.CompactString(missing)) + } + + 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")) + if len(ruleResolutionErrors) > 0 { + msg = msg + fmt.Sprintf("; resolution errors: %v", ruleResolutionErrors) + } + + return errors.New(msg) + } + return nil +} + +type DefaultRuleResolver struct { + roleGetter RoleGetter + roleBindingLister RoleBindingLister + clusterRoleGetter ClusterRoleGetter + clusterRoleBindingLister ClusterRoleBindingLister +} + +func NewDefaultRuleResolver(roleGetter RoleGetter, roleBindingLister RoleBindingLister, clusterRoleGetter ClusterRoleGetter, clusterRoleBindingLister ClusterRoleBindingLister) *DefaultRuleResolver { + return &DefaultRuleResolver{roleGetter, roleBindingLister, clusterRoleGetter, clusterRoleBindingLister} +} + +type RoleGetter interface { + GetRole(namespace, name string) (*rbacv1.Role, error) +} + +type RoleBindingLister interface { + ListRoleBindings(namespace string) ([]*rbacv1.RoleBinding, error) +} + +type ClusterRoleGetter interface { + GetClusterRole(name string) (*rbacv1.ClusterRole, error) +} + +type ClusterRoleBindingLister interface { + ListClusterRoleBindings() ([]*rbacv1.ClusterRoleBinding, error) +} + +func (r *DefaultRuleResolver) RulesFor(user user.Info, namespace string) ([]rbacv1.PolicyRule, error) { + visitor := &ruleAccumulator{} + r.VisitRulesFor(user, namespace, visitor.visit) + return visitor.rules, utilerrors.NewAggregate(visitor.errors) +} + +type ruleAccumulator struct { + rules []rbacv1.PolicyRule + errors []error +} + +func (r *ruleAccumulator) visit(source fmt.Stringer, rule *rbacv1.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 describeSubject(s *rbacv1.Subject, bindingNamespace string) string { + switch s.Kind { + case rbacv1.ServiceAccountKind: + if len(s.Namespace) > 0 { + return fmt.Sprintf("%s %q", s.Kind, s.Name+"/"+s.Namespace) + } + return fmt.Sprintf("%s %q", s.Kind, s.Name+"/"+bindingNamespace) + default: + return fmt.Sprintf("%s %q", s.Kind, s.Name) + } +} + +type clusterRoleBindingDescriber struct { + binding *rbacv1.ClusterRoleBinding + subject *rbacv1.Subject +} + +func (d *clusterRoleBindingDescriber) String() string { + return fmt.Sprintf("ClusterRoleBinding %q of %s %q to %s", + d.binding.Name, + d.binding.RoleRef.Kind, + d.binding.RoleRef.Name, + describeSubject(d.subject, ""), + ) +} + +type roleBindingDescriber struct { + binding *rbacv1.RoleBinding + subject *rbacv1.Subject +} + +func (d *roleBindingDescriber) String() string { + return fmt.Sprintf("RoleBinding %q of %s %q to %s", + d.binding.Name+"/"+d.binding.Namespace, + d.binding.RoleRef.Kind, + d.binding.RoleRef.Name, + describeSubject(d.subject, d.binding.Namespace), + ) +} + +func (r *DefaultRuleResolver) VisitRulesFor(user user.Info, namespace string, visitor func(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool) { + if clusterRoleBindings, err := r.clusterRoleBindingLister.ListClusterRoleBindings(); err != nil { + if !visitor(nil, nil, err) { + return + } + } else { + sourceDescriber := &clusterRoleBindingDescriber{} + for _, clusterRoleBinding := range clusterRoleBindings { + subjectIndex, applies := appliesTo(user, clusterRoleBinding.Subjects, "") + if !applies { + continue + } + rules, err := r.GetRoleReferenceRules(clusterRoleBinding.RoleRef, "") + if err != nil { + if !visitor(nil, nil, err) { + return + } + continue + } + sourceDescriber.binding = clusterRoleBinding + sourceDescriber.subject = &clusterRoleBinding.Subjects[subjectIndex] + for i := range rules { + if !visitor(sourceDescriber, &rules[i], nil) { + return + } + } + } + } + + if len(namespace) > 0 { + if roleBindings, err := r.roleBindingLister.ListRoleBindings(namespace); err != nil { + if !visitor(nil, nil, err) { + return + } + } else { + sourceDescriber := &roleBindingDescriber{} + for _, roleBinding := range roleBindings { + subjectIndex, applies := appliesTo(user, roleBinding.Subjects, namespace) + if !applies { + continue + } + rules, err := r.GetRoleReferenceRules(roleBinding.RoleRef, namespace) + if err != nil { + if !visitor(nil, nil, err) { + return + } + continue + } + sourceDescriber.binding = roleBinding + sourceDescriber.subject = &roleBinding.Subjects[subjectIndex] + for i := range rules { + if !visitor(sourceDescriber, &rules[i], nil) { + return + } + } + } + } + } +} + +// GetRoleReferenceRules attempts to resolve the RoleBinding or ClusterRoleBinding. +func (r *DefaultRuleResolver) GetRoleReferenceRules(roleRef rbacv1.RoleRef, bindingNamespace string) ([]rbacv1.PolicyRule, error) { + switch roleRef.Kind { + case "Role": + role, err := r.roleGetter.GetRole(bindingNamespace, roleRef.Name) + if err != nil { + return nil, err + } + return role.Rules, nil + + case "ClusterRole": + clusterRole, err := r.clusterRoleGetter.GetClusterRole(roleRef.Name) + if err != nil { + return nil, err + } + return clusterRole.Rules, nil + + default: + return nil, fmt.Errorf("unsupported role reference kind: %q", roleRef.Kind) + } +} + +// appliesTo returns whether any of the bindingSubjects applies to the specified subject, +// and if true, the index of the first subject that applies +func appliesTo(user user.Info, bindingSubjects []rbacv1.Subject, namespace string) (int, bool) { + for i, bindingSubject := range bindingSubjects { + if appliesToUser(user, bindingSubject, namespace) { + return i, true + } + } + return 0, false +} + +func has(set []string, ele string) bool { + for _, s := range set { + if s == ele { + return true + } + } + return false +} + +func appliesToUser(user user.Info, subject rbacv1.Subject, namespace string) bool { + switch subject.Kind { + case rbacv1.UserKind: + return user.GetName() == subject.Name + + case rbacv1.GroupKind: + return has(user.GetGroups(), subject.Name) + + case rbacv1.ServiceAccountKind: + // default the namespace to namespace we're working in if its available. This allows rolebindings that reference + // SAs in th local namespace to avoid having to qualify them. + saNamespace := namespace + if len(subject.Namespace) > 0 { + saNamespace = subject.Namespace + } + if len(saNamespace) == 0 { + return false + } + // use a more efficient comparison for RBAC checking + return serviceaccount.MatchesUsername(saNamespace, subject.Name, user.GetName()) + default: + return false + } +} + +// NewTestRuleResolver returns a rule resolver from lists of role objects. +func NewTestRuleResolver(roles []*rbacv1.Role, roleBindings []*rbacv1.RoleBinding, clusterRoles []*rbacv1.ClusterRole, clusterRoleBindings []*rbacv1.ClusterRoleBinding) (AuthorizationRuleResolver, *StaticRoles) { + r := StaticRoles{ + roles: roles, + roleBindings: roleBindings, + clusterRoles: clusterRoles, + clusterRoleBindings: clusterRoleBindings, + } + return newMockRuleResolver(&r), &r +} + +func newMockRuleResolver(r *StaticRoles) AuthorizationRuleResolver { + return NewDefaultRuleResolver(r, r, r, r) +} + +// StaticRoles is a rule resolver that resolves from lists of role objects. +type StaticRoles struct { + roles []*rbacv1.Role + roleBindings []*rbacv1.RoleBinding + clusterRoles []*rbacv1.ClusterRole + clusterRoleBindings []*rbacv1.ClusterRoleBinding +} + +func (r *StaticRoles) GetRole(namespace, name string) (*rbacv1.Role, error) { + if len(namespace) == 0 { + return nil, errors.New("must provide namespace when getting role") + } + for _, role := range r.roles { + if role.Namespace == namespace && role.Name == name { + return role, nil + } + } + return nil, errors.New("role not found") +} + +func (r *StaticRoles) GetClusterRole(name string) (*rbacv1.ClusterRole, error) { + for _, clusterRole := range r.clusterRoles { + if clusterRole.Name == name { + return clusterRole, nil + } + } + return nil, errors.New("clusterrole not found") +} + +func (r *StaticRoles) ListRoleBindings(namespace string) ([]*rbacv1.RoleBinding, error) { + if len(namespace) == 0 { + return nil, errors.New("must provide namespace when listing role bindings") + } + + roleBindingList := []*rbacv1.RoleBinding{} + for _, roleBinding := range r.roleBindings { + if roleBinding.Namespace != namespace { + continue + } + // TODO(ericchiang): need to implement label selectors? + roleBindingList = append(roleBindingList, roleBinding) + } + return roleBindingList, nil +} + +func (r *StaticRoles) ListClusterRoleBindings() ([]*rbacv1.ClusterRoleBinding, error) { + return r.clusterRoleBindings, nil +} diff --git a/vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/rbac.go b/vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/rbac.go new file mode 100644 index 000000000..4f25d1372 --- /dev/null +++ b/vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/rbac.go @@ -0,0 +1,225 @@ +/* +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" + "context" + "fmt" + + "k8s.io/klog/v2" + + 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(ctx context.Context, 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).Enabled() { + 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: no rules authorize user %q with groups %q to %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()) +} diff --git a/vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/subject_locator.go b/vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/subject_locator.go new file mode 100644 index 000000000..cdd327e5b --- /dev/null +++ b/vendor/k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/subject_locator.go @@ -0,0 +1,123 @@ +/* +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 ( + rbacv1 "k8s.io/api/rbac/v1" + utilerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apiserver/pkg/authentication/user" + "k8s.io/apiserver/pkg/authorization/authorizer" + rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation" +) + +type RoleToRuleMapper interface { + // GetRoleReferenceRules attempts to resolve the role reference of a RoleBinding or ClusterRoleBinding. The passed namespace should be the namespace + // of the role binding, the empty string if a cluster role binding. + GetRoleReferenceRules(roleRef rbacv1.RoleRef, namespace string) ([]rbacv1.PolicyRule, error) +} + +type SubjectLocator interface { + AllowedSubjects(attributes authorizer.Attributes) ([]rbacv1.Subject, error) +} + +var _ = SubjectLocator(&SubjectAccessEvaluator{}) + +type SubjectAccessEvaluator struct { + superUser string + + roleBindingLister rbacregistryvalidation.RoleBindingLister + clusterRoleBindingLister rbacregistryvalidation.ClusterRoleBindingLister + roleToRuleMapper RoleToRuleMapper +} + +func NewSubjectAccessEvaluator(roles rbacregistryvalidation.RoleGetter, roleBindings rbacregistryvalidation.RoleBindingLister, clusterRoles rbacregistryvalidation.ClusterRoleGetter, clusterRoleBindings rbacregistryvalidation.ClusterRoleBindingLister, superUser string) *SubjectAccessEvaluator { + subjectLocator := &SubjectAccessEvaluator{ + superUser: superUser, + roleBindingLister: roleBindings, + clusterRoleBindingLister: clusterRoleBindings, + roleToRuleMapper: rbacregistryvalidation.NewDefaultRuleResolver( + roles, roleBindings, clusterRoles, clusterRoleBindings, + ), + } + return subjectLocator +} + +// AllowedSubjects returns the subjects that can perform an action and any errors encountered while computing the list. +// It is possible to have both subjects and errors returned if some rolebindings couldn't be resolved, but others could be. +func (r *SubjectAccessEvaluator) AllowedSubjects(requestAttributes authorizer.Attributes) ([]rbacv1.Subject, error) { + subjects := []rbacv1.Subject{{Kind: rbacv1.GroupKind, APIGroup: rbacv1.GroupName, Name: user.SystemPrivilegedGroup}} + if len(r.superUser) > 0 { + subjects = append(subjects, rbacv1.Subject{Kind: rbacv1.UserKind, APIGroup: rbacv1.GroupName, Name: r.superUser}) + } + errorlist := []error{} + + if clusterRoleBindings, err := r.clusterRoleBindingLister.ListClusterRoleBindings(); err != nil { + errorlist = append(errorlist, err) + + } else { + for _, clusterRoleBinding := range clusterRoleBindings { + rules, err := r.roleToRuleMapper.GetRoleReferenceRules(clusterRoleBinding.RoleRef, "") + if err != nil { + // if we have an error, just keep track of it and keep processing. Since rules are additive, + // missing a reference is bad, but we can continue with other rolebindings and still have a list + // that does not contain any invalid values + errorlist = append(errorlist, err) + } + if RulesAllow(requestAttributes, rules...) { + subjects = append(subjects, clusterRoleBinding.Subjects...) + } + } + } + + if namespace := requestAttributes.GetNamespace(); len(namespace) > 0 { + if roleBindings, err := r.roleBindingLister.ListRoleBindings(namespace); err != nil { + errorlist = append(errorlist, err) + + } else { + for _, roleBinding := range roleBindings { + rules, err := r.roleToRuleMapper.GetRoleReferenceRules(roleBinding.RoleRef, namespace) + if err != nil { + // if we have an error, just keep track of it and keep processing. Since rules are additive, + // missing a reference is bad, but we can continue with other rolebindings and still have a list + // that does not contain any invalid values + errorlist = append(errorlist, err) + } + if RulesAllow(requestAttributes, rules...) { + subjects = append(subjects, roleBinding.Subjects...) + } + } + } + } + + dedupedSubjects := []rbacv1.Subject{} + for _, subject := range subjects { + found := false + for _, curr := range dedupedSubjects { + if curr == subject { + found = true + break + } + } + + if !found { + dedupedSubjects = append(dedupedSubjects, subject) + } + } + + return subjects, utilerrors.NewAggregate(errorlist) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index c9efbeb0e..ab6d0cb47 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1989,6 +1989,7 @@ k8s.io/component-base/version/verflag # k8s.io/component-helpers v0.0.0 => github.com/kubeedge/kubernetes/staging/src/k8s.io/component-helpers v1.23.15-kubeedge1 ## explicit; go 1.16 k8s.io/component-helpers/apimachinery/lease +k8s.io/component-helpers/auth/rbac/validation k8s.io/component-helpers/node/util k8s.io/component-helpers/node/util/sysctl k8s.io/component-helpers/scheduling/corev1 @@ -2320,6 +2321,7 @@ k8s.io/kubernetes/pkg/proxy/metaproxier k8s.io/kubernetes/pkg/proxy/metrics k8s.io/kubernetes/pkg/proxy/util k8s.io/kubernetes/pkg/proxy/util/iptables +k8s.io/kubernetes/pkg/registry/rbac/validation k8s.io/kubernetes/pkg/scheduler/apis/config k8s.io/kubernetes/pkg/scheduler/apis/config/v1beta2 k8s.io/kubernetes/pkg/scheduler/apis/config/v1beta3 @@ -2389,6 +2391,7 @@ k8s.io/kubernetes/pkg/volume/util/volumepathhandler k8s.io/kubernetes/pkg/volume/validation k8s.io/kubernetes/pkg/windows/service k8s.io/kubernetes/plugin/pkg/admission/serviceaccount +k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac k8s.io/kubernetes/test/e2e/apps k8s.io/kubernetes/test/e2e/auth k8s.io/kubernetes/test/e2e/common |
