k8s.io/kubernetes@v1.29.3/pkg/registry/rbac/role/policybased/storage_test.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package policybased 18 19 import ( 20 "context" 21 "testing" 22 23 rbacv1 "k8s.io/api/rbac/v1" 24 "k8s.io/apimachinery/pkg/api/errors" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/runtime" 27 "k8s.io/apiserver/pkg/authentication/user" 28 "k8s.io/apiserver/pkg/authorization/authorizer" 29 "k8s.io/apiserver/pkg/endpoints/request" 30 "k8s.io/apiserver/pkg/registry/rest" 31 "k8s.io/kubernetes/pkg/apis/rbac" 32 _ "k8s.io/kubernetes/pkg/apis/rbac/install" 33 "k8s.io/kubernetes/pkg/registry/rbac/validation" 34 ) 35 36 func TestEscalation(t *testing.T) { 37 createContext := request.WithRequestInfo(request.WithNamespace(context.TODO(), "myns"), &request.RequestInfo{ 38 IsResourceRequest: true, 39 Verb: "create", 40 APIGroup: "rbac.authorization.k8s.io", 41 APIVersion: "v1", 42 Namespace: "myns", 43 Resource: "roles", 44 Name: "", 45 }) 46 updateContext := request.WithRequestInfo(request.WithNamespace(context.TODO(), "myns"), &request.RequestInfo{ 47 IsResourceRequest: true, 48 Verb: "update", 49 APIGroup: "rbac.authorization.k8s.io", 50 APIVersion: "v1", 51 Namespace: "myns", 52 Resource: "roles", 53 Name: "myrole", 54 }) 55 56 superuser := &user.DefaultInfo{Name: "superuser", Groups: []string{"system:masters"}} 57 bob := &user.DefaultInfo{Name: "bob"} 58 steve := &user.DefaultInfo{Name: "steve"} 59 alice := &user.DefaultInfo{Name: "alice"} 60 61 authzCalled := 0 62 fakeStorage := &fakeStorage{} 63 fakeAuthorizer := authorizer.AuthorizerFunc(func(ctx context.Context, attr authorizer.Attributes) (authorizer.Decision, string, error) { 64 authzCalled++ 65 if attr.GetUser().GetName() == "steve" { 66 return authorizer.DecisionAllow, "", nil 67 } 68 return authorizer.DecisionNoOpinion, "", nil 69 }) 70 fakeRuleResolver, _ := validation.NewTestRuleResolver( 71 nil, 72 nil, 73 []*rbacv1.ClusterRole{{ObjectMeta: metav1.ObjectMeta{Name: "alice-role"}, Rules: []rbacv1.PolicyRule{{APIGroups: []string{"*"}, Resources: []string{"*"}, Verbs: []string{"*"}}}}}, 74 []*rbacv1.ClusterRoleBinding{{RoleRef: rbacv1.RoleRef{Name: "alice-role", APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole"}, Subjects: []rbacv1.Subject{{Name: "alice", Kind: "User", APIGroup: "rbac.authorization.k8s.io"}}}}, 75 ) 76 77 role := &rbac.Role{ 78 ObjectMeta: metav1.ObjectMeta{Name: "myrole", Namespace: "myns"}, 79 Rules: []rbac.PolicyRule{{APIGroups: []string{""}, Verbs: []string{"get"}, Resources: []string{"pods"}}}, 80 } 81 82 s := NewStorage(fakeStorage, fakeAuthorizer, fakeRuleResolver) 83 84 testcases := []struct { 85 name string 86 user user.Info 87 expectAllowed bool 88 expectAuthz bool 89 }{ 90 // superuser doesn't even trigger an authz check, and is allowed 91 { 92 name: "superuser", 93 user: superuser, 94 expectAuthz: false, 95 expectAllowed: true, 96 }, 97 // bob triggers an authz check, is disallowed by the authorizer, and has no RBAC permissions, so is not allowed 98 { 99 name: "bob", 100 user: bob, 101 expectAuthz: true, 102 expectAllowed: false, 103 }, 104 // steve triggers an authz check, is allowed by the authorizer, and has no RBAC permissions, but is still allowed 105 { 106 name: "steve", 107 user: steve, 108 expectAuthz: true, 109 expectAllowed: true, 110 }, 111 // alice triggers an authz check, is denied by the authorizer, but has RBAC permissions in the fakeRuleResolver, so is allowed 112 { 113 name: "alice", 114 user: alice, 115 expectAuthz: true, 116 expectAllowed: true, 117 }, 118 } 119 120 for _, tc := range testcases { 121 t.Run(tc.name, func(t *testing.T) { 122 authzCalled, fakeStorage.created, fakeStorage.updated = 0, 0, 0 123 _, err := s.Create(request.WithUser(createContext, tc.user), role, nil, nil) 124 125 if tc.expectAllowed { 126 if err != nil { 127 t.Error(err) 128 return 129 } 130 if fakeStorage.created != 1 { 131 t.Errorf("unexpected calls to underlying storage.Create: %d", fakeStorage.created) 132 return 133 } 134 } else { 135 if !errors.IsForbidden(err) { 136 t.Errorf("expected forbidden, got %v", err) 137 return 138 } 139 if fakeStorage.created != 0 { 140 t.Errorf("unexpected calls to underlying storage.Create: %d", fakeStorage.created) 141 return 142 } 143 } 144 145 if tc.expectAuthz != (authzCalled > 0) { 146 t.Fatalf("expected authz=%v, saw %d calls", tc.expectAuthz, authzCalled) 147 } 148 149 authzCalled, fakeStorage.created, fakeStorage.updated = 0, 0, 0 150 _, _, err = s.Update(request.WithUser(updateContext, tc.user), role.Name, rest.DefaultUpdatedObjectInfo(role), nil, nil, false, nil) 151 152 if tc.expectAllowed { 153 if err != nil { 154 t.Error(err) 155 return 156 } 157 if fakeStorage.updated != 1 { 158 t.Errorf("unexpected calls to underlying storage.Update: %d", fakeStorage.updated) 159 return 160 } 161 } else { 162 if !errors.IsForbidden(err) { 163 t.Errorf("expected forbidden, got %v", err) 164 return 165 } 166 if fakeStorage.updated != 0 { 167 t.Errorf("unexpected calls to underlying storage.Update: %d", fakeStorage.updated) 168 return 169 } 170 } 171 172 if tc.expectAuthz != (authzCalled > 0) { 173 t.Fatalf("expected authz=%v, saw %d calls", tc.expectAuthz, authzCalled) 174 } 175 }) 176 } 177 } 178 179 type fakeStorage struct { 180 updated int 181 created int 182 rest.StandardStorage 183 } 184 185 func (f *fakeStorage) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) { 186 f.created++ 187 return nil, nil 188 } 189 190 func (f *fakeStorage) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) { 191 obj, err := objInfo.UpdatedObject(ctx, &rbac.Role{}) 192 if err != nil { 193 return obj, false, err 194 } 195 f.updated++ 196 return nil, false, nil 197 }