mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
add rbac action to subjects type
This commit is contained in:
parent
dbc4121e16
commit
252d8b7066
@ -190,28 +190,29 @@ func appliesToUser(user user.Info, subject rbac.Subject, namespace string) bool
|
||||
}
|
||||
|
||||
// NewTestRuleResolver returns a rule resolver from lists of role objects.
|
||||
func NewTestRuleResolver(roles []*rbac.Role, roleBindings []*rbac.RoleBinding, clusterRoles []*rbac.ClusterRole, clusterRoleBindings []*rbac.ClusterRoleBinding) AuthorizationRuleResolver {
|
||||
r := staticRoles{
|
||||
func NewTestRuleResolver(roles []*rbac.Role, roleBindings []*rbac.RoleBinding, clusterRoles []*rbac.ClusterRole, clusterRoleBindings []*rbac.ClusterRoleBinding) (AuthorizationRuleResolver, *StaticRoles) {
|
||||
r := StaticRoles{
|
||||
roles: roles,
|
||||
roleBindings: roleBindings,
|
||||
clusterRoles: clusterRoles,
|
||||
clusterRoleBindings: clusterRoleBindings,
|
||||
}
|
||||
return newMockRuleResolver(&r)
|
||||
return newMockRuleResolver(&r), &r
|
||||
}
|
||||
|
||||
func newMockRuleResolver(r *staticRoles) AuthorizationRuleResolver {
|
||||
func newMockRuleResolver(r *StaticRoles) AuthorizationRuleResolver {
|
||||
return NewDefaultRuleResolver(r, r, r, r)
|
||||
}
|
||||
|
||||
type staticRoles struct {
|
||||
// StaticRoles is a rule resolver that resolves from lists of role objects.
|
||||
type StaticRoles struct {
|
||||
roles []*rbac.Role
|
||||
roleBindings []*rbac.RoleBinding
|
||||
clusterRoles []*rbac.ClusterRole
|
||||
clusterRoleBindings []*rbac.ClusterRoleBinding
|
||||
}
|
||||
|
||||
func (r *staticRoles) GetRole(namespace, name string) (*rbac.Role, error) {
|
||||
func (r *StaticRoles) GetRole(namespace, name string) (*rbac.Role, error) {
|
||||
if len(namespace) == 0 {
|
||||
return nil, errors.New("must provide namespace when getting role")
|
||||
}
|
||||
@ -223,7 +224,7 @@ func (r *staticRoles) GetRole(namespace, name string) (*rbac.Role, error) {
|
||||
return nil, errors.New("role not found")
|
||||
}
|
||||
|
||||
func (r *staticRoles) GetClusterRole(name string) (*rbac.ClusterRole, error) {
|
||||
func (r *StaticRoles) GetClusterRole(name string) (*rbac.ClusterRole, error) {
|
||||
for _, clusterRole := range r.clusterRoles {
|
||||
if clusterRole.Name == name {
|
||||
return clusterRole, nil
|
||||
@ -232,7 +233,7 @@ func (r *staticRoles) GetClusterRole(name string) (*rbac.ClusterRole, error) {
|
||||
return nil, errors.New("role not found")
|
||||
}
|
||||
|
||||
func (r *staticRoles) ListRoleBindings(namespace string) ([]*rbac.RoleBinding, error) {
|
||||
func (r *StaticRoles) ListRoleBindings(namespace string) ([]*rbac.RoleBinding, error) {
|
||||
if len(namespace) == 0 {
|
||||
return nil, errors.New("must provide namespace when listing role bindings")
|
||||
}
|
||||
@ -248,6 +249,6 @@ func (r *staticRoles) ListRoleBindings(namespace string) ([]*rbac.RoleBinding, e
|
||||
return roleBindingList, nil
|
||||
}
|
||||
|
||||
func (r *staticRoles) ListClusterRoleBindings() ([]*rbac.ClusterRoleBinding, error) {
|
||||
func (r *StaticRoles) ListClusterRoleBindings() ([]*rbac.ClusterRoleBinding, error) {
|
||||
return r.clusterRoleBindings, nil
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ func TestDefaultRuleResolver(t *testing.T) {
|
||||
Resources: []string{"*"},
|
||||
}
|
||||
|
||||
staticRoles1 := staticRoles{
|
||||
staticRoles1 := StaticRoles{
|
||||
roles: []*rbac.Role{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Namespace: "namespace1", Name: "readthings"},
|
||||
@ -111,7 +111,7 @@ func TestDefaultRuleResolver(t *testing.T) {
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
staticRoles
|
||||
StaticRoles
|
||||
|
||||
// For a given context, what are the rules that apply?
|
||||
user user.Info
|
||||
@ -119,32 +119,32 @@ func TestDefaultRuleResolver(t *testing.T) {
|
||||
effectiveRules []rbac.PolicyRule
|
||||
}{
|
||||
{
|
||||
staticRoles: staticRoles1,
|
||||
StaticRoles: staticRoles1,
|
||||
user: &user.DefaultInfo{Name: "foobar"},
|
||||
namespace: "namespace1",
|
||||
effectiveRules: []rbac.PolicyRule{ruleReadPods, ruleReadServices},
|
||||
},
|
||||
{
|
||||
staticRoles: staticRoles1,
|
||||
StaticRoles: staticRoles1,
|
||||
user: &user.DefaultInfo{Name: "foobar"},
|
||||
namespace: "namespace2",
|
||||
effectiveRules: []rbac.PolicyRule{},
|
||||
},
|
||||
{
|
||||
staticRoles: staticRoles1,
|
||||
StaticRoles: staticRoles1,
|
||||
// Same as above but without a namespace. Only cluster rules should apply.
|
||||
user: &user.DefaultInfo{Name: "foobar", Groups: []string{"admin"}},
|
||||
effectiveRules: []rbac.PolicyRule{ruleAdmin},
|
||||
},
|
||||
{
|
||||
staticRoles: staticRoles1,
|
||||
StaticRoles: staticRoles1,
|
||||
user: &user.DefaultInfo{},
|
||||
effectiveRules: []rbac.PolicyRule{},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range tests {
|
||||
ruleResolver := newMockRuleResolver(&tc.staticRoles)
|
||||
ruleResolver := newMockRuleResolver(&tc.StaticRoles)
|
||||
rules, err := ruleResolver.RulesFor(tc.user, tc.namespace)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: GetEffectivePolicyRules(context)=%v", i, err)
|
||||
|
@ -12,19 +12,26 @@ load(
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["rbac.go"],
|
||||
srcs = [
|
||||
"rbac.go",
|
||||
"subject_locator.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/apis/rbac/validation:go_default_library",
|
||||
"//pkg/auth/authorizer:go_default_library",
|
||||
"//pkg/auth/user:go_default_library",
|
||||
"//pkg/util/errors:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["rbac_test.go"],
|
||||
srcs = [
|
||||
"rbac_test.go",
|
||||
"subject_locator_test.go",
|
||||
],
|
||||
library = "go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
|
@ -221,7 +221,7 @@ func TestAuthorizer(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
ruleResolver := validation.NewTestRuleResolver(tt.roles, tt.roleBindings, tt.clusterRoles, tt.clusterRoleBindings)
|
||||
ruleResolver, _ := validation.NewTestRuleResolver(tt.roles, tt.roleBindings, tt.clusterRoles, tt.clusterRoleBindings)
|
||||
a := RBACAuthorizer{tt.superUser, ruleResolver}
|
||||
for _, attr := range tt.shouldPass {
|
||||
if authorized, _, _ := a.Authorize(attr); !authorized {
|
||||
|
117
plugin/pkg/auth/authorizer/rbac/subject_locator.go
Normal file
117
plugin/pkg/auth/authorizer/rbac/subject_locator.go
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
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 (
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/validation"
|
||||
"k8s.io/kubernetes/pkg/auth/authorizer"
|
||||
"k8s.io/kubernetes/pkg/auth/user"
|
||||
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
||||
)
|
||||
|
||||
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 rbac.RoleRef, namespace string) ([]rbac.PolicyRule, error)
|
||||
}
|
||||
|
||||
type SubjectAccessEvaluator struct {
|
||||
superUser string
|
||||
|
||||
roleBindingLister validation.RoleBindingLister
|
||||
clusterRoleBindingLister validation.ClusterRoleBindingLister
|
||||
roleToRuleMapper RoleToRuleMapper
|
||||
}
|
||||
|
||||
func NewSubjectAccessEvaluator(roles validation.RoleGetter, roleBindings validation.RoleBindingLister, clusterRoles validation.ClusterRoleGetter, clusterRoleBindings validation.ClusterRoleBindingLister, superUser string) *SubjectAccessEvaluator {
|
||||
subjectLocator := &SubjectAccessEvaluator{
|
||||
superUser: superUser,
|
||||
roleBindingLister: roleBindings,
|
||||
clusterRoleBindingLister: clusterRoleBindings,
|
||||
roleToRuleMapper: validation.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) ([]rbac.Subject, error) {
|
||||
subjects := []rbac.Subject{{Kind: rbac.GroupKind, Name: user.SystemPrivilegedGroup}}
|
||||
if len(r.superUser) > 0 {
|
||||
subjects = append(subjects, rbac.Subject{Kind: rbac.UserKind, APIVersion: "v1alpha1", 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 := []rbac.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)
|
||||
}
|
151
plugin/pkg/auth/authorizer/rbac/subject_locator_test.go
Normal file
151
plugin/pkg/auth/authorizer/rbac/subject_locator_test.go
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
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
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/validation"
|
||||
"k8s.io/kubernetes/pkg/auth/authorizer"
|
||||
"k8s.io/kubernetes/pkg/auth/user"
|
||||
)
|
||||
|
||||
func TestSubjectLocator(t *testing.T) {
|
||||
type actionToSubjects struct {
|
||||
action authorizer.Attributes
|
||||
subjects []rbac.Subject
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
roles []*rbac.Role
|
||||
roleBindings []*rbac.RoleBinding
|
||||
clusterRoles []*rbac.ClusterRole
|
||||
clusterRoleBindings []*rbac.ClusterRoleBinding
|
||||
|
||||
superUser string
|
||||
|
||||
actionsToSubjects []actionToSubjects
|
||||
}{
|
||||
{
|
||||
name: "no super user, star matches star",
|
||||
clusterRoles: []*rbac.ClusterRole{
|
||||
newClusterRole("admin", newRule("*", "*", "*", "*")),
|
||||
},
|
||||
clusterRoleBindings: []*rbac.ClusterRoleBinding{
|
||||
newClusterRoleBinding("admin", "User:super-admin", "Group:super-admins"),
|
||||
},
|
||||
roleBindings: []*rbac.RoleBinding{
|
||||
newRoleBinding("ns1", "admin", bindToClusterRole, "User:admin", "Group:admins"),
|
||||
},
|
||||
actionsToSubjects: []actionToSubjects{
|
||||
{
|
||||
&defaultAttributes{"", "", "get", "Pods", "", "ns1", ""},
|
||||
[]rbac.Subject{
|
||||
{Kind: rbac.GroupKind, Name: user.SystemPrivilegedGroup},
|
||||
{Kind: rbac.UserKind, Name: "super-admin"},
|
||||
{Kind: rbac.GroupKind, Name: "super-admins"},
|
||||
{Kind: rbac.UserKind, Name: "admin"},
|
||||
{Kind: rbac.GroupKind, Name: "admins"},
|
||||
},
|
||||
},
|
||||
{
|
||||
// cluster role matches star in namespace
|
||||
&defaultAttributes{"", "", "*", "Pods", "", "*", ""},
|
||||
[]rbac.Subject{
|
||||
{Kind: rbac.GroupKind, Name: user.SystemPrivilegedGroup},
|
||||
{Kind: rbac.UserKind, Name: "super-admin"},
|
||||
{Kind: rbac.GroupKind, Name: "super-admins"},
|
||||
},
|
||||
},
|
||||
{
|
||||
// empty ns
|
||||
&defaultAttributes{"", "", "*", "Pods", "", "", ""},
|
||||
[]rbac.Subject{
|
||||
{Kind: rbac.GroupKind, Name: user.SystemPrivilegedGroup},
|
||||
{Kind: rbac.UserKind, Name: "super-admin"},
|
||||
{Kind: rbac.GroupKind, Name: "super-admins"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "super user, local roles work",
|
||||
superUser: "foo",
|
||||
clusterRoles: []*rbac.ClusterRole{
|
||||
newClusterRole("admin", newRule("*", "*", "*", "*")),
|
||||
},
|
||||
clusterRoleBindings: []*rbac.ClusterRoleBinding{
|
||||
newClusterRoleBinding("admin", "User:super-admin", "Group:super-admins"),
|
||||
},
|
||||
roles: []*rbac.Role{
|
||||
newRole("admin", "ns1", newRule("get", "*", "Pods", "*")),
|
||||
},
|
||||
roleBindings: []*rbac.RoleBinding{
|
||||
newRoleBinding("ns1", "admin", bindToRole, "User:admin", "Group:admins"),
|
||||
},
|
||||
actionsToSubjects: []actionToSubjects{
|
||||
{
|
||||
&defaultAttributes{"", "", "get", "Pods", "", "ns1", ""},
|
||||
[]rbac.Subject{
|
||||
{Kind: rbac.GroupKind, Name: user.SystemPrivilegedGroup},
|
||||
{Kind: rbac.UserKind, APIVersion: "v1alpha1", Name: "foo"},
|
||||
{Kind: rbac.UserKind, Name: "super-admin"},
|
||||
{Kind: rbac.GroupKind, Name: "super-admins"},
|
||||
{Kind: rbac.UserKind, Name: "admin"},
|
||||
{Kind: rbac.GroupKind, Name: "admins"},
|
||||
},
|
||||
},
|
||||
{
|
||||
// verb matchies correctly
|
||||
&defaultAttributes{"", "", "create", "Pods", "", "ns1", ""},
|
||||
[]rbac.Subject{
|
||||
{Kind: rbac.GroupKind, Name: user.SystemPrivilegedGroup},
|
||||
{Kind: rbac.UserKind, APIVersion: "v1alpha1", Name: "foo"},
|
||||
{Kind: rbac.UserKind, Name: "super-admin"},
|
||||
{Kind: rbac.GroupKind, Name: "super-admins"},
|
||||
},
|
||||
},
|
||||
{
|
||||
// binding only works in correct ns
|
||||
&defaultAttributes{"", "", "get", "Pods", "", "ns2", ""},
|
||||
[]rbac.Subject{
|
||||
{Kind: rbac.GroupKind, Name: user.SystemPrivilegedGroup},
|
||||
{Kind: rbac.UserKind, APIVersion: "v1alpha1", Name: "foo"},
|
||||
{Kind: rbac.UserKind, Name: "super-admin"},
|
||||
{Kind: rbac.GroupKind, Name: "super-admins"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
ruleResolver, lister := validation.NewTestRuleResolver(tt.roles, tt.roleBindings, tt.clusterRoles, tt.clusterRoleBindings)
|
||||
a := SubjectAccessEvaluator{tt.superUser, lister, lister, ruleResolver}
|
||||
for i, action := range tt.actionsToSubjects {
|
||||
actualSubjects, err := a.AllowedSubjects(action.action)
|
||||
if err != nil {
|
||||
t.Errorf("case %q %d: error %v", tt.name, i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(actualSubjects, action.subjects) {
|
||||
t.Errorf("case %q %d: expected %v actual %v", tt.name, i, action.subjects, actualSubjects)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user