mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 01:06:27 +00:00
wire in ctx to rbac plugins
This commit is contained in:
parent
8d402c9941
commit
4e4eb8c5c9
@ -239,7 +239,7 @@ func (pl PolicyList) Authorize(ctx context.Context, a authorizer.Attributes) (au
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RulesFor returns rules for the given user and namespace.
|
// RulesFor returns rules for the given user and namespace.
|
||||||
func (pl PolicyList) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
func (pl PolicyList) RulesFor(ctx context.Context, user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
||||||
var (
|
var (
|
||||||
resourceRules []authorizer.ResourceRuleInfo
|
resourceRules []authorizer.ResourceRuleInfo
|
||||||
nonResourceRules []authorizer.NonResourceRuleInfo
|
nonResourceRules []authorizer.NonResourceRuleInfo
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||||
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
"k8s.io/kubernetes/pkg/apis/abac"
|
"k8s.io/kubernetes/pkg/apis/abac"
|
||||||
"k8s.io/kubernetes/pkg/apis/abac/v0"
|
"k8s.io/kubernetes/pkg/apis/abac/v0"
|
||||||
"k8s.io/kubernetes/pkg/apis/abac/v1beta1"
|
"k8s.io/kubernetes/pkg/apis/abac/v1beta1"
|
||||||
@ -324,7 +325,7 @@ func TestRulesFor(t *testing.T) {
|
|||||||
User: &tc.User,
|
User: &tc.User,
|
||||||
Namespace: tc.Namespace,
|
Namespace: tc.Namespace,
|
||||||
}
|
}
|
||||||
resourceRules, nonResourceRules, _, _ := a.RulesFor(attr.GetUser(), attr.GetNamespace())
|
resourceRules, nonResourceRules, _, _ := a.RulesFor(genericapirequest.NewContext(), attr.GetUser(), attr.GetNamespace())
|
||||||
actualResourceRules := getResourceRules(resourceRules)
|
actualResourceRules := getResourceRules(resourceRules)
|
||||||
if !reflect.DeepEqual(tc.ExpectResourceRules, actualResourceRules) {
|
if !reflect.DeepEqual(tc.ExpectResourceRules, actualResourceRules) {
|
||||||
t.Logf("tc: %v -> attr %v", tc, attr)
|
t.Logf("tc: %v -> attr %v", tc, attr)
|
||||||
|
@ -78,8 +78,8 @@ func (r *reloadableAuthorizerResolver) Authorize(ctx context.Context, a authoriz
|
|||||||
return r.current.Load().authorizer.Authorize(ctx, a)
|
return r.current.Load().authorizer.Authorize(ctx, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *reloadableAuthorizerResolver) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
func (r *reloadableAuthorizerResolver) RulesFor(ctx context.Context, user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
||||||
return r.current.Load().ruleResolver.RulesFor(user, namespace)
|
return r.current.Load().ruleResolver.RulesFor(ctx, user, namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newForConfig constructs
|
// newForConfig constructs
|
||||||
|
@ -78,7 +78,7 @@ func (r *REST) Create(ctx context.Context, obj runtime.Object, createValidation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceInfo, nonResourceInfo, incomplete, err := r.ruleResolver.RulesFor(user, namespace)
|
resourceInfo, nonResourceInfo, incomplete, err := r.ruleResolver.RulesFor(ctx, user, namespace)
|
||||||
|
|
||||||
ret := &authorizationapi.SelfSubjectRulesReview{
|
ret := &authorizationapi.SelfSubjectRulesReview{
|
||||||
Status: authorizationapi.SubjectRulesReviewStatus{
|
Status: authorizationapi.SubjectRulesReviewStatus{
|
||||||
|
@ -62,6 +62,6 @@ type AuthorizerAdapter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetClusterRole returns the corresponding ClusterRole by name
|
// GetClusterRole returns the corresponding ClusterRole by name
|
||||||
func (a AuthorizerAdapter) GetClusterRole(name string) (*rbacv1.ClusterRole, error) {
|
func (a AuthorizerAdapter) GetClusterRole(ctx context.Context, name string) (*rbacv1.ClusterRole, error) {
|
||||||
return a.Registry.GetClusterRole(genericapirequest.NewContext(), name, &metav1.GetOptions{})
|
return a.Registry.GetClusterRole(genericapirequest.WithNamespace(ctx, ""), name, &metav1.GetOptions{})
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidati
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rules, err := s.ruleResolver.GetRoleReferenceRules(v1RoleRef, metav1.NamespaceNone)
|
rules, err := s.ruleResolver.GetRoleReferenceRules(ctx, v1RoleRef, metav1.NamespaceNone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -115,7 +115,7 @@ func (s *Storage) Update(ctx context.Context, name string, obj rest.UpdatedObjec
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rules, err := s.ruleResolver.GetRoleReferenceRules(v1RoleRef, metav1.NamespaceNone)
|
rules, err := s.ruleResolver.GetRoleReferenceRules(ctx, v1RoleRef, metav1.NamespaceNone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -61,8 +61,8 @@ type AuthorizerAdapter struct {
|
|||||||
Registry Registry
|
Registry Registry
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AuthorizerAdapter) ListClusterRoleBindings() ([]*rbacv1.ClusterRoleBinding, error) {
|
func (a AuthorizerAdapter) ListClusterRoleBindings(ctx context.Context) ([]*rbacv1.ClusterRoleBinding, error) {
|
||||||
list, err := a.Registry.ListClusterRoleBindings(genericapirequest.NewContext(), &metainternalversion.ListOptions{})
|
list, err := a.Registry.ListClusterRoleBindings(genericapirequest.WithNamespace(ctx, ""), &metainternalversion.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,6 @@ type AuthorizerAdapter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetRole returns the corresponding Role by name in specified namespace
|
// GetRole returns the corresponding Role by name in specified namespace
|
||||||
func (a AuthorizerAdapter) GetRole(namespace, name string) (*rbacv1.Role, error) {
|
func (a AuthorizerAdapter) GetRole(ctx context.Context, namespace, name string) (*rbacv1.Role, error) {
|
||||||
return a.Registry.GetRole(genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace), name, &metav1.GetOptions{})
|
return a.Registry.GetRole(genericapirequest.WithNamespace(ctx, namespace), name, &metav1.GetOptions{})
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidati
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rules, err := s.ruleResolver.GetRoleReferenceRules(v1RoleRef, namespace)
|
rules, err := s.ruleResolver.GetRoleReferenceRules(ctx, v1RoleRef, namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -130,7 +130,7 @@ func (s *Storage) Update(ctx context.Context, name string, obj rest.UpdatedObjec
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rules, err := s.ruleResolver.GetRoleReferenceRules(v1RoleRef, namespace)
|
rules, err := s.ruleResolver.GetRoleReferenceRules(ctx, v1RoleRef, namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -61,8 +61,8 @@ type AuthorizerAdapter struct {
|
|||||||
Registry Registry
|
Registry Registry
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AuthorizerAdapter) ListRoleBindings(namespace string) ([]*rbacv1.RoleBinding, error) {
|
func (a AuthorizerAdapter) ListRoleBindings(ctx context.Context, namespace string) ([]*rbacv1.RoleBinding, error) {
|
||||||
list, err := a.Registry.ListRoleBindings(genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace), &metainternalversion.ListOptions{})
|
list, err := a.Registry.ListRoleBindings(genericapirequest.WithNamespace(ctx, namespace), &metainternalversion.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -37,16 +37,16 @@ import (
|
|||||||
type AuthorizationRuleResolver interface {
|
type AuthorizationRuleResolver interface {
|
||||||
// GetRoleReferenceRules attempts to resolve the role reference of a RoleBinding or ClusterRoleBinding. The passed namespace should be the namespace
|
// 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.
|
// of the role binding, the empty string if a cluster role binding.
|
||||||
GetRoleReferenceRules(roleRef rbacv1.RoleRef, namespace string) ([]rbacv1.PolicyRule, error)
|
GetRoleReferenceRules(ctx context.Context, 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
|
// 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
|
// 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.
|
// can be made on the basis of those rules that are found.
|
||||||
RulesFor(user user.Info, namespace string) ([]rbacv1.PolicyRule, error)
|
RulesFor(ctx context.Context, 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.
|
// 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.
|
// 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)
|
VisitRulesFor(ctx context.Context, 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.
|
// ConfirmNoEscalation determines if the roles for a given user in a given namespace encompass the provided role.
|
||||||
@ -59,7 +59,7 @@ func ConfirmNoEscalation(ctx context.Context, ruleResolver AuthorizationRuleReso
|
|||||||
}
|
}
|
||||||
namespace, _ := genericapirequest.NamespaceFrom(ctx)
|
namespace, _ := genericapirequest.NamespaceFrom(ctx)
|
||||||
|
|
||||||
ownerRules, err := ruleResolver.RulesFor(user, namespace)
|
ownerRules, err := ruleResolver.RulesFor(ctx, user, namespace)
|
||||||
if err != nil {
|
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.
|
// 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)
|
klog.V(1).Infof("non-fatal error getting local rules for %v: %v", user, err)
|
||||||
@ -100,24 +100,24 @@ func NewDefaultRuleResolver(roleGetter RoleGetter, roleBindingLister RoleBinding
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RoleGetter interface {
|
type RoleGetter interface {
|
||||||
GetRole(namespace, name string) (*rbacv1.Role, error)
|
GetRole(ctx context.Context, namespace, name string) (*rbacv1.Role, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type RoleBindingLister interface {
|
type RoleBindingLister interface {
|
||||||
ListRoleBindings(namespace string) ([]*rbacv1.RoleBinding, error)
|
ListRoleBindings(ctx context.Context, namespace string) ([]*rbacv1.RoleBinding, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClusterRoleGetter interface {
|
type ClusterRoleGetter interface {
|
||||||
GetClusterRole(name string) (*rbacv1.ClusterRole, error)
|
GetClusterRole(ctx context.Context, name string) (*rbacv1.ClusterRole, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClusterRoleBindingLister interface {
|
type ClusterRoleBindingLister interface {
|
||||||
ListClusterRoleBindings() ([]*rbacv1.ClusterRoleBinding, error)
|
ListClusterRoleBindings(ctx context.Context) ([]*rbacv1.ClusterRoleBinding, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DefaultRuleResolver) RulesFor(user user.Info, namespace string) ([]rbacv1.PolicyRule, error) {
|
func (r *DefaultRuleResolver) RulesFor(ctx context.Context, user user.Info, namespace string) ([]rbacv1.PolicyRule, error) {
|
||||||
visitor := &ruleAccumulator{}
|
visitor := &ruleAccumulator{}
|
||||||
r.VisitRulesFor(user, namespace, visitor.visit)
|
r.VisitRulesFor(ctx, user, namespace, visitor.visit)
|
||||||
return visitor.rules, utilerrors.NewAggregate(visitor.errors)
|
return visitor.rules, utilerrors.NewAggregate(visitor.errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,8 +176,8 @@ func (d *roleBindingDescriber) String() string {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DefaultRuleResolver) VisitRulesFor(user user.Info, namespace string, visitor func(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool) {
|
func (r *DefaultRuleResolver) VisitRulesFor(ctx context.Context, 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 clusterRoleBindings, err := r.clusterRoleBindingLister.ListClusterRoleBindings(ctx); err != nil {
|
||||||
if !visitor(nil, nil, err) {
|
if !visitor(nil, nil, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -188,7 +188,7 @@ func (r *DefaultRuleResolver) VisitRulesFor(user user.Info, namespace string, vi
|
|||||||
if !applies {
|
if !applies {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
rules, err := r.GetRoleReferenceRules(clusterRoleBinding.RoleRef, "")
|
rules, err := r.GetRoleReferenceRules(ctx, clusterRoleBinding.RoleRef, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !visitor(nil, nil, err) {
|
if !visitor(nil, nil, err) {
|
||||||
return
|
return
|
||||||
@ -206,7 +206,7 @@ func (r *DefaultRuleResolver) VisitRulesFor(user user.Info, namespace string, vi
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(namespace) > 0 {
|
if len(namespace) > 0 {
|
||||||
if roleBindings, err := r.roleBindingLister.ListRoleBindings(namespace); err != nil {
|
if roleBindings, err := r.roleBindingLister.ListRoleBindings(ctx, namespace); err != nil {
|
||||||
if !visitor(nil, nil, err) {
|
if !visitor(nil, nil, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -217,7 +217,7 @@ func (r *DefaultRuleResolver) VisitRulesFor(user user.Info, namespace string, vi
|
|||||||
if !applies {
|
if !applies {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
rules, err := r.GetRoleReferenceRules(roleBinding.RoleRef, namespace)
|
rules, err := r.GetRoleReferenceRules(ctx, roleBinding.RoleRef, namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !visitor(nil, nil, err) {
|
if !visitor(nil, nil, err) {
|
||||||
return
|
return
|
||||||
@ -237,17 +237,17 @@ func (r *DefaultRuleResolver) VisitRulesFor(user user.Info, namespace string, vi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetRoleReferenceRules attempts to resolve the RoleBinding or ClusterRoleBinding.
|
// GetRoleReferenceRules attempts to resolve the RoleBinding or ClusterRoleBinding.
|
||||||
func (r *DefaultRuleResolver) GetRoleReferenceRules(roleRef rbacv1.RoleRef, bindingNamespace string) ([]rbacv1.PolicyRule, error) {
|
func (r *DefaultRuleResolver) GetRoleReferenceRules(ctx context.Context, roleRef rbacv1.RoleRef, bindingNamespace string) ([]rbacv1.PolicyRule, error) {
|
||||||
switch roleRef.Kind {
|
switch roleRef.Kind {
|
||||||
case "Role":
|
case "Role":
|
||||||
role, err := r.roleGetter.GetRole(bindingNamespace, roleRef.Name)
|
role, err := r.roleGetter.GetRole(ctx, bindingNamespace, roleRef.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return role.Rules, nil
|
return role.Rules, nil
|
||||||
|
|
||||||
case "ClusterRole":
|
case "ClusterRole":
|
||||||
clusterRole, err := r.clusterRoleGetter.GetClusterRole(roleRef.Name)
|
clusterRole, err := r.clusterRoleGetter.GetClusterRole(ctx, roleRef.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -326,7 +326,7 @@ type StaticRoles struct {
|
|||||||
clusterRoleBindings []*rbacv1.ClusterRoleBinding
|
clusterRoleBindings []*rbacv1.ClusterRoleBinding
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *StaticRoles) GetRole(namespace, name string) (*rbacv1.Role, error) {
|
func (r *StaticRoles) GetRole(ctx context.Context, namespace, name string) (*rbacv1.Role, error) {
|
||||||
if len(namespace) == 0 {
|
if len(namespace) == 0 {
|
||||||
return nil, errors.New("must provide namespace when getting role")
|
return nil, errors.New("must provide namespace when getting role")
|
||||||
}
|
}
|
||||||
@ -338,7 +338,7 @@ func (r *StaticRoles) GetRole(namespace, name string) (*rbacv1.Role, error) {
|
|||||||
return nil, errors.New("role not found")
|
return nil, errors.New("role not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *StaticRoles) GetClusterRole(name string) (*rbacv1.ClusterRole, error) {
|
func (r *StaticRoles) GetClusterRole(ctx context.Context, name string) (*rbacv1.ClusterRole, error) {
|
||||||
for _, clusterRole := range r.clusterRoles {
|
for _, clusterRole := range r.clusterRoles {
|
||||||
if clusterRole.Name == name {
|
if clusterRole.Name == name {
|
||||||
return clusterRole, nil
|
return clusterRole, nil
|
||||||
@ -347,7 +347,7 @@ func (r *StaticRoles) GetClusterRole(name string) (*rbacv1.ClusterRole, error) {
|
|||||||
return nil, errors.New("clusterrole not found")
|
return nil, errors.New("clusterrole not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *StaticRoles) ListRoleBindings(namespace string) ([]*rbacv1.RoleBinding, error) {
|
func (r *StaticRoles) ListRoleBindings(ctx context.Context, namespace string) ([]*rbacv1.RoleBinding, error) {
|
||||||
if len(namespace) == 0 {
|
if len(namespace) == 0 {
|
||||||
return nil, errors.New("must provide namespace when listing role bindings")
|
return nil, errors.New("must provide namespace when listing role bindings")
|
||||||
}
|
}
|
||||||
@ -363,6 +363,6 @@ func (r *StaticRoles) ListRoleBindings(namespace string) ([]*rbacv1.RoleBinding,
|
|||||||
return roleBindingList, nil
|
return roleBindingList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *StaticRoles) ListClusterRoleBindings() ([]*rbacv1.ClusterRoleBinding, error) {
|
func (r *StaticRoles) ListClusterRoleBindings(ctx context.Context) ([]*rbacv1.ClusterRoleBinding, error) {
|
||||||
return r.clusterRoleBindings, nil
|
return r.clusterRoleBindings, nil
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
)
|
)
|
||||||
|
|
||||||
// compute a hash of a policy rule so we can sort in a deterministic order
|
// compute a hash of a policy rule so we can sort in a deterministic order
|
||||||
@ -145,7 +146,7 @@ func TestDefaultRuleResolver(t *testing.T) {
|
|||||||
|
|
||||||
for i, tc := range tests {
|
for i, tc := range tests {
|
||||||
ruleResolver := newMockRuleResolver(&tc.StaticRoles)
|
ruleResolver := newMockRuleResolver(&tc.StaticRoles)
|
||||||
rules, err := ruleResolver.RulesFor(tc.user, tc.namespace)
|
rules, err := ruleResolver.RulesFor(genericapirequest.NewContext(), tc.user, tc.namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("case %d: GetEffectivePolicyRules(context)=%v", i, err)
|
t.Errorf("case %d: GetEffectivePolicyRules(context)=%v", i, err)
|
||||||
continue
|
continue
|
||||||
|
@ -95,7 +95,7 @@ var (
|
|||||||
csiNodeResource = storageapi.Resource("csinodes")
|
csiNodeResource = storageapi.Resource("csinodes")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *NodeAuthorizer) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
func (r *NodeAuthorizer) RulesFor(ctx context.Context, user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
||||||
if _, isNode := r.identifier.NodeIdentity(user); isNode {
|
if _, isNode := r.identifier.NodeIdentity(user); isNode {
|
||||||
// indicate nodes do not have fully enumerated permissions
|
// indicate nodes do not have fully enumerated permissions
|
||||||
return nil, nil, true, fmt.Errorf("node authorizer does not support user rule resolution")
|
return nil, nil, true, fmt.Errorf("node authorizer does not support user rule resolution")
|
||||||
|
@ -39,12 +39,12 @@ type RequestToRuleMapper interface {
|
|||||||
// Any rule returned is still valid, since rules are deny by default. If you can pass with the 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
|
// supplied, you do not have to fail the request. If you cannot, you should indicate the error along
|
||||||
// with your denial.
|
// with your denial.
|
||||||
RulesFor(subject user.Info, namespace string) ([]rbacv1.PolicyRule, error)
|
RulesFor(ctx context.Context, subject user.Info, namespace string) ([]rbacv1.PolicyRule, error)
|
||||||
|
|
||||||
// VisitRulesFor invokes visitor() with each rule that applies to a given user in a given namespace,
|
// 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.
|
// and each error encountered resolving those rules. Rule may be nil if err is non-nil.
|
||||||
// If visitor() returns false, visiting is short-circuited.
|
// 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)
|
VisitRulesFor(ctx context.Context, user user.Info, namespace string, visitor func(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
type RBACAuthorizer struct {
|
type RBACAuthorizer struct {
|
||||||
@ -75,7 +75,7 @@ func (v *authorizingVisitor) visit(source fmt.Stringer, rule *rbacv1.PolicyRule,
|
|||||||
func (r *RBACAuthorizer) Authorize(ctx context.Context, requestAttributes authorizer.Attributes) (authorizer.Decision, string, error) {
|
func (r *RBACAuthorizer) Authorize(ctx context.Context, requestAttributes authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||||
ruleCheckingVisitor := &authorizingVisitor{requestAttributes: requestAttributes}
|
ruleCheckingVisitor := &authorizingVisitor{requestAttributes: requestAttributes}
|
||||||
|
|
||||||
r.authorizationRuleResolver.VisitRulesFor(requestAttributes.GetUser(), requestAttributes.GetNamespace(), ruleCheckingVisitor.visit)
|
r.authorizationRuleResolver.VisitRulesFor(ctx, requestAttributes.GetUser(), requestAttributes.GetNamespace(), ruleCheckingVisitor.visit)
|
||||||
if ruleCheckingVisitor.allowed {
|
if ruleCheckingVisitor.allowed {
|
||||||
return authorizer.DecisionAllow, ruleCheckingVisitor.reason, nil
|
return authorizer.DecisionAllow, ruleCheckingVisitor.reason, nil
|
||||||
}
|
}
|
||||||
@ -126,13 +126,13 @@ func (r *RBACAuthorizer) Authorize(ctx context.Context, requestAttributes author
|
|||||||
return authorizer.DecisionNoOpinion, reason, nil
|
return authorizer.DecisionNoOpinion, reason, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RBACAuthorizer) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
func (r *RBACAuthorizer) RulesFor(ctx context.Context, user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
||||||
var (
|
var (
|
||||||
resourceRules []authorizer.ResourceRuleInfo
|
resourceRules []authorizer.ResourceRuleInfo
|
||||||
nonResourceRules []authorizer.NonResourceRuleInfo
|
nonResourceRules []authorizer.NonResourceRuleInfo
|
||||||
)
|
)
|
||||||
|
|
||||||
policyRules, err := r.authorizationRuleResolver.RulesFor(user, namespace)
|
policyRules, err := r.authorizationRuleResolver.RulesFor(ctx, user, namespace)
|
||||||
for _, policyRule := range policyRules {
|
for _, policyRule := range policyRules {
|
||||||
if len(policyRule.Resources) > 0 {
|
if len(policyRule.Resources) > 0 {
|
||||||
r := authorizer.DefaultResourceRuleInfo{
|
r := authorizer.DefaultResourceRuleInfo{
|
||||||
@ -196,7 +196,7 @@ type RoleGetter struct {
|
|||||||
Lister rbaclisters.RoleLister
|
Lister rbaclisters.RoleLister
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *RoleGetter) GetRole(namespace, name string) (*rbacv1.Role, error) {
|
func (g *RoleGetter) GetRole(ctx context.Context, namespace, name string) (*rbacv1.Role, error) {
|
||||||
return g.Lister.Roles(namespace).Get(name)
|
return g.Lister.Roles(namespace).Get(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,7 +204,7 @@ type RoleBindingLister struct {
|
|||||||
Lister rbaclisters.RoleBindingLister
|
Lister rbaclisters.RoleBindingLister
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *RoleBindingLister) ListRoleBindings(namespace string) ([]*rbacv1.RoleBinding, error) {
|
func (l *RoleBindingLister) ListRoleBindings(ctx context.Context, namespace string) ([]*rbacv1.RoleBinding, error) {
|
||||||
return l.Lister.RoleBindings(namespace).List(labels.Everything())
|
return l.Lister.RoleBindings(namespace).List(labels.Everything())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,7 +212,7 @@ type ClusterRoleGetter struct {
|
|||||||
Lister rbaclisters.ClusterRoleLister
|
Lister rbaclisters.ClusterRoleLister
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *ClusterRoleGetter) GetClusterRole(name string) (*rbacv1.ClusterRole, error) {
|
func (g *ClusterRoleGetter) GetClusterRole(ctx context.Context, name string) (*rbacv1.ClusterRole, error) {
|
||||||
return g.Lister.Get(name)
|
return g.Lister.Get(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,6 +220,6 @@ type ClusterRoleBindingLister struct {
|
|||||||
Lister rbaclisters.ClusterRoleBindingLister
|
Lister rbaclisters.ClusterRoleBindingLister
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *ClusterRoleBindingLister) ListClusterRoleBindings() ([]*rbacv1.ClusterRoleBinding, error) {
|
func (l *ClusterRoleBindingLister) ListClusterRoleBindings(ctx context.Context) ([]*rbacv1.ClusterRoleBinding, error) {
|
||||||
return l.Lister.List(labels.Everything())
|
return l.Lister.List(labels.Everything())
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,8 @@ limitations under the License.
|
|||||||
package rbac
|
package rbac
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
@ -28,11 +30,11 @@ import (
|
|||||||
type RoleToRuleMapper interface {
|
type RoleToRuleMapper interface {
|
||||||
// GetRoleReferenceRules attempts to resolve the role reference of a RoleBinding or ClusterRoleBinding. The passed namespace should be the namespace
|
// 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.
|
// of the role binding, the empty string if a cluster role binding.
|
||||||
GetRoleReferenceRules(roleRef rbacv1.RoleRef, namespace string) ([]rbacv1.PolicyRule, error)
|
GetRoleReferenceRules(ctx context.Context, roleRef rbacv1.RoleRef, namespace string) ([]rbacv1.PolicyRule, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SubjectLocator interface {
|
type SubjectLocator interface {
|
||||||
AllowedSubjects(attributes authorizer.Attributes) ([]rbacv1.Subject, error)
|
AllowedSubjects(ctx context.Context, attributes authorizer.Attributes) ([]rbacv1.Subject, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ = SubjectLocator(&SubjectAccessEvaluator{})
|
var _ = SubjectLocator(&SubjectAccessEvaluator{})
|
||||||
@ -59,19 +61,19 @@ func NewSubjectAccessEvaluator(roles rbacregistryvalidation.RoleGetter, roleBind
|
|||||||
|
|
||||||
// AllowedSubjects returns the subjects that can perform an action and any errors encountered while computing the list.
|
// 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.
|
// 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) {
|
func (r *SubjectAccessEvaluator) AllowedSubjects(ctx context.Context, requestAttributes authorizer.Attributes) ([]rbacv1.Subject, error) {
|
||||||
subjects := []rbacv1.Subject{{Kind: rbacv1.GroupKind, APIGroup: rbacv1.GroupName, Name: user.SystemPrivilegedGroup}}
|
subjects := []rbacv1.Subject{{Kind: rbacv1.GroupKind, APIGroup: rbacv1.GroupName, Name: user.SystemPrivilegedGroup}}
|
||||||
if len(r.superUser) > 0 {
|
if len(r.superUser) > 0 {
|
||||||
subjects = append(subjects, rbacv1.Subject{Kind: rbacv1.UserKind, APIGroup: rbacv1.GroupName, Name: r.superUser})
|
subjects = append(subjects, rbacv1.Subject{Kind: rbacv1.UserKind, APIGroup: rbacv1.GroupName, Name: r.superUser})
|
||||||
}
|
}
|
||||||
errorlist := []error{}
|
errorlist := []error{}
|
||||||
|
|
||||||
if clusterRoleBindings, err := r.clusterRoleBindingLister.ListClusterRoleBindings(); err != nil {
|
if clusterRoleBindings, err := r.clusterRoleBindingLister.ListClusterRoleBindings(ctx); err != nil {
|
||||||
errorlist = append(errorlist, err)
|
errorlist = append(errorlist, err)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
for _, clusterRoleBinding := range clusterRoleBindings {
|
for _, clusterRoleBinding := range clusterRoleBindings {
|
||||||
rules, err := r.roleToRuleMapper.GetRoleReferenceRules(clusterRoleBinding.RoleRef, "")
|
rules, err := r.roleToRuleMapper.GetRoleReferenceRules(ctx, clusterRoleBinding.RoleRef, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// if we have an error, just keep track of it and keep processing. Since rules are additive,
|
// 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
|
// missing a reference is bad, but we can continue with other rolebindings and still have a list
|
||||||
@ -85,12 +87,12 @@ func (r *SubjectAccessEvaluator) AllowedSubjects(requestAttributes authorizer.At
|
|||||||
}
|
}
|
||||||
|
|
||||||
if namespace := requestAttributes.GetNamespace(); len(namespace) > 0 {
|
if namespace := requestAttributes.GetNamespace(); len(namespace) > 0 {
|
||||||
if roleBindings, err := r.roleBindingLister.ListRoleBindings(namespace); err != nil {
|
if roleBindings, err := r.roleBindingLister.ListRoleBindings(ctx, namespace); err != nil {
|
||||||
errorlist = append(errorlist, err)
|
errorlist = append(errorlist, err)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
for _, roleBinding := range roleBindings {
|
for _, roleBinding := range roleBindings {
|
||||||
rules, err := r.roleToRuleMapper.GetRoleReferenceRules(roleBinding.RoleRef, namespace)
|
rules, err := r.roleToRuleMapper.GetRoleReferenceRules(ctx, roleBinding.RoleRef, namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// if we have an error, just keep track of it and keep processing. Since rules are additive,
|
// 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
|
// missing a reference is bad, but we can continue with other rolebindings and still have a list
|
||||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package rbac
|
package rbac
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -139,7 +140,7 @@ func TestSubjectLocator(t *testing.T) {
|
|||||||
ruleResolver, lister := rbacregistryvalidation.NewTestRuleResolver(tt.roles, tt.roleBindings, tt.clusterRoles, tt.clusterRoleBindings)
|
ruleResolver, lister := rbacregistryvalidation.NewTestRuleResolver(tt.roles, tt.roleBindings, tt.clusterRoles, tt.clusterRoleBindings)
|
||||||
a := SubjectAccessEvaluator{tt.superUser, lister, lister, ruleResolver}
|
a := SubjectAccessEvaluator{tt.superUser, lister, lister, ruleResolver}
|
||||||
for i, action := range tt.actionsToSubjects {
|
for i, action := range tt.actionsToSubjects {
|
||||||
actualSubjects, err := a.AllowedSubjects(action.action)
|
actualSubjects, err := a.AllowedSubjects(context.Background(), action.action)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("case %q %d: error %v", tt.name, i, err)
|
t.Errorf("case %q %d: error %v", tt.name, i, err)
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ func (f AuthorizerFunc) Authorize(ctx context.Context, a Attributes) (Decision,
|
|||||||
// RuleResolver provides a mechanism for resolving the list of rules that apply to a given user within a namespace.
|
// RuleResolver provides a mechanism for resolving the list of rules that apply to a given user within a namespace.
|
||||||
type RuleResolver interface {
|
type RuleResolver interface {
|
||||||
// RulesFor get the list of cluster wide rules, the list of rules in the specific namespace, incomplete status and errors.
|
// RulesFor get the list of cluster wide rules, the list of rules in the specific namespace, incomplete status and errors.
|
||||||
RulesFor(user user.Info, namespace string) ([]ResourceRuleInfo, []NonResourceRuleInfo, bool, error)
|
RulesFor(ctx context.Context, user user.Info, namespace string) ([]ResourceRuleInfo, []NonResourceRuleInfo, bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequestAttributesGetter provides a function that extracts Attributes from an http.Request
|
// RequestAttributesGetter provides a function that extracts Attributes from an http.Request
|
||||||
|
@ -33,7 +33,7 @@ func (alwaysAllowAuthorizer) Authorize(ctx context.Context, a authorizer.Attribu
|
|||||||
return authorizer.DecisionAllow, "", nil
|
return authorizer.DecisionAllow, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (alwaysAllowAuthorizer) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
func (alwaysAllowAuthorizer) RulesFor(ctx context.Context, user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
||||||
return []authorizer.ResourceRuleInfo{
|
return []authorizer.ResourceRuleInfo{
|
||||||
&authorizer.DefaultResourceRuleInfo{
|
&authorizer.DefaultResourceRuleInfo{
|
||||||
Verbs: []string{"*"},
|
Verbs: []string{"*"},
|
||||||
@ -61,7 +61,7 @@ func (alwaysDenyAuthorizer) Authorize(ctx context.Context, a authorizer.Attribut
|
|||||||
return authorizer.DecisionNoOpinion, "Everything is forbidden.", nil
|
return authorizer.DecisionNoOpinion, "Everything is forbidden.", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (alwaysDenyAuthorizer) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
func (alwaysDenyAuthorizer) RulesFor(ctx context.Context, user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
||||||
return []authorizer.ResourceRuleInfo{}, []authorizer.NonResourceRuleInfo{}, false, nil
|
return []authorizer.ResourceRuleInfo{}, []authorizer.NonResourceRuleInfo{}, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ func NewRuleResolvers(authorizationHandlers ...authorizer.RuleResolver) authoriz
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RulesFor against a chain of authorizer.RuleResolver objects and returns nil if successful and returns error if unsuccessful
|
// RulesFor against a chain of authorizer.RuleResolver objects and returns nil if successful and returns error if unsuccessful
|
||||||
func (authzHandler unionAuthzRulesHandler) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
func (authzHandler unionAuthzRulesHandler) RulesFor(ctx context.Context, user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
||||||
var (
|
var (
|
||||||
errList []error
|
errList []error
|
||||||
resourceRulesList []authorizer.ResourceRuleInfo
|
resourceRulesList []authorizer.ResourceRuleInfo
|
||||||
@ -86,7 +86,7 @@ func (authzHandler unionAuthzRulesHandler) RulesFor(user user.Info, namespace st
|
|||||||
incompleteStatus := false
|
incompleteStatus := false
|
||||||
|
|
||||||
for _, currAuthzHandler := range authzHandler {
|
for _, currAuthzHandler := range authzHandler {
|
||||||
resourceRules, nonResourceRules, incomplete, err := currAuthzHandler.RulesFor(user, namespace)
|
resourceRules, nonResourceRules, incomplete, err := currAuthzHandler.RulesFor(ctx, user, namespace)
|
||||||
|
|
||||||
if incomplete {
|
if incomplete {
|
||||||
incompleteStatus = true
|
incompleteStatus = true
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
|
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||||
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockAuthzHandler struct {
|
type mockAuthzHandler struct {
|
||||||
@ -86,7 +87,7 @@ type mockAuthzRuleHandler struct {
|
|||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mock *mockAuthzRuleHandler) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
func (mock *mockAuthzRuleHandler) RulesFor(ctx context.Context, user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
||||||
if mock.err != nil {
|
if mock.err != nil {
|
||||||
return []authorizer.ResourceRuleInfo{}, []authorizer.NonResourceRuleInfo{}, false, mock.err
|
return []authorizer.ResourceRuleInfo{}, []authorizer.NonResourceRuleInfo{}, false, mock.err
|
||||||
}
|
}
|
||||||
@ -150,7 +151,7 @@ func TestAuthorizationResourceRules(t *testing.T) {
|
|||||||
|
|
||||||
authzRulesHandler := NewRuleResolvers(handler1, handler2)
|
authzRulesHandler := NewRuleResolvers(handler1, handler2)
|
||||||
|
|
||||||
rules, _, _, _ := authzRulesHandler.RulesFor(nil, "")
|
rules, _, _, _ := authzRulesHandler.RulesFor(genericapirequest.NewContext(), nil, "")
|
||||||
actual := getResourceRules(rules)
|
actual := getResourceRules(rules)
|
||||||
if !reflect.DeepEqual(expected, actual) {
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
t.Errorf("Expected: \n%#v\n but actual: \n%#v\n", expected, actual)
|
t.Errorf("Expected: \n%#v\n but actual: \n%#v\n", expected, actual)
|
||||||
@ -189,7 +190,7 @@ func TestAuthorizationNonResourceRules(t *testing.T) {
|
|||||||
|
|
||||||
authzRulesHandler := NewRuleResolvers(handler1, handler2)
|
authzRulesHandler := NewRuleResolvers(handler1, handler2)
|
||||||
|
|
||||||
_, rules, _, _ := authzRulesHandler.RulesFor(nil, "")
|
_, rules, _, _ := authzRulesHandler.RulesFor(genericapirequest.NewContext(), nil, "")
|
||||||
actual := getNonResourceRules(rules)
|
actual := getNonResourceRules(rules)
|
||||||
if !reflect.DeepEqual(expected, actual) {
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
t.Errorf("Expected: \n%#v\n but actual: \n%#v\n", expected, actual)
|
t.Errorf("Expected: \n%#v\n but actual: \n%#v\n", expected, actual)
|
||||||
|
@ -402,7 +402,7 @@ func labelSelectorToAuthorizationAPI(attr authorizer.Attributes) ([]metav1.Label
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: need to finish the method to get the rules when using webhook mode
|
// TODO: need to finish the method to get the rules when using webhook mode
|
||||||
func (w *WebhookAuthorizer) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
func (w *WebhookAuthorizer) RulesFor(ctx context.Context, user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
||||||
var (
|
var (
|
||||||
resourceRules []authorizer.ResourceRuleInfo
|
resourceRules []authorizer.ResourceRuleInfo
|
||||||
nonResourceRules []authorizer.NonResourceRuleInfo
|
nonResourceRules []authorizer.NonResourceRuleInfo
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
rbacapi "k8s.io/api/rbac/v1"
|
rbacapi "k8s.io/api/rbac/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
@ -40,6 +41,7 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||||
unionauthz "k8s.io/apiserver/pkg/authorization/union"
|
unionauthz "k8s.io/apiserver/pkg/authorization/union"
|
||||||
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
"k8s.io/apiserver/pkg/registry/generic"
|
"k8s.io/apiserver/pkg/registry/generic"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
@ -133,26 +135,27 @@ type bootstrapRoles struct {
|
|||||||
//
|
//
|
||||||
// client should be authenticated as the RBAC super user.
|
// client should be authenticated as the RBAC super user.
|
||||||
func (b bootstrapRoles) bootstrap(client clientset.Interface) error {
|
func (b bootstrapRoles) bootstrap(client clientset.Interface) error {
|
||||||
|
ctx := context.TODO()
|
||||||
for _, r := range b.clusterRoles {
|
for _, r := range b.clusterRoles {
|
||||||
_, err := client.RbacV1().ClusterRoles().Create(context.TODO(), &r, metav1.CreateOptions{})
|
_, err := client.RbacV1().ClusterRoles().Create(ctx, &r, metav1.CreateOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to make request: %v", err)
|
return fmt.Errorf("failed to make request: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, r := range b.roles {
|
for _, r := range b.roles {
|
||||||
_, err := client.RbacV1().Roles(r.Namespace).Create(context.TODO(), &r, metav1.CreateOptions{})
|
_, err := client.RbacV1().Roles(r.Namespace).Create(ctx, &r, metav1.CreateOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to make request: %v", err)
|
return fmt.Errorf("failed to make request: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, r := range b.clusterRoleBindings {
|
for _, r := range b.clusterRoleBindings {
|
||||||
_, err := client.RbacV1().ClusterRoleBindings().Create(context.TODO(), &r, metav1.CreateOptions{})
|
_, err := client.RbacV1().ClusterRoleBindings().Create(ctx, &r, metav1.CreateOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to make request: %v", err)
|
return fmt.Errorf("failed to make request: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, r := range b.roleBindings {
|
for _, r := range b.roleBindings {
|
||||||
_, err := client.RbacV1().RoleBindings(r.Namespace).Create(context.TODO(), &r, metav1.CreateOptions{})
|
_, err := client.RbacV1().RoleBindings(r.Namespace).Create(ctx, &r, metav1.CreateOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to make request: %v", err)
|
return fmt.Errorf("failed to make request: %v", err)
|
||||||
}
|
}
|
||||||
@ -817,3 +820,220 @@ func TestDiscoveryUpgradeBootstrapping(t *testing.T) {
|
|||||||
t.Errorf("`system:public-info-viewer` should have inherited Subjects from `system:discovery` Wanted: %v, got %v", newDiscRoleBinding.Subjects, publicInfoViewerRoleBinding.Subjects)
|
t.Errorf("`system:public-info-viewer` should have inherited Subjects from `system:discovery` Wanted: %v, got %v", newDiscRoleBinding.Subjects, publicInfoViewerRoleBinding.Subjects)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type authorizeRequest struct {
|
||||||
|
ar authorizer.AttributesRecord
|
||||||
|
expected authorizer.Decision
|
||||||
|
}
|
||||||
|
|
||||||
|
// For 1.31 ctx was wired into the authorizers. This tests check that context values
|
||||||
|
// are not used inside the code to resolve namespaces or users with the goal of
|
||||||
|
// preventing regressions in the future.
|
||||||
|
func TestRBACContextContamination(t *testing.T) {
|
||||||
|
superUser := "admin/system:masters"
|
||||||
|
validNamespace := "pod-namespace"
|
||||||
|
invalidNamespace := "forbidden-namespace"
|
||||||
|
|
||||||
|
roles := bootstrapRoles{}
|
||||||
|
testcases := []authorizeRequest{}
|
||||||
|
|
||||||
|
// Tests itself is bit oververbose and each case creates its own objects.
|
||||||
|
// This makes readability bit easier over trying to overoptimize test case.
|
||||||
|
|
||||||
|
// Case 1: clusterrole+clusterbinding
|
||||||
|
// should allow cluster-scoped request
|
||||||
|
// should allow namespace-scoped request in any namespace
|
||||||
|
// should disallow request for resource not in rules
|
||||||
|
{
|
||||||
|
roles.clusterRoles = append(roles.clusterRoles, rbacapi.ClusterRole{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "c1-clusterrole"},
|
||||||
|
Rules: []rbacapi.PolicyRule{ruleReadPods},
|
||||||
|
})
|
||||||
|
roles.clusterRoleBindings = append(roles.clusterRoleBindings, rbacapi.ClusterRoleBinding{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "c1-clusterrolebinding"},
|
||||||
|
Subjects: []rbacapi.Subject{{Kind: "User", Name: "c1-user"}},
|
||||||
|
RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "c1-clusterrole"},
|
||||||
|
})
|
||||||
|
|
||||||
|
user := &user.DefaultInfo{Name: "c1-user"}
|
||||||
|
testcases = append(testcases, []authorizeRequest{
|
||||||
|
{
|
||||||
|
ar: authorizer.AttributesRecord{Verb: "list", Resource: "pods", Namespace: "", ResourceRequest: true, User: user},
|
||||||
|
expected: authorizer.DecisionAllow,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ar: authorizer.AttributesRecord{Verb: "list", Resource: "pods", Namespace: validNamespace, ResourceRequest: true, User: user},
|
||||||
|
expected: authorizer.DecisionAllow,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ar: authorizer.AttributesRecord{Verb: "list", Resource: "configmaps", Namespace: "", ResourceRequest: true, User: user},
|
||||||
|
expected: authorizer.DecisionNoOpinion,
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// case 2: clusterrole+rolebinding
|
||||||
|
// should disallow cluster-scoped request
|
||||||
|
// should allow namespace-scoped request in rolebinding namespace
|
||||||
|
// should disallow namespace-scoped request in other namespace
|
||||||
|
// should disallow request for resource not in rules
|
||||||
|
{
|
||||||
|
roles.clusterRoles = append(roles.clusterRoles, rbacapi.ClusterRole{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "c2-clusterrole"},
|
||||||
|
Rules: []rbacapi.PolicyRule{ruleReadPods},
|
||||||
|
})
|
||||||
|
roles.roleBindings = append(roles.roleBindings, rbacapi.RoleBinding{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "c2-rolebinding",
|
||||||
|
Namespace: validNamespace,
|
||||||
|
},
|
||||||
|
Subjects: []rbacapi.Subject{{Kind: "User", Name: "c2-user"}},
|
||||||
|
RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "c2-clusterrole"},
|
||||||
|
})
|
||||||
|
|
||||||
|
user := &user.DefaultInfo{Name: "c2-user"}
|
||||||
|
testcases = append(testcases, []authorizeRequest{
|
||||||
|
{
|
||||||
|
ar: authorizer.AttributesRecord{Verb: "list", Resource: "pods", Namespace: "", ResourceRequest: true, User: user},
|
||||||
|
expected: authorizer.DecisionNoOpinion,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ar: authorizer.AttributesRecord{Verb: "list", Resource: "pods", Namespace: validNamespace, ResourceRequest: true, User: user},
|
||||||
|
expected: authorizer.DecisionAllow,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ar: authorizer.AttributesRecord{Verb: "list", Resource: "configmaps", Namespace: validNamespace, ResourceRequest: true, User: user},
|
||||||
|
expected: authorizer.DecisionNoOpinion,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ar: authorizer.AttributesRecord{Verb: "list", Resource: "pods", Namespace: invalidNamespace, ResourceRequest: true, User: user},
|
||||||
|
expected: authorizer.DecisionNoOpinion,
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// case 3: role+rolebinding
|
||||||
|
// should disallow cluster-scoped request
|
||||||
|
// should allow namespace-scoped request in rolebinding namespace
|
||||||
|
// should disallow namespace-scoped request in other namespace
|
||||||
|
// should disallow request for resource not in rules
|
||||||
|
{
|
||||||
|
roles.roles = append(roles.roles, rbacapi.Role{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "c3-role",
|
||||||
|
Namespace: validNamespace,
|
||||||
|
},
|
||||||
|
Rules: []rbacapi.PolicyRule{ruleReadPods},
|
||||||
|
})
|
||||||
|
roles.roleBindings = append(roles.roleBindings, rbacapi.RoleBinding{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "c3-rolebinding",
|
||||||
|
Namespace: validNamespace,
|
||||||
|
},
|
||||||
|
Subjects: []rbacapi.Subject{{Kind: "User", Name: "c3-user"}},
|
||||||
|
RoleRef: rbacapi.RoleRef{Kind: "Role", Name: "c3-role"},
|
||||||
|
})
|
||||||
|
|
||||||
|
user := &user.DefaultInfo{Name: "c3-user"}
|
||||||
|
testcases = append(testcases, []authorizeRequest{
|
||||||
|
{
|
||||||
|
ar: authorizer.AttributesRecord{Verb: "list", Resource: "pods", Namespace: "", ResourceRequest: true, User: user},
|
||||||
|
expected: authorizer.DecisionNoOpinion,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ar: authorizer.AttributesRecord{Verb: "list", Resource: "pods", Namespace: validNamespace, ResourceRequest: true, User: user},
|
||||||
|
expected: authorizer.DecisionAllow,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ar: authorizer.AttributesRecord{Verb: "list", Resource: "configmaps", Namespace: validNamespace, ResourceRequest: true, User: user},
|
||||||
|
expected: authorizer.DecisionNoOpinion,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ar: authorizer.AttributesRecord{Verb: "list", Resource: "pods", Namespace: invalidNamespace, ResourceRequest: true, User: user},
|
||||||
|
expected: authorizer.DecisionNoOpinion,
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticator := group.NewAuthenticatedGroupAdder(bearertoken.New(tokenfile.New(map[string]*user.DefaultInfo{
|
||||||
|
superUser: {Name: "admin", Groups: []string{"system:masters"}},
|
||||||
|
})))
|
||||||
|
|
||||||
|
var tearDownAuthorizerFn func()
|
||||||
|
defer func() {
|
||||||
|
if tearDownAuthorizerFn != nil {
|
||||||
|
tearDownAuthorizerFn()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
var rbacAuthz authorizer.Authorizer
|
||||||
|
_, kubeConfig, tearDownFn := framework.StartTestServer(context.Background(), t, framework.TestServerSetup{
|
||||||
|
ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
|
||||||
|
// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
|
||||||
|
// Also disable namespace lifecycle to workaroung the test limitation that first creates
|
||||||
|
// roles/rolebindings and only then creates corresponding namespaces.
|
||||||
|
opts.Admission.GenericAdmission.DisablePlugins = []string{"ServiceAccount", "NamespaceLifecycle"}
|
||||||
|
// Disable built-in authorizers
|
||||||
|
opts.Authorization.Modes = []string{"AlwaysDeny"}
|
||||||
|
},
|
||||||
|
ModifyServerConfig: func(config *controlplane.Config) {
|
||||||
|
// Append our custom test authenticator
|
||||||
|
config.ControlPlane.Generic.Authentication.Authenticator = unionauthn.New(config.ControlPlane.Generic.Authentication.Authenticator, authenticator)
|
||||||
|
// Append our custom test authorizer
|
||||||
|
rbacAuthz, tearDownAuthorizerFn = newRBACAuthorizer(t, config)
|
||||||
|
config.ControlPlane.Generic.Authorization.Authorizer = unionauthz.New(config.ControlPlane.Generic.Authorization.Authorizer, rbacAuthz)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
defer tearDownFn()
|
||||||
|
|
||||||
|
// Bootstrap the API Server with the test case's initial roles.
|
||||||
|
superuserClient, _ := clientsetForToken(superUser, kubeConfig)
|
||||||
|
if err := roles.bootstrap(superuserClient); err != nil {
|
||||||
|
t.Errorf("failed to apply initial roles: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ns := range []string{validNamespace, invalidNamespace} {
|
||||||
|
_, err := superuserClient.CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: ns,
|
||||||
|
},
|
||||||
|
}, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to create namespace: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3 cases shared for all test cases:
|
||||||
|
// 1. Default context
|
||||||
|
// 2. Empty namespace
|
||||||
|
// 3. Invalid namespace in the context
|
||||||
|
for j, r := range testcases {
|
||||||
|
ctx := context.Background()
|
||||||
|
// 1. Default context
|
||||||
|
if decision, _, err := rbacAuthz.Authorize(ctx, &r.ar); err != nil {
|
||||||
|
t.Errorf("req %d: unexpected error: %v", j, err)
|
||||||
|
return
|
||||||
|
} else if decision != r.expected {
|
||||||
|
t.Errorf("req %d: expected %v, got %v", j, r.expected, decision)
|
||||||
|
}
|
||||||
|
// 2. Empty namespace
|
||||||
|
if decision, _, err := rbacAuthz.Authorize(genericapirequest.WithNamespace(ctx, ""), &r.ar); err != nil {
|
||||||
|
t.Errorf("req %d: unexpected error: %v", j, err)
|
||||||
|
return
|
||||||
|
} else if decision != r.expected {
|
||||||
|
t.Errorf("req %d: expected %v, got %v", j, r.expected, decision)
|
||||||
|
}
|
||||||
|
// 3. Invalid namespace in the context
|
||||||
|
if decision, _, err := rbacAuthz.Authorize(genericapirequest.WithNamespace(ctx, invalidNamespace), &r.ar); err != nil {
|
||||||
|
t.Errorf("req %d: unexpected error: %v", j, err)
|
||||||
|
return
|
||||||
|
} else if decision != r.expected {
|
||||||
|
t.Errorf("req %d: expected %v, got %v", j, r.expected, decision)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user