123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- /*
- 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 (
- "hash/fnv"
- "io"
- "reflect"
- "sort"
- "testing"
- "k8s.io/kubernetes/pkg/api"
- "k8s.io/kubernetes/pkg/apis/rbac"
- "k8s.io/kubernetes/pkg/auth/user"
- "k8s.io/kubernetes/pkg/util/diff"
- )
- // compute a hash of a policy rule so we can sort in a deterministic order
- func hashOf(p rbac.PolicyRule) string {
- hash := fnv.New32()
- writeStrings := func(slis ...[]string) {
- for _, sli := range slis {
- for _, s := range sli {
- io.WriteString(hash, s)
- }
- }
- }
- writeStrings(p.Verbs, p.APIGroups, p.Resources, p.ResourceNames, p.NonResourceURLs)
- return string(hash.Sum(nil))
- }
- // byHash sorts a set of policy rules by a hash of its fields
- type byHash []rbac.PolicyRule
- func (b byHash) Len() int { return len(b) }
- func (b byHash) Less(i, j int) bool { return hashOf(b[i]) < hashOf(b[j]) }
- func (b byHash) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
- func TestDefaultRuleResolver(t *testing.T) {
- ruleReadPods := rbac.PolicyRule{
- Verbs: []string{"GET", "WATCH"},
- APIGroups: []string{"v1"},
- Resources: []string{"pods"},
- }
- ruleReadServices := rbac.PolicyRule{
- Verbs: []string{"GET", "WATCH"},
- APIGroups: []string{"v1"},
- Resources: []string{"services"},
- }
- ruleWriteNodes := rbac.PolicyRule{
- Verbs: []string{"PUT", "CREATE", "UPDATE"},
- APIGroups: []string{"v1"},
- Resources: []string{"nodes"},
- }
- ruleAdmin := rbac.PolicyRule{
- Verbs: []string{"*"},
- APIGroups: []string{"*"},
- Resources: []string{"*"},
- }
- staticRoles1 := staticRoles{
- roles: []rbac.Role{
- {
- ObjectMeta: api.ObjectMeta{Namespace: "namespace1", Name: "readthings"},
- Rules: []rbac.PolicyRule{ruleReadPods, ruleReadServices},
- },
- },
- clusterRoles: []rbac.ClusterRole{
- {
- ObjectMeta: api.ObjectMeta{Name: "cluster-admin"},
- Rules: []rbac.PolicyRule{ruleAdmin},
- },
- {
- ObjectMeta: api.ObjectMeta{Name: "write-nodes"},
- Rules: []rbac.PolicyRule{ruleWriteNodes},
- },
- },
- roleBindings: []rbac.RoleBinding{
- {
- ObjectMeta: api.ObjectMeta{Namespace: "namespace1"},
- Subjects: []rbac.Subject{
- {Kind: rbac.UserKind, Name: "foobar"},
- {Kind: rbac.GroupKind, Name: "group1"},
- },
- RoleRef: api.ObjectReference{Kind: "Role", Namespace: "namespace1", Name: "readthings"},
- },
- },
- clusterRoleBindings: []rbac.ClusterRoleBinding{
- {
- Subjects: []rbac.Subject{
- {Kind: rbac.UserKind, Name: "admin"},
- {Kind: rbac.GroupKind, Name: "admin"},
- },
- RoleRef: api.ObjectReference{Kind: "ClusterRole", Name: "cluster-admin"},
- },
- },
- }
- tests := []struct {
- staticRoles
- // For a given context, what are the rules that apply?
- ctx api.Context
- effectiveRules []rbac.PolicyRule
- }{
- {
- staticRoles: staticRoles1,
- ctx: api.WithNamespace(
- api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}), "namespace1",
- ),
- effectiveRules: []rbac.PolicyRule{ruleReadPods, ruleReadServices},
- },
- {
- staticRoles: staticRoles1,
- ctx: api.WithNamespace(
- // Same as above but diffrerent namespace. Should return no rules.
- api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}), "namespace2",
- ),
- effectiveRules: []rbac.PolicyRule{},
- },
- {
- staticRoles: staticRoles1,
- // GetEffectivePolicyRules only returns the policies for the namespace, not the master namespace.
- ctx: api.WithNamespace(
- api.WithUser(api.NewContext(), &user.DefaultInfo{
- Name: "foobar", Groups: []string{"admin"},
- }), "namespace1",
- ),
- effectiveRules: []rbac.PolicyRule{ruleReadPods, ruleReadServices},
- },
- {
- staticRoles: staticRoles1,
- // Same as above but without a namespace. Only cluster rules should apply.
- ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{
- Name: "foobar", Groups: []string{"admin"},
- }),
- effectiveRules: []rbac.PolicyRule{ruleAdmin},
- },
- {
- staticRoles: staticRoles1,
- ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{}),
- effectiveRules: []rbac.PolicyRule{},
- },
- }
- for i, tc := range tests {
- ruleResolver := newMockRuleResolver(&tc.staticRoles)
- rules, err := ruleResolver.GetEffectivePolicyRules(tc.ctx)
- if err != nil {
- t.Errorf("case %d: GetEffectivePolicyRules(context)=%v", i, err)
- continue
- }
- // Sort for deep equals
- sort.Sort(byHash(rules))
- sort.Sort(byHash(tc.effectiveRules))
- if !reflect.DeepEqual(rules, tc.effectiveRules) {
- ruleDiff := diff.ObjectDiff(rules, tc.effectiveRules)
- t.Errorf("case %d: %s", i, ruleDiff)
- }
- }
- }
- func TestAppliesTo(t *testing.T) {
- tests := []struct {
- subjects []rbac.Subject
- ctx api.Context
- appliesTo bool
- testCase string
- }{
- {
- subjects: []rbac.Subject{
- {Kind: rbac.UserKind, Name: "foobar"},
- },
- ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}),
- appliesTo: true,
- testCase: "single subject that matches username",
- },
- {
- subjects: []rbac.Subject{
- {Kind: rbac.UserKind, Name: "barfoo"},
- {Kind: rbac.UserKind, Name: "foobar"},
- },
- ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}),
- appliesTo: true,
- testCase: "multiple subjects, one that matches username",
- },
- {
- subjects: []rbac.Subject{
- {Kind: rbac.UserKind, Name: "barfoo"},
- {Kind: rbac.UserKind, Name: "foobar"},
- },
- ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "zimzam"}),
- appliesTo: false,
- testCase: "multiple subjects, none that match username",
- },
- {
- subjects: []rbac.Subject{
- {Kind: rbac.UserKind, Name: "barfoo"},
- {Kind: rbac.GroupKind, Name: "foobar"},
- },
- ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}}),
- appliesTo: true,
- testCase: "multiple subjects, one that match group",
- },
- {
- subjects: []rbac.Subject{
- {Kind: rbac.UserKind, Name: "barfoo"},
- {Kind: rbac.GroupKind, Name: "foobar"},
- },
- ctx: api.WithNamespace(
- api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}}),
- "namespace1",
- ),
- appliesTo: true,
- testCase: "multiple subjects, one that match group, should ignore namespace",
- },
- {
- subjects: []rbac.Subject{
- {Kind: rbac.UserKind, Name: "barfoo"},
- {Kind: rbac.GroupKind, Name: "foobar"},
- {Kind: rbac.ServiceAccountKind, Namespace: "kube-system", Name: "default"},
- },
- ctx: api.WithNamespace(
- api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "system:serviceaccount:kube-system:default"}),
- "default",
- ),
- appliesTo: true,
- testCase: "multiple subjects with a service account that matches",
- },
- {
- subjects: []rbac.Subject{
- {Kind: rbac.UserKind, Name: "*"},
- },
- ctx: api.WithNamespace(
- api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}),
- "default",
- ),
- appliesTo: true,
- testCase: "multiple subjects with a service account that matches",
- },
- }
- for _, tc := range tests {
- got, err := appliesTo(tc.ctx, tc.subjects)
- if err != nil {
- t.Errorf("case %q %v", tc.testCase, err)
- continue
- }
- if got != tc.appliesTo {
- t.Errorf("case %q want appliesTo=%t, got appliesTo=%t", tc.testCase, tc.appliesTo, got)
- }
- }
- }
|