1
16
17 package rest
18
19 import (
20 "context"
21 "fmt"
22 "time"
23
24 "k8s.io/klog/v2"
25
26 rbacapiv1 "k8s.io/api/rbac/v1"
27 apierrors "k8s.io/apimachinery/pkg/api/errors"
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 "k8s.io/apimachinery/pkg/runtime/schema"
30 utilruntime "k8s.io/apimachinery/pkg/util/runtime"
31 "k8s.io/apimachinery/pkg/util/wait"
32 "k8s.io/apiserver/pkg/authorization/authorizer"
33 "k8s.io/apiserver/pkg/registry/generic"
34 "k8s.io/apiserver/pkg/registry/rest"
35 genericapiserver "k8s.io/apiserver/pkg/server"
36 serverstorage "k8s.io/apiserver/pkg/server/storage"
37 clientset "k8s.io/client-go/kubernetes"
38 rbacv1client "k8s.io/client-go/kubernetes/typed/rbac/v1"
39 "k8s.io/client-go/util/retry"
40 "k8s.io/component-helpers/auth/rbac/reconciliation"
41 "k8s.io/kubernetes/pkg/api/legacyscheme"
42 "k8s.io/kubernetes/pkg/apis/rbac"
43 "k8s.io/kubernetes/pkg/registry/rbac/clusterrole"
44 clusterrolepolicybased "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/policybased"
45 clusterrolestore "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/storage"
46 "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding"
47 clusterrolebindingpolicybased "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/policybased"
48 clusterrolebindingstore "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/storage"
49 "k8s.io/kubernetes/pkg/registry/rbac/role"
50 rolepolicybased "k8s.io/kubernetes/pkg/registry/rbac/role/policybased"
51 rolestore "k8s.io/kubernetes/pkg/registry/rbac/role/storage"
52 "k8s.io/kubernetes/pkg/registry/rbac/rolebinding"
53 rolebindingpolicybased "k8s.io/kubernetes/pkg/registry/rbac/rolebinding/policybased"
54 rolebindingstore "k8s.io/kubernetes/pkg/registry/rbac/rolebinding/storage"
55 rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
56 "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy"
57 )
58
59 const PostStartHookName = "rbac/bootstrap-roles"
60
61 type RESTStorageProvider struct {
62 Authorizer authorizer.Authorizer
63 }
64
65 var _ genericapiserver.PostStartHookProvider = RESTStorageProvider{}
66
67 func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, error) {
68 apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(rbac.GroupName, legacyscheme.Scheme, legacyscheme.ParameterCodec, legacyscheme.Codecs)
69
70
71
72 if storageMap, err := p.storage(rbacapiv1.SchemeGroupVersion, apiResourceConfigSource, restOptionsGetter); err != nil {
73 return genericapiserver.APIGroupInfo{}, err
74 } else if len(storageMap) > 0 {
75 apiGroupInfo.VersionedResourcesStorageMap[rbacapiv1.SchemeGroupVersion.Version] = storageMap
76 }
77
78 return apiGroupInfo, nil
79 }
80
81 func (p RESTStorageProvider) storage(version schema.GroupVersion, apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
82 storage := map[string]rest.Storage{}
83
84 rolesStorage, err := rolestore.NewREST(restOptionsGetter)
85 if err != nil {
86 return storage, err
87 }
88 roleBindingsStorage, err := rolebindingstore.NewREST(restOptionsGetter)
89 if err != nil {
90 return storage, err
91 }
92 clusterRolesStorage, err := clusterrolestore.NewREST(restOptionsGetter)
93 if err != nil {
94 return storage, err
95 }
96 clusterRoleBindingsStorage, err := clusterrolebindingstore.NewREST(restOptionsGetter)
97 if err != nil {
98 return storage, err
99 }
100
101 authorizationRuleResolver := rbacregistryvalidation.NewDefaultRuleResolver(
102 role.AuthorizerAdapter{Registry: role.NewRegistry(rolesStorage)},
103 rolebinding.AuthorizerAdapter{Registry: rolebinding.NewRegistry(roleBindingsStorage)},
104 clusterrole.AuthorizerAdapter{Registry: clusterrole.NewRegistry(clusterRolesStorage)},
105 clusterrolebinding.AuthorizerAdapter{Registry: clusterrolebinding.NewRegistry(clusterRoleBindingsStorage)},
106 )
107
108
109 if resource := "roles"; apiResourceConfigSource.ResourceEnabled(rbacapiv1.SchemeGroupVersion.WithResource(resource)) {
110 storage[resource] = rolepolicybased.NewStorage(rolesStorage, p.Authorizer, authorizationRuleResolver)
111 }
112
113
114 if resource := "rolebindings"; apiResourceConfigSource.ResourceEnabled(rbacapiv1.SchemeGroupVersion.WithResource(resource)) {
115 storage[resource] = rolebindingpolicybased.NewStorage(roleBindingsStorage, p.Authorizer, authorizationRuleResolver)
116 }
117
118
119 if resource := "clusterroles"; apiResourceConfigSource.ResourceEnabled(rbacapiv1.SchemeGroupVersion.WithResource(resource)) {
120 storage[resource] = clusterrolepolicybased.NewStorage(clusterRolesStorage, p.Authorizer, authorizationRuleResolver)
121 }
122
123
124 if resource := "clusterrolebindings"; apiResourceConfigSource.ResourceEnabled(rbacapiv1.SchemeGroupVersion.WithResource(resource)) {
125 storage[resource] = clusterrolebindingpolicybased.NewStorage(clusterRoleBindingsStorage, p.Authorizer, authorizationRuleResolver)
126 }
127
128 return storage, nil
129 }
130
131 func (p RESTStorageProvider) PostStartHook() (string, genericapiserver.PostStartHookFunc, error) {
132 policy := &PolicyData{
133 ClusterRoles: append(bootstrappolicy.ClusterRoles(), bootstrappolicy.ControllerRoles()...),
134 ClusterRoleBindings: append(bootstrappolicy.ClusterRoleBindings(), bootstrappolicy.ControllerRoleBindings()...),
135 Roles: bootstrappolicy.NamespaceRoles(),
136 RoleBindings: bootstrappolicy.NamespaceRoleBindings(),
137 ClusterRolesToAggregate: bootstrappolicy.ClusterRolesToAggregate(),
138 ClusterRoleBindingsToSplit: bootstrappolicy.ClusterRoleBindingsToSplit(),
139 }
140 return PostStartHookName, policy.EnsureRBACPolicy(), nil
141 }
142
143 type PolicyData struct {
144 ClusterRoles []rbacapiv1.ClusterRole
145 ClusterRoleBindings []rbacapiv1.ClusterRoleBinding
146 Roles map[string][]rbacapiv1.Role
147 RoleBindings map[string][]rbacapiv1.RoleBinding
148
149 ClusterRolesToAggregate map[string]string
150
151 ClusterRoleBindingsToSplit map[string]rbacapiv1.ClusterRoleBinding
152 }
153
154 func isConflictOrServiceUnavailable(err error) bool {
155 return apierrors.IsConflict(err) || apierrors.IsServiceUnavailable(err)
156 }
157
158 func retryOnConflictOrServiceUnavailable(backoff wait.Backoff, fn func() error) error {
159 return retry.OnError(backoff, isConflictOrServiceUnavailable, fn)
160 }
161
162 func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc {
163 return func(hookContext genericapiserver.PostStartHookContext) error {
164
165
166 err := wait.Poll(1*time.Second, 30*time.Second, func() (done bool, err error) {
167 client, err := clientset.NewForConfig(hookContext.LoopbackClientConfig)
168 if err != nil {
169 utilruntime.HandleError(fmt.Errorf("unable to initialize client set: %v", err))
170 return false, nil
171 }
172 return ensureRBACPolicy(p, client)
173 })
174
175 if err != nil {
176 return fmt.Errorf("unable to initialize roles: %v", err)
177 }
178
179 return nil
180 }
181 }
182
183 func ensureRBACPolicy(p *PolicyData, client clientset.Interface) (done bool, err error) {
184 failedReconciliation := false
185
186 if _, err := client.RbacV1().ClusterRoles().List(context.TODO(), metav1.ListOptions{}); err != nil {
187 utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err))
188 return false, nil
189 }
190 if _, err := client.RbacV1().ClusterRoleBindings().List(context.TODO(), metav1.ListOptions{}); err != nil {
191 utilruntime.HandleError(fmt.Errorf("unable to initialize clusterrolebindings: %v", err))
192 return false, nil
193 }
194
195
196
197 if err := primeAggregatedClusterRoles(p.ClusterRolesToAggregate, client.RbacV1()); err != nil {
198 utilruntime.HandleError(fmt.Errorf("unable to prime aggregated clusterroles: %v", err))
199 return false, nil
200 }
201
202 if err := primeSplitClusterRoleBindings(p.ClusterRoleBindingsToSplit, client.RbacV1()); err != nil {
203 utilruntime.HandleError(fmt.Errorf("unable to prime split ClusterRoleBindings: %v", err))
204 return false, nil
205 }
206
207
208 for _, clusterRole := range p.ClusterRoles {
209 opts := reconciliation.ReconcileRoleOptions{
210 Role: reconciliation.ClusterRoleRuleOwner{ClusterRole: &clusterRole},
211 Client: reconciliation.ClusterRoleModifier{Client: client.RbacV1().ClusterRoles()},
212 Confirm: true,
213 }
214
215 err := retryOnConflictOrServiceUnavailable(retry.DefaultBackoff, func() error {
216 result, err := opts.Run()
217 if err != nil {
218 return err
219 }
220 switch {
221 case result.Protected && result.Operation != reconciliation.ReconcileNone:
222 klog.Warningf("skipped reconcile-protected clusterrole.%s/%s with missing permissions: %v", rbac.GroupName, clusterRole.Name, result.MissingRules)
223 case result.Operation == reconciliation.ReconcileUpdate:
224 klog.V(2).Infof("updated clusterrole.%s/%s with additional permissions: %v", rbac.GroupName, clusterRole.Name, result.MissingRules)
225 case result.Operation == reconciliation.ReconcileCreate:
226 klog.V(2).Infof("created clusterrole.%s/%s", rbac.GroupName, clusterRole.Name)
227 }
228 return nil
229 })
230 if err != nil {
231
232 utilruntime.HandleError(fmt.Errorf("unable to reconcile clusterrole.%s/%s: %v", rbac.GroupName, clusterRole.Name, err))
233 failedReconciliation = true
234 }
235 }
236
237
238 for _, clusterRoleBinding := range p.ClusterRoleBindings {
239 opts := reconciliation.ReconcileRoleBindingOptions{
240 RoleBinding: reconciliation.ClusterRoleBindingAdapter{ClusterRoleBinding: &clusterRoleBinding},
241 Client: reconciliation.ClusterRoleBindingClientAdapter{Client: client.RbacV1().ClusterRoleBindings()},
242 Confirm: true,
243 }
244
245 err := retryOnConflictOrServiceUnavailable(retry.DefaultBackoff, func() error {
246 result, err := opts.Run()
247 if err != nil {
248 return err
249 }
250 switch {
251 case result.Protected && result.Operation != reconciliation.ReconcileNone:
252 klog.Warningf("skipped reconcile-protected clusterrolebinding.%s/%s with missing subjects: %v", rbac.GroupName, clusterRoleBinding.Name, result.MissingSubjects)
253 case result.Operation == reconciliation.ReconcileUpdate:
254 klog.V(2).Infof("updated clusterrolebinding.%s/%s with additional subjects: %v", rbac.GroupName, clusterRoleBinding.Name, result.MissingSubjects)
255 case result.Operation == reconciliation.ReconcileCreate:
256 klog.V(2).Infof("created clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name)
257 case result.Operation == reconciliation.ReconcileRecreate:
258 klog.V(2).Infof("recreated clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name)
259 }
260 return nil
261 })
262 if err != nil {
263
264 utilruntime.HandleError(fmt.Errorf("unable to reconcile clusterrolebinding.%s/%s: %v", rbac.GroupName, clusterRoleBinding.Name, err))
265 failedReconciliation = true
266 }
267 }
268
269
270 for namespace, roles := range p.Roles {
271 for _, role := range roles {
272 opts := reconciliation.ReconcileRoleOptions{
273 Role: reconciliation.RoleRuleOwner{Role: &role},
274 Client: reconciliation.RoleModifier{Client: client.RbacV1(), NamespaceClient: client.CoreV1().Namespaces()},
275 Confirm: true,
276 }
277
278 err := retryOnConflictOrServiceUnavailable(retry.DefaultBackoff, func() error {
279 result, err := opts.Run()
280 if err != nil {
281 return err
282 }
283 switch {
284 case result.Protected && result.Operation != reconciliation.ReconcileNone:
285 klog.Warningf("skipped reconcile-protected role.%s/%s in %v with missing permissions: %v", rbac.GroupName, role.Name, namespace, result.MissingRules)
286 case result.Operation == reconciliation.ReconcileUpdate:
287 klog.V(2).Infof("updated role.%s/%s in %v with additional permissions: %v", rbac.GroupName, role.Name, namespace, result.MissingRules)
288 case result.Operation == reconciliation.ReconcileCreate:
289 klog.V(2).Infof("created role.%s/%s in %v", rbac.GroupName, role.Name, namespace)
290 }
291 return nil
292 })
293 if err != nil {
294
295 utilruntime.HandleError(fmt.Errorf("unable to reconcile role.%s/%s in %v: %v", rbac.GroupName, role.Name, namespace, err))
296 failedReconciliation = true
297 }
298 }
299 }
300
301
302 for namespace, roleBindings := range p.RoleBindings {
303 for _, roleBinding := range roleBindings {
304 opts := reconciliation.ReconcileRoleBindingOptions{
305 RoleBinding: reconciliation.RoleBindingAdapter{RoleBinding: &roleBinding},
306 Client: reconciliation.RoleBindingClientAdapter{Client: client.RbacV1(), NamespaceClient: client.CoreV1().Namespaces()},
307 Confirm: true,
308 }
309
310 err := retryOnConflictOrServiceUnavailable(retry.DefaultBackoff, func() error {
311 result, err := opts.Run()
312 if err != nil {
313 return err
314 }
315 switch {
316 case result.Protected && result.Operation != reconciliation.ReconcileNone:
317 klog.Warningf("skipped reconcile-protected rolebinding.%s/%s in %v with missing subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects)
318 case result.Operation == reconciliation.ReconcileUpdate:
319 klog.V(2).Infof("updated rolebinding.%s/%s in %v with additional subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects)
320 case result.Operation == reconciliation.ReconcileCreate:
321 klog.V(2).Infof("created rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.Name, namespace)
322 case result.Operation == reconciliation.ReconcileRecreate:
323 klog.V(2).Infof("recreated rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.Name, namespace)
324 }
325 return nil
326 })
327 if err != nil {
328
329 utilruntime.HandleError(fmt.Errorf("unable to reconcile rolebinding.%s/%s in %v: %v", rbac.GroupName, roleBinding.Name, namespace, err))
330 failedReconciliation = true
331 }
332 }
333 }
334
335 if failedReconciliation {
336 return false, nil
337 }
338
339 return true, nil
340 }
341
342 func (p RESTStorageProvider) GroupName() string {
343 return rbac.GroupName
344 }
345
346
347
348 func primeAggregatedClusterRoles(clusterRolesToAggregate map[string]string, clusterRoleClient rbacv1client.ClusterRolesGetter) error {
349 for oldName, newName := range clusterRolesToAggregate {
350 _, err := clusterRoleClient.ClusterRoles().Get(context.TODO(), newName, metav1.GetOptions{})
351 if err == nil {
352 continue
353 }
354 if !apierrors.IsNotFound(err) {
355 return err
356 }
357
358 existingRole, err := clusterRoleClient.ClusterRoles().Get(context.TODO(), oldName, metav1.GetOptions{})
359 if apierrors.IsNotFound(err) {
360 continue
361 }
362 if err != nil {
363 return err
364 }
365 if existingRole.AggregationRule != nil {
366
367 return nil
368 }
369 klog.V(1).Infof("migrating %v to %v", existingRole.Name, newName)
370 existingRole.Name = newName
371 existingRole.ResourceVersion = ""
372 if _, err := clusterRoleClient.ClusterRoles().Create(context.TODO(), existingRole, metav1.CreateOptions{}); err != nil && !apierrors.IsAlreadyExists(err) {
373 return err
374 }
375 }
376
377 return nil
378 }
379
380
381
382
383 func primeSplitClusterRoleBindings(clusterRoleBindingToSplit map[string]rbacapiv1.ClusterRoleBinding, clusterRoleBindingClient rbacv1client.ClusterRoleBindingsGetter) error {
384 for existingBindingName, clusterRoleBindingToCreate := range clusterRoleBindingToSplit {
385
386 existingRoleBinding, err := clusterRoleBindingClient.ClusterRoleBindings().Get(context.TODO(), existingBindingName, metav1.GetOptions{})
387 if apierrors.IsNotFound(err) {
388 continue
389 }
390 if err != nil {
391 return err
392 }
393
394
395 _, err = clusterRoleBindingClient.ClusterRoleBindings().Get(context.TODO(), clusterRoleBindingToCreate.Name, metav1.GetOptions{})
396 if err == nil {
397 continue
398 }
399 if !apierrors.IsNotFound(err) {
400 return err
401 }
402
403
404
405 klog.V(1).Infof("copying subjects, labels, and annotations from ClusterRoleBinding %q to template %q", existingBindingName, clusterRoleBindingToCreate.Name)
406 newCRB := clusterRoleBindingToCreate.DeepCopy()
407 newCRB.Subjects = existingRoleBinding.Subjects
408 newCRB.Labels = existingRoleBinding.Labels
409 newCRB.Annotations = existingRoleBinding.Annotations
410 if _, err := clusterRoleBindingClient.ClusterRoleBindings().Create(context.TODO(), newCRB, metav1.CreateOptions{}); err != nil && !apierrors.IsAlreadyExists(err) {
411 return err
412 }
413 }
414 return nil
415 }
416
View as plain text