gc_admission.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  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 gc
  14. import (
  15. "context"
  16. "fmt"
  17. "io"
  18. apiequality "k8s.io/apimachinery/pkg/api/equality"
  19. "k8s.io/apimachinery/pkg/api/meta"
  20. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  21. "k8s.io/apimachinery/pkg/runtime"
  22. "k8s.io/apimachinery/pkg/runtime/schema"
  23. "k8s.io/apimachinery/pkg/types"
  24. "k8s.io/apiserver/pkg/admission"
  25. "k8s.io/apiserver/pkg/authorization/authorizer"
  26. )
  27. // PluginName indicates name of admission plugin.
  28. const PluginName = "OwnerReferencesPermissionEnforcement"
  29. // Register registers a plugin
  30. func Register(plugins *admission.Plugins) {
  31. plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
  32. // the pods/status endpoint is ignored by this plugin since old kubelets
  33. // corrupt them. the pod status strategy ensures status updates cannot mutate
  34. // ownerRef.
  35. whiteList := []whiteListItem{
  36. {
  37. groupResource: schema.GroupResource{Resource: "pods"},
  38. subresource: "status",
  39. },
  40. }
  41. return &gcPermissionsEnforcement{
  42. Handler: admission.NewHandler(admission.Create, admission.Update),
  43. whiteList: whiteList,
  44. }, nil
  45. })
  46. }
  47. // gcPermissionsEnforcement is an implementation of admission.Interface.
  48. type gcPermissionsEnforcement struct {
  49. *admission.Handler
  50. authorizer authorizer.Authorizer
  51. restMapper meta.RESTMapper
  52. // items in this whitelist are ignored upon admission.
  53. // any item in this list must protect against ownerRef mutations
  54. // via strategy enforcement.
  55. whiteList []whiteListItem
  56. }
  57. var _ admission.ValidationInterface = &gcPermissionsEnforcement{}
  58. // whiteListItem describes an entry in a whitelist ignored by gc permission enforcement.
  59. type whiteListItem struct {
  60. groupResource schema.GroupResource
  61. subresource string
  62. }
  63. // isWhiteListed returns true if the specified item is in the whitelist.
  64. func (a *gcPermissionsEnforcement) isWhiteListed(groupResource schema.GroupResource, subresource string) bool {
  65. for _, item := range a.whiteList {
  66. if item.groupResource == groupResource && item.subresource == subresource {
  67. return true
  68. }
  69. }
  70. return false
  71. }
  72. func (a *gcPermissionsEnforcement) Validate(ctx context.Context, attributes admission.Attributes, o admission.ObjectInterfaces) (err error) {
  73. // // if the request is in the whitelist, we skip mutation checks for this resource.
  74. if a.isWhiteListed(attributes.GetResource().GroupResource(), attributes.GetSubresource()) {
  75. return nil
  76. }
  77. // if we aren't changing owner references, then the edit is always allowed
  78. if !isChangingOwnerReference(attributes.GetObject(), attributes.GetOldObject()) {
  79. return nil
  80. }
  81. // if you are creating a thing, you should always be allowed to set an owner ref since you logically had the power
  82. // to never create it. We still need to check block owner deletion below, because the power to delete does not
  83. // imply the power to prevent deletion on other resources.
  84. if attributes.GetOperation() != admission.Create {
  85. deleteAttributes := authorizer.AttributesRecord{
  86. User: attributes.GetUserInfo(),
  87. Verb: "delete",
  88. Namespace: attributes.GetNamespace(),
  89. APIGroup: attributes.GetResource().Group,
  90. APIVersion: attributes.GetResource().Version,
  91. Resource: attributes.GetResource().Resource,
  92. Subresource: attributes.GetSubresource(),
  93. Name: attributes.GetName(),
  94. ResourceRequest: true,
  95. Path: "",
  96. }
  97. decision, reason, err := a.authorizer.Authorize(ctx, deleteAttributes)
  98. if decision != authorizer.DecisionAllow {
  99. return admission.NewForbidden(attributes, fmt.Errorf("cannot set an ownerRef on a resource you can't delete: %v, %v", reason, err))
  100. }
  101. }
  102. // Further check if the user is setting ownerReference.blockOwnerDeletion to
  103. // true. If so, only allows the change if the user has delete permission of
  104. // the _OWNER_
  105. newBlockingRefs := newBlockingOwnerDeletionRefs(attributes.GetObject(), attributes.GetOldObject())
  106. for _, ref := range newBlockingRefs {
  107. records, err := a.ownerRefToDeleteAttributeRecords(ref, attributes)
  108. if err != nil {
  109. return admission.NewForbidden(attributes, fmt.Errorf("cannot set blockOwnerDeletion in this case because cannot find RESTMapping for APIVersion %s Kind %s: %v", ref.APIVersion, ref.Kind, err))
  110. }
  111. // Multiple records are returned if ref.Kind could map to multiple
  112. // resources. User needs to have delete permission on all the
  113. // matched Resources.
  114. for _, record := range records {
  115. decision, reason, err := a.authorizer.Authorize(ctx, record)
  116. if decision != authorizer.DecisionAllow {
  117. return admission.NewForbidden(attributes, fmt.Errorf("cannot set blockOwnerDeletion if an ownerReference refers to a resource you can't set finalizers on: %v, %v", reason, err))
  118. }
  119. }
  120. }
  121. return nil
  122. }
  123. func isChangingOwnerReference(newObj, oldObj runtime.Object) bool {
  124. newMeta, err := meta.Accessor(newObj)
  125. if err != nil {
  126. // if we don't have objectmeta, we don't have the object reference
  127. return false
  128. }
  129. if oldObj == nil {
  130. return len(newMeta.GetOwnerReferences()) > 0
  131. }
  132. oldMeta, err := meta.Accessor(oldObj)
  133. if err != nil {
  134. // if we don't have objectmeta, we don't have the object reference
  135. return false
  136. }
  137. // compare the old and new. If they aren't the same, then we're trying to change an ownerRef
  138. oldOwners := oldMeta.GetOwnerReferences()
  139. newOwners := newMeta.GetOwnerReferences()
  140. if len(oldOwners) != len(newOwners) {
  141. return true
  142. }
  143. for i := range oldOwners {
  144. if !apiequality.Semantic.DeepEqual(oldOwners[i], newOwners[i]) {
  145. return true
  146. }
  147. }
  148. return false
  149. }
  150. // Translates ref to a DeleteAttribute deleting the object referred by the ref.
  151. // OwnerReference only records the object kind, which might map to multiple
  152. // resources, so multiple DeleteAttribute might be returned.
  153. func (a *gcPermissionsEnforcement) ownerRefToDeleteAttributeRecords(ref metav1.OwnerReference, attributes admission.Attributes) ([]authorizer.AttributesRecord, error) {
  154. var ret []authorizer.AttributesRecord
  155. groupVersion, err := schema.ParseGroupVersion(ref.APIVersion)
  156. if err != nil {
  157. return ret, err
  158. }
  159. mappings, err := a.restMapper.RESTMappings(schema.GroupKind{Group: groupVersion.Group, Kind: ref.Kind}, groupVersion.Version)
  160. if err != nil {
  161. return ret, err
  162. }
  163. for _, mapping := range mappings {
  164. ar := authorizer.AttributesRecord{
  165. User: attributes.GetUserInfo(),
  166. Verb: "update",
  167. APIGroup: mapping.Resource.Group,
  168. APIVersion: mapping.Resource.Version,
  169. Resource: mapping.Resource.Resource,
  170. Subresource: "finalizers",
  171. Name: ref.Name,
  172. ResourceRequest: true,
  173. Path: "",
  174. }
  175. if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
  176. // if the owner is namespaced, it must be in the same namespace as the dependent is.
  177. ar.Namespace = attributes.GetNamespace()
  178. }
  179. ret = append(ret, ar)
  180. }
  181. return ret, nil
  182. }
  183. // only keeps the blocking refs
  184. func blockingOwnerRefs(refs []metav1.OwnerReference) []metav1.OwnerReference {
  185. var ret []metav1.OwnerReference
  186. for _, ref := range refs {
  187. if ref.BlockOwnerDeletion != nil && *ref.BlockOwnerDeletion == true {
  188. ret = append(ret, ref)
  189. }
  190. }
  191. return ret
  192. }
  193. func indexByUID(refs []metav1.OwnerReference) map[types.UID]metav1.OwnerReference {
  194. ret := make(map[types.UID]metav1.OwnerReference)
  195. for _, ref := range refs {
  196. ret[ref.UID] = ref
  197. }
  198. return ret
  199. }
  200. // Returns new blocking ownerReferences, and references whose blockOwnerDeletion
  201. // field is changed from nil or false to true.
  202. func newBlockingOwnerDeletionRefs(newObj, oldObj runtime.Object) []metav1.OwnerReference {
  203. newMeta, err := meta.Accessor(newObj)
  204. if err != nil {
  205. // if we don't have objectmeta, we don't have the object reference
  206. return nil
  207. }
  208. newRefs := newMeta.GetOwnerReferences()
  209. blockingNewRefs := blockingOwnerRefs(newRefs)
  210. if len(blockingNewRefs) == 0 {
  211. return nil
  212. }
  213. if oldObj == nil {
  214. return blockingNewRefs
  215. }
  216. oldMeta, err := meta.Accessor(oldObj)
  217. if err != nil {
  218. // if we don't have objectmeta, treat it as if all the ownerReference are newly created
  219. return blockingNewRefs
  220. }
  221. var ret []metav1.OwnerReference
  222. indexedOldRefs := indexByUID(oldMeta.GetOwnerReferences())
  223. for _, ref := range blockingNewRefs {
  224. oldRef, ok := indexedOldRefs[ref.UID]
  225. if !ok {
  226. // if ref is newly added, and it's blocking, then returns it.
  227. ret = append(ret, ref)
  228. continue
  229. }
  230. wasNotBlocking := oldRef.BlockOwnerDeletion == nil || *oldRef.BlockOwnerDeletion == false
  231. if wasNotBlocking {
  232. ret = append(ret, ref)
  233. }
  234. }
  235. return ret
  236. }
  237. func (a *gcPermissionsEnforcement) SetAuthorizer(authorizer authorizer.Authorizer) {
  238. a.authorizer = authorizer
  239. }
  240. func (a *gcPermissionsEnforcement) SetRESTMapper(restMapper meta.RESTMapper) {
  241. a.restMapper = restMapper
  242. }
  243. func (a *gcPermissionsEnforcement) ValidateInitialization() error {
  244. if a.authorizer == nil {
  245. return fmt.Errorf("missing authorizer")
  246. }
  247. if a.restMapper == nil {
  248. return fmt.Errorf("missing restMapper")
  249. }
  250. return nil
  251. }