1
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(), ""), &request.RequestInfo{
38 IsResourceRequest: true,
39 Verb: "create",
40 APIGroup: "rbac.authorization.k8s.io",
41 APIVersion: "v1",
42 Resource: "clusterroles",
43 Name: "",
44 })
45 updateContext := request.WithRequestInfo(request.WithNamespace(context.TODO(), ""), &request.RequestInfo{
46 IsResourceRequest: true,
47 Verb: "update",
48 APIGroup: "rbac.authorization.k8s.io",
49 APIVersion: "v1",
50 Resource: "clusterroles",
51 Name: "myrole",
52 })
53
54 superuser := &user.DefaultInfo{Name: "superuser", Groups: []string{"system:masters"}}
55 bob := &user.DefaultInfo{Name: "bob"}
56 steve := &user.DefaultInfo{Name: "steve"}
57 alice := &user.DefaultInfo{Name: "alice"}
58
59 authzCalled := 0
60 fakeStorage := &fakeStorage{}
61 fakeAuthorizer := authorizer.AuthorizerFunc(func(ctx context.Context, attr authorizer.Attributes) (authorizer.Decision, string, error) {
62 authzCalled++
63 if attr.GetUser().GetName() == "steve" {
64 return authorizer.DecisionAllow, "", nil
65 }
66 return authorizer.DecisionNoOpinion, "", nil
67 })
68 fakeRuleResolver, _ := validation.NewTestRuleResolver(
69 nil,
70 nil,
71 []*rbacv1.ClusterRole{{ObjectMeta: metav1.ObjectMeta{Name: "alice-role"}, Rules: []rbacv1.PolicyRule{{APIGroups: []string{"*"}, Resources: []string{"*"}, Verbs: []string{"*"}}}}},
72 []*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"}}}},
73 )
74
75 role := &rbac.ClusterRole{
76 ObjectMeta: metav1.ObjectMeta{Name: "myrole", Namespace: ""},
77 Rules: []rbac.PolicyRule{{APIGroups: []string{""}, Verbs: []string{"get"}, Resources: []string{"pods"}}},
78 }
79
80 s := NewStorage(fakeStorage, fakeAuthorizer, fakeRuleResolver)
81
82 testcases := []struct {
83 name string
84 user user.Info
85 expectAllowed bool
86 expectAuthz bool
87 }{
88
89 {
90 name: "superuser",
91 user: superuser,
92 expectAuthz: false,
93 expectAllowed: true,
94 },
95
96 {
97 name: "bob",
98 user: bob,
99 expectAuthz: true,
100 expectAllowed: false,
101 },
102
103 {
104 name: "steve",
105 user: steve,
106 expectAuthz: true,
107 expectAllowed: true,
108 },
109
110 {
111 name: "alice",
112 user: alice,
113 expectAuthz: true,
114 expectAllowed: true,
115 },
116 }
117
118 for _, tc := range testcases {
119 t.Run(tc.name, func(t *testing.T) {
120 authzCalled, fakeStorage.created, fakeStorage.updated = 0, 0, 0
121 _, err := s.Create(request.WithUser(createContext, tc.user), role, nil, nil)
122
123 if tc.expectAllowed {
124 if err != nil {
125 t.Error(err)
126 return
127 }
128 if fakeStorage.created != 1 {
129 t.Errorf("unexpected calls to underlying storage.Create: %d", fakeStorage.created)
130 return
131 }
132 } else {
133 if !errors.IsForbidden(err) {
134 t.Errorf("expected forbidden, got %v", err)
135 return
136 }
137 if fakeStorage.created != 0 {
138 t.Errorf("unexpected calls to underlying storage.Create: %d", fakeStorage.created)
139 return
140 }
141 }
142
143 if tc.expectAuthz != (authzCalled > 0) {
144 t.Fatalf("expected authz=%v, saw %d calls", tc.expectAuthz, authzCalled)
145 }
146
147 authzCalled, fakeStorage.created, fakeStorage.updated = 0, 0, 0
148 _, _, err = s.Update(request.WithUser(updateContext, tc.user), role.Name, rest.DefaultUpdatedObjectInfo(role), nil, nil, false, nil)
149
150 if tc.expectAllowed {
151 if err != nil {
152 t.Error(err)
153 return
154 }
155 if fakeStorage.updated != 1 {
156 t.Errorf("unexpected calls to underlying storage.Update: %d", fakeStorage.updated)
157 return
158 }
159 } else {
160 if !errors.IsForbidden(err) {
161 t.Errorf("expected forbidden, got %v", err)
162 return
163 }
164 if fakeStorage.updated != 0 {
165 t.Errorf("unexpected calls to underlying storage.Update: %d", fakeStorage.updated)
166 return
167 }
168 }
169
170 if tc.expectAuthz != (authzCalled > 0) {
171 t.Fatalf("expected authz=%v, saw %d calls", tc.expectAuthz, authzCalled)
172 }
173 })
174 }
175 }
176
177 type fakeStorage struct {
178 updated int
179 created int
180 rest.StandardStorage
181 }
182
183 func (f *fakeStorage) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
184 f.created++
185 return nil, nil
186 }
187
188 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) {
189 obj, err := objInfo.UpdatedObject(ctx, &rbac.ClusterRole{})
190 if err != nil {
191 return obj, false, err
192 }
193 f.updated++
194 return nil, false, nil
195 }
196
View as plain text