1
16
17 package gc
18
19 import (
20 "context"
21 "fmt"
22 "io"
23
24 apiequality "k8s.io/apimachinery/pkg/api/equality"
25 "k8s.io/apimachinery/pkg/api/meta"
26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27 "k8s.io/apimachinery/pkg/runtime"
28 "k8s.io/apimachinery/pkg/runtime/schema"
29 "k8s.io/apimachinery/pkg/types"
30 "k8s.io/apiserver/pkg/admission"
31 "k8s.io/apiserver/pkg/authentication/user"
32 "k8s.io/apiserver/pkg/authorization/authorizer"
33 )
34
35
36 const PluginName = "OwnerReferencesPermissionEnforcement"
37
38
39 func Register(plugins *admission.Plugins) {
40 plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
41
42
43
44 whiteList := []whiteListItem{
45 {
46 groupResource: schema.GroupResource{Resource: "pods"},
47 subresource: "status",
48 },
49 }
50 return &gcPermissionsEnforcement{
51 Handler: admission.NewHandler(admission.Create, admission.Update),
52 whiteList: whiteList,
53 }, nil
54 })
55 }
56
57
58 type gcPermissionsEnforcement struct {
59 *admission.Handler
60
61 authorizer authorizer.Authorizer
62
63 restMapper meta.RESTMapper
64
65
66
67
68 whiteList []whiteListItem
69 }
70
71 var _ admission.ValidationInterface = &gcPermissionsEnforcement{}
72
73
74 type whiteListItem struct {
75 groupResource schema.GroupResource
76 subresource string
77 }
78
79
80 func (a *gcPermissionsEnforcement) isWhiteListed(groupResource schema.GroupResource, subresource string) bool {
81 for _, item := range a.whiteList {
82 if item.groupResource == groupResource && item.subresource == subresource {
83 return true
84 }
85 }
86 return false
87 }
88
89 func (a *gcPermissionsEnforcement) Validate(ctx context.Context, attributes admission.Attributes, o admission.ObjectInterfaces) (err error) {
90
91 if a.isWhiteListed(attributes.GetResource().GroupResource(), attributes.GetSubresource()) {
92 return nil
93 }
94
95
96 if !isChangingOwnerReference(attributes.GetObject(), attributes.GetOldObject()) {
97 return nil
98 }
99
100
101
102
103 if attributes.GetOperation() != admission.Create {
104 deleteAttributes := authorizer.AttributesRecord{
105 User: attributes.GetUserInfo(),
106 Verb: "delete",
107 Namespace: attributes.GetNamespace(),
108 APIGroup: attributes.GetResource().Group,
109 APIVersion: attributes.GetResource().Version,
110 Resource: attributes.GetResource().Resource,
111 Subresource: attributes.GetSubresource(),
112 Name: attributes.GetName(),
113 ResourceRequest: true,
114 Path: "",
115 }
116 decision, reason, err := a.authorizer.Authorize(ctx, deleteAttributes)
117 if decision != authorizer.DecisionAllow {
118 return admission.NewForbidden(attributes, fmt.Errorf("cannot set an ownerRef on a resource you can't delete: %v, %v", reason, err))
119 }
120 }
121
122
123
124
125 newBlockingRefs := newBlockingOwnerDeletionRefs(attributes.GetObject(), attributes.GetOldObject())
126 if len(newBlockingRefs) == 0 {
127 return nil
128 }
129
130
131
132
133
134 if decision, _, _ := a.authorizer.Authorize(ctx, finalizeAnythingRecord(attributes.GetUserInfo())); decision == authorizer.DecisionAllow {
135 return nil
136 }
137
138 for _, ref := range newBlockingRefs {
139 records, err := a.ownerRefToDeleteAttributeRecords(ref, attributes)
140 if err != nil {
141 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))
142 }
143
144
145
146 for _, record := range records {
147 decision, reason, err := a.authorizer.Authorize(ctx, record)
148 if decision != authorizer.DecisionAllow {
149 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))
150 }
151 }
152 }
153
154 return nil
155
156 }
157
158 func isChangingOwnerReference(newObj, oldObj runtime.Object) bool {
159 newMeta, err := meta.Accessor(newObj)
160 if err != nil {
161
162 return false
163 }
164
165 if oldObj == nil {
166 return len(newMeta.GetOwnerReferences()) > 0
167 }
168 oldMeta, err := meta.Accessor(oldObj)
169 if err != nil {
170
171 return false
172 }
173
174
175 oldOwners := oldMeta.GetOwnerReferences()
176 newOwners := newMeta.GetOwnerReferences()
177 if len(oldOwners) != len(newOwners) {
178 return true
179 }
180 for i := range oldOwners {
181 if !apiequality.Semantic.DeepEqual(oldOwners[i], newOwners[i]) {
182 return true
183 }
184 }
185
186 return false
187 }
188
189 func finalizeAnythingRecord(userInfo user.Info) authorizer.AttributesRecord {
190 return authorizer.AttributesRecord{
191 User: userInfo,
192 Verb: "update",
193 APIGroup: "*",
194 APIVersion: "*",
195 Resource: "*",
196 Subresource: "finalizers",
197 Name: "*",
198 ResourceRequest: true,
199 Path: "",
200 }
201 }
202
203
204
205
206 func (a *gcPermissionsEnforcement) ownerRefToDeleteAttributeRecords(ref metav1.OwnerReference, attributes admission.Attributes) ([]authorizer.AttributesRecord, error) {
207 var ret []authorizer.AttributesRecord
208 groupVersion, err := schema.ParseGroupVersion(ref.APIVersion)
209 if err != nil {
210 return ret, err
211 }
212 mappings, err := a.restMapper.RESTMappings(schema.GroupKind{Group: groupVersion.Group, Kind: ref.Kind}, groupVersion.Version)
213 if err != nil {
214 return ret, err
215 }
216 for _, mapping := range mappings {
217 ar := authorizer.AttributesRecord{
218 User: attributes.GetUserInfo(),
219 Verb: "update",
220 APIGroup: mapping.Resource.Group,
221 APIVersion: mapping.Resource.Version,
222 Resource: mapping.Resource.Resource,
223 Subresource: "finalizers",
224 Name: ref.Name,
225 ResourceRequest: true,
226 Path: "",
227 }
228 if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
229
230 ar.Namespace = attributes.GetNamespace()
231 }
232 ret = append(ret, ar)
233 }
234 return ret, nil
235 }
236
237
238 func blockingOwnerRefs(refs []metav1.OwnerReference) []metav1.OwnerReference {
239 var ret []metav1.OwnerReference
240 for _, ref := range refs {
241 if ref.BlockOwnerDeletion != nil && *ref.BlockOwnerDeletion {
242 ret = append(ret, ref)
243 }
244 }
245 return ret
246 }
247
248 func indexByUID(refs []metav1.OwnerReference) map[types.UID]metav1.OwnerReference {
249 ret := make(map[types.UID]metav1.OwnerReference)
250 for _, ref := range refs {
251 ret[ref.UID] = ref
252 }
253 return ret
254 }
255
256
257
258 func newBlockingOwnerDeletionRefs(newObj, oldObj runtime.Object) []metav1.OwnerReference {
259 newMeta, err := meta.Accessor(newObj)
260 if err != nil {
261
262 return nil
263 }
264 newRefs := newMeta.GetOwnerReferences()
265 blockingNewRefs := blockingOwnerRefs(newRefs)
266 if len(blockingNewRefs) == 0 {
267 return nil
268 }
269
270 if oldObj == nil {
271 return blockingNewRefs
272 }
273 oldMeta, err := meta.Accessor(oldObj)
274 if err != nil {
275
276 return blockingNewRefs
277 }
278
279 var ret []metav1.OwnerReference
280 indexedOldRefs := indexByUID(oldMeta.GetOwnerReferences())
281 for _, ref := range blockingNewRefs {
282 oldRef, ok := indexedOldRefs[ref.UID]
283 if !ok {
284
285 ret = append(ret, ref)
286 continue
287 }
288 wasNotBlocking := oldRef.BlockOwnerDeletion == nil || *oldRef.BlockOwnerDeletion == false
289 if wasNotBlocking {
290 ret = append(ret, ref)
291 }
292 }
293 return ret
294 }
295
296 func (a *gcPermissionsEnforcement) SetAuthorizer(authorizer authorizer.Authorizer) {
297 a.authorizer = authorizer
298 }
299
300 func (a *gcPermissionsEnforcement) SetRESTMapper(restMapper meta.RESTMapper) {
301 a.restMapper = restMapper
302 }
303
304 func (a *gcPermissionsEnforcement) ValidateInitialization() error {
305 if a.authorizer == nil {
306 return fmt.Errorf("missing authorizer")
307 }
308 if a.restMapper == nil {
309 return fmt.Errorf("missing restMapper")
310 }
311 return nil
312 }
313
View as plain text