storage_rbac.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package rest
  14. import (
  15. "context"
  16. "fmt"
  17. "time"
  18. "k8s.io/klog"
  19. rbacapiv1 "k8s.io/api/rbac/v1"
  20. rbacapiv1alpha1 "k8s.io/api/rbac/v1alpha1"
  21. rbacapiv1beta1 "k8s.io/api/rbac/v1beta1"
  22. apierrors "k8s.io/apimachinery/pkg/api/errors"
  23. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  24. "k8s.io/apimachinery/pkg/runtime/schema"
  25. utilruntime "k8s.io/apimachinery/pkg/util/runtime"
  26. "k8s.io/apimachinery/pkg/util/wait"
  27. "k8s.io/apiserver/pkg/authorization/authorizer"
  28. "k8s.io/apiserver/pkg/registry/generic"
  29. "k8s.io/apiserver/pkg/registry/rest"
  30. genericapiserver "k8s.io/apiserver/pkg/server"
  31. serverstorage "k8s.io/apiserver/pkg/server/storage"
  32. corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
  33. rbacv1client "k8s.io/client-go/kubernetes/typed/rbac/v1"
  34. "k8s.io/client-go/util/retry"
  35. "k8s.io/kubernetes/pkg/api/legacyscheme"
  36. "k8s.io/kubernetes/pkg/apis/rbac"
  37. "k8s.io/kubernetes/pkg/registry/rbac/clusterrole"
  38. clusterrolepolicybased "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/policybased"
  39. clusterrolestore "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/storage"
  40. "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding"
  41. clusterrolebindingpolicybased "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/policybased"
  42. clusterrolebindingstore "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/storage"
  43. "k8s.io/kubernetes/pkg/registry/rbac/reconciliation"
  44. "k8s.io/kubernetes/pkg/registry/rbac/role"
  45. rolepolicybased "k8s.io/kubernetes/pkg/registry/rbac/role/policybased"
  46. rolestore "k8s.io/kubernetes/pkg/registry/rbac/role/storage"
  47. "k8s.io/kubernetes/pkg/registry/rbac/rolebinding"
  48. rolebindingpolicybased "k8s.io/kubernetes/pkg/registry/rbac/rolebinding/policybased"
  49. rolebindingstore "k8s.io/kubernetes/pkg/registry/rbac/rolebinding/storage"
  50. rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
  51. "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy"
  52. )
  53. const PostStartHookName = "rbac/bootstrap-roles"
  54. type RESTStorageProvider struct {
  55. Authorizer authorizer.Authorizer
  56. }
  57. var _ genericapiserver.PostStartHookProvider = RESTStorageProvider{}
  58. func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool, error) {
  59. apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(rbac.GroupName, legacyscheme.Scheme, legacyscheme.ParameterCodec, legacyscheme.Codecs)
  60. // If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities.
  61. // TODO refactor the plumbing to provide the information in the APIGroupInfo
  62. if apiResourceConfigSource.VersionEnabled(rbacapiv1alpha1.SchemeGroupVersion) {
  63. if storageMap, err := p.storage(rbacapiv1alpha1.SchemeGroupVersion, apiResourceConfigSource, restOptionsGetter); err != nil {
  64. return genericapiserver.APIGroupInfo{}, false, err
  65. } else {
  66. apiGroupInfo.VersionedResourcesStorageMap[rbacapiv1alpha1.SchemeGroupVersion.Version] = storageMap
  67. }
  68. }
  69. if apiResourceConfigSource.VersionEnabled(rbacapiv1beta1.SchemeGroupVersion) {
  70. if storageMap, err := p.storage(rbacapiv1beta1.SchemeGroupVersion, apiResourceConfigSource, restOptionsGetter); err != nil {
  71. return genericapiserver.APIGroupInfo{}, false, err
  72. } else {
  73. apiGroupInfo.VersionedResourcesStorageMap[rbacapiv1beta1.SchemeGroupVersion.Version] = storageMap
  74. }
  75. }
  76. if apiResourceConfigSource.VersionEnabled(rbacapiv1.SchemeGroupVersion) {
  77. if storageMap, err := p.storage(rbacapiv1.SchemeGroupVersion, apiResourceConfigSource, restOptionsGetter); err != nil {
  78. return genericapiserver.APIGroupInfo{}, false, err
  79. } else {
  80. apiGroupInfo.VersionedResourcesStorageMap[rbacapiv1.SchemeGroupVersion.Version] = storageMap
  81. }
  82. }
  83. return apiGroupInfo, true, nil
  84. }
  85. func (p RESTStorageProvider) storage(version schema.GroupVersion, apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
  86. storage := map[string]rest.Storage{}
  87. rolesStorage, err := rolestore.NewREST(restOptionsGetter)
  88. if err != nil {
  89. return storage, err
  90. }
  91. roleBindingsStorage, err := rolebindingstore.NewREST(restOptionsGetter)
  92. if err != nil {
  93. return storage, err
  94. }
  95. clusterRolesStorage, err := clusterrolestore.NewREST(restOptionsGetter)
  96. if err != nil {
  97. return storage, err
  98. }
  99. clusterRoleBindingsStorage, err := clusterrolebindingstore.NewREST(restOptionsGetter)
  100. if err != nil {
  101. return storage, err
  102. }
  103. authorizationRuleResolver := rbacregistryvalidation.NewDefaultRuleResolver(
  104. role.AuthorizerAdapter{Registry: role.NewRegistry(rolesStorage)},
  105. rolebinding.AuthorizerAdapter{Registry: rolebinding.NewRegistry(roleBindingsStorage)},
  106. clusterrole.AuthorizerAdapter{Registry: clusterrole.NewRegistry(clusterRolesStorage)},
  107. clusterrolebinding.AuthorizerAdapter{Registry: clusterrolebinding.NewRegistry(clusterRoleBindingsStorage)},
  108. )
  109. // roles
  110. storage["roles"] = rolepolicybased.NewStorage(rolesStorage, p.Authorizer, authorizationRuleResolver)
  111. // rolebindings
  112. storage["rolebindings"] = rolebindingpolicybased.NewStorage(roleBindingsStorage, p.Authorizer, authorizationRuleResolver)
  113. // clusterroles
  114. storage["clusterroles"] = clusterrolepolicybased.NewStorage(clusterRolesStorage, p.Authorizer, authorizationRuleResolver)
  115. // clusterrolebindings
  116. storage["clusterrolebindings"] = clusterrolebindingpolicybased.NewStorage(clusterRoleBindingsStorage, p.Authorizer, authorizationRuleResolver)
  117. return storage, nil
  118. }
  119. func (p RESTStorageProvider) PostStartHook() (string, genericapiserver.PostStartHookFunc, error) {
  120. policy := &PolicyData{
  121. ClusterRoles: append(bootstrappolicy.ClusterRoles(), bootstrappolicy.ControllerRoles()...),
  122. ClusterRoleBindings: append(bootstrappolicy.ClusterRoleBindings(), bootstrappolicy.ControllerRoleBindings()...),
  123. Roles: bootstrappolicy.NamespaceRoles(),
  124. RoleBindings: bootstrappolicy.NamespaceRoleBindings(),
  125. ClusterRolesToAggregate: bootstrappolicy.ClusterRolesToAggregate(),
  126. ClusterRoleBindingsToSplit: bootstrappolicy.ClusterRoleBindingsToSplit(),
  127. }
  128. return PostStartHookName, policy.EnsureRBACPolicy(), nil
  129. }
  130. type PolicyData struct {
  131. ClusterRoles []rbacapiv1.ClusterRole
  132. ClusterRoleBindings []rbacapiv1.ClusterRoleBinding
  133. Roles map[string][]rbacapiv1.Role
  134. RoleBindings map[string][]rbacapiv1.RoleBinding
  135. // ClusterRolesToAggregate maps from previous clusterrole name to the new clusterrole name
  136. ClusterRolesToAggregate map[string]string
  137. // ClusterRoleBindingsToSplit maps from previous ClusterRoleBinding Name to a template for the new ClusterRoleBinding
  138. ClusterRoleBindingsToSplit map[string]rbacapiv1.ClusterRoleBinding
  139. }
  140. func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc {
  141. return func(hookContext genericapiserver.PostStartHookContext) error {
  142. // initializing roles is really important. On some e2e runs, we've seen cases where etcd is down when the server
  143. // starts, the roles don't initialize, and nothing works.
  144. err := wait.Poll(1*time.Second, 30*time.Second, func() (done bool, err error) {
  145. coreclientset, err := corev1client.NewForConfig(hookContext.LoopbackClientConfig)
  146. if err != nil {
  147. utilruntime.HandleError(fmt.Errorf("unable to initialize client: %v", err))
  148. return false, nil
  149. }
  150. clientset, err := rbacv1client.NewForConfig(hookContext.LoopbackClientConfig)
  151. if err != nil {
  152. utilruntime.HandleError(fmt.Errorf("unable to initialize client: %v", err))
  153. return false, nil
  154. }
  155. // Make sure etcd is responding before we start reconciling
  156. if _, err := clientset.ClusterRoles().List(context.TODO(), metav1.ListOptions{}); err != nil {
  157. utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err))
  158. return false, nil
  159. }
  160. if _, err := clientset.ClusterRoleBindings().List(context.TODO(), metav1.ListOptions{}); err != nil {
  161. utilruntime.HandleError(fmt.Errorf("unable to initialize clusterrolebindings: %v", err))
  162. return false, nil
  163. }
  164. // if the new cluster roles to aggregate do not yet exist, then we need to copy the old roles if they don't exist
  165. // in new locations
  166. if err := primeAggregatedClusterRoles(p.ClusterRolesToAggregate, clientset); err != nil {
  167. utilruntime.HandleError(fmt.Errorf("unable to prime aggregated clusterroles: %v", err))
  168. return false, nil
  169. }
  170. if err := primeSplitClusterRoleBindings(p.ClusterRoleBindingsToSplit, clientset); err != nil {
  171. utilruntime.HandleError(fmt.Errorf("unable to prime split ClusterRoleBindings: %v", err))
  172. return false, nil
  173. }
  174. // ensure bootstrap roles are created or reconciled
  175. for _, clusterRole := range p.ClusterRoles {
  176. opts := reconciliation.ReconcileRoleOptions{
  177. Role: reconciliation.ClusterRoleRuleOwner{ClusterRole: &clusterRole},
  178. Client: reconciliation.ClusterRoleModifier{Client: clientset.ClusterRoles()},
  179. Confirm: true,
  180. }
  181. err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
  182. result, err := opts.Run()
  183. if err != nil {
  184. return err
  185. }
  186. switch {
  187. case result.Protected && result.Operation != reconciliation.ReconcileNone:
  188. klog.Warningf("skipped reconcile-protected clusterrole.%s/%s with missing permissions: %v", rbac.GroupName, clusterRole.Name, result.MissingRules)
  189. case result.Operation == reconciliation.ReconcileUpdate:
  190. klog.V(2).Infof("updated clusterrole.%s/%s with additional permissions: %v", rbac.GroupName, clusterRole.Name, result.MissingRules)
  191. case result.Operation == reconciliation.ReconcileCreate:
  192. klog.V(2).Infof("created clusterrole.%s/%s", rbac.GroupName, clusterRole.Name)
  193. }
  194. return nil
  195. })
  196. if err != nil {
  197. // don't fail on failures, try to create as many as you can
  198. utilruntime.HandleError(fmt.Errorf("unable to reconcile clusterrole.%s/%s: %v", rbac.GroupName, clusterRole.Name, err))
  199. }
  200. }
  201. // ensure bootstrap rolebindings are created or reconciled
  202. for _, clusterRoleBinding := range p.ClusterRoleBindings {
  203. opts := reconciliation.ReconcileRoleBindingOptions{
  204. RoleBinding: reconciliation.ClusterRoleBindingAdapter{ClusterRoleBinding: &clusterRoleBinding},
  205. Client: reconciliation.ClusterRoleBindingClientAdapter{Client: clientset.ClusterRoleBindings()},
  206. Confirm: true,
  207. }
  208. err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
  209. result, err := opts.Run()
  210. if err != nil {
  211. return err
  212. }
  213. switch {
  214. case result.Protected && result.Operation != reconciliation.ReconcileNone:
  215. klog.Warningf("skipped reconcile-protected clusterrolebinding.%s/%s with missing subjects: %v", rbac.GroupName, clusterRoleBinding.Name, result.MissingSubjects)
  216. case result.Operation == reconciliation.ReconcileUpdate:
  217. klog.V(2).Infof("updated clusterrolebinding.%s/%s with additional subjects: %v", rbac.GroupName, clusterRoleBinding.Name, result.MissingSubjects)
  218. case result.Operation == reconciliation.ReconcileCreate:
  219. klog.V(2).Infof("created clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name)
  220. case result.Operation == reconciliation.ReconcileRecreate:
  221. klog.V(2).Infof("recreated clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name)
  222. }
  223. return nil
  224. })
  225. if err != nil {
  226. // don't fail on failures, try to create as many as you can
  227. utilruntime.HandleError(fmt.Errorf("unable to reconcile clusterrolebinding.%s/%s: %v", rbac.GroupName, clusterRoleBinding.Name, err))
  228. }
  229. }
  230. // ensure bootstrap namespaced roles are created or reconciled
  231. for namespace, roles := range p.Roles {
  232. for _, role := range roles {
  233. opts := reconciliation.ReconcileRoleOptions{
  234. Role: reconciliation.RoleRuleOwner{Role: &role},
  235. Client: reconciliation.RoleModifier{Client: clientset, NamespaceClient: coreclientset.Namespaces()},
  236. Confirm: true,
  237. }
  238. err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
  239. result, err := opts.Run()
  240. if err != nil {
  241. return err
  242. }
  243. switch {
  244. case result.Protected && result.Operation != reconciliation.ReconcileNone:
  245. klog.Warningf("skipped reconcile-protected role.%s/%s in %v with missing permissions: %v", rbac.GroupName, role.Name, namespace, result.MissingRules)
  246. case result.Operation == reconciliation.ReconcileUpdate:
  247. klog.V(2).Infof("updated role.%s/%s in %v with additional permissions: %v", rbac.GroupName, role.Name, namespace, result.MissingRules)
  248. case result.Operation == reconciliation.ReconcileCreate:
  249. klog.V(2).Infof("created role.%s/%s in %v", rbac.GroupName, role.Name, namespace)
  250. }
  251. return nil
  252. })
  253. if err != nil {
  254. // don't fail on failures, try to create as many as you can
  255. utilruntime.HandleError(fmt.Errorf("unable to reconcile role.%s/%s in %v: %v", rbac.GroupName, role.Name, namespace, err))
  256. }
  257. }
  258. }
  259. // ensure bootstrap namespaced rolebindings are created or reconciled
  260. for namespace, roleBindings := range p.RoleBindings {
  261. for _, roleBinding := range roleBindings {
  262. opts := reconciliation.ReconcileRoleBindingOptions{
  263. RoleBinding: reconciliation.RoleBindingAdapter{RoleBinding: &roleBinding},
  264. Client: reconciliation.RoleBindingClientAdapter{Client: clientset, NamespaceClient: coreclientset.Namespaces()},
  265. Confirm: true,
  266. }
  267. err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
  268. result, err := opts.Run()
  269. if err != nil {
  270. return err
  271. }
  272. switch {
  273. case result.Protected && result.Operation != reconciliation.ReconcileNone:
  274. klog.Warningf("skipped reconcile-protected rolebinding.%s/%s in %v with missing subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects)
  275. case result.Operation == reconciliation.ReconcileUpdate:
  276. klog.V(2).Infof("updated rolebinding.%s/%s in %v with additional subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects)
  277. case result.Operation == reconciliation.ReconcileCreate:
  278. klog.V(2).Infof("created rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.Name, namespace)
  279. case result.Operation == reconciliation.ReconcileRecreate:
  280. klog.V(2).Infof("recreated rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.Name, namespace)
  281. }
  282. return nil
  283. })
  284. if err != nil {
  285. // don't fail on failures, try to create as many as you can
  286. utilruntime.HandleError(fmt.Errorf("unable to reconcile rolebinding.%s/%s in %v: %v", rbac.GroupName, roleBinding.Name, namespace, err))
  287. }
  288. }
  289. }
  290. return true, nil
  291. })
  292. // if we're never able to make it through initialization, kill the API server
  293. if err != nil {
  294. return fmt.Errorf("unable to initialize roles: %v", err)
  295. }
  296. return nil
  297. }
  298. }
  299. func (p RESTStorageProvider) GroupName() string {
  300. return rbac.GroupName
  301. }
  302. // primeAggregatedClusterRoles copies roles that have transitioned to aggregated roles and may need to pick up changes
  303. // that were done to the legacy roles.
  304. func primeAggregatedClusterRoles(clusterRolesToAggregate map[string]string, clusterRoleClient rbacv1client.ClusterRolesGetter) error {
  305. for oldName, newName := range clusterRolesToAggregate {
  306. _, err := clusterRoleClient.ClusterRoles().Get(context.TODO(), newName, metav1.GetOptions{})
  307. if err == nil {
  308. continue
  309. }
  310. if !apierrors.IsNotFound(err) {
  311. return err
  312. }
  313. existingRole, err := clusterRoleClient.ClusterRoles().Get(context.TODO(), oldName, metav1.GetOptions{})
  314. if apierrors.IsNotFound(err) {
  315. continue
  316. }
  317. if err != nil {
  318. return err
  319. }
  320. if existingRole.AggregationRule != nil {
  321. // the old role already moved to an aggregated role, so there are no custom rules to migrate at this point
  322. return nil
  323. }
  324. klog.V(1).Infof("migrating %v to %v", existingRole.Name, newName)
  325. existingRole.Name = newName
  326. existingRole.ResourceVersion = "" // clear this so the object can be created.
  327. if _, err := clusterRoleClient.ClusterRoles().Create(context.TODO(), existingRole, metav1.CreateOptions{}); err != nil && !apierrors.IsAlreadyExists(err) {
  328. return err
  329. }
  330. }
  331. return nil
  332. }
  333. // primeSplitClusterRoleBindings ensures the existence of target ClusterRoleBindings
  334. // by copying Subjects, Annotations, and Labels from the specified source
  335. // ClusterRoleBinding, if present.
  336. func primeSplitClusterRoleBindings(clusterRoleBindingToSplit map[string]rbacapiv1.ClusterRoleBinding, clusterRoleBindingClient rbacv1client.ClusterRoleBindingsGetter) error {
  337. for existingBindingName, clusterRoleBindingToCreate := range clusterRoleBindingToSplit {
  338. // If source ClusterRoleBinding does not exist, do nothing.
  339. existingRoleBinding, err := clusterRoleBindingClient.ClusterRoleBindings().Get(context.TODO(), existingBindingName, metav1.GetOptions{})
  340. if apierrors.IsNotFound(err) {
  341. continue
  342. }
  343. if err != nil {
  344. return err
  345. }
  346. // If the target ClusterRoleBinding already exists, do nothing.
  347. _, err = clusterRoleBindingClient.ClusterRoleBindings().Get(context.TODO(), clusterRoleBindingToCreate.Name, metav1.GetOptions{})
  348. if err == nil {
  349. continue
  350. }
  351. if !apierrors.IsNotFound(err) {
  352. return err
  353. }
  354. // If the source exists, but the target does not,
  355. // copy the subjects, labels, and annotations from the former to create the latter.
  356. klog.V(1).Infof("copying subjects, labels, and annotations from ClusterRoleBinding %q to template %q", existingBindingName, clusterRoleBindingToCreate.Name)
  357. newCRB := clusterRoleBindingToCreate.DeepCopy()
  358. newCRB.Subjects = existingRoleBinding.Subjects
  359. newCRB.Labels = existingRoleBinding.Labels
  360. newCRB.Annotations = existingRoleBinding.Annotations
  361. if _, err := clusterRoleBindingClient.ClusterRoleBindings().Create(context.TODO(), newCRB, metav1.CreateOptions{}); err != nil && !apierrors.IsAlreadyExists(err) {
  362. return err
  363. }
  364. }
  365. return nil
  366. }