1
16
17 package node
18
19 import (
20 "context"
21 "fmt"
22
23 "k8s.io/klog/v2"
24
25 rbacv1 "k8s.io/api/rbac/v1"
26 "k8s.io/apimachinery/pkg/runtime/schema"
27 "k8s.io/apiserver/pkg/authentication/user"
28 "k8s.io/apiserver/pkg/authorization/authorizer"
29 utilfeature "k8s.io/apiserver/pkg/util/feature"
30 "k8s.io/component-base/featuregate"
31 coordapi "k8s.io/kubernetes/pkg/apis/coordination"
32 api "k8s.io/kubernetes/pkg/apis/core"
33 resourceapi "k8s.io/kubernetes/pkg/apis/resource"
34 storageapi "k8s.io/kubernetes/pkg/apis/storage"
35 "k8s.io/kubernetes/pkg/auth/nodeidentifier"
36 "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
37 "k8s.io/kubernetes/third_party/forked/gonum/graph"
38 "k8s.io/kubernetes/third_party/forked/gonum/graph/traverse"
39 )
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 type NodeAuthorizer struct {
60 graph *Graph
61 identifier nodeidentifier.NodeIdentifier
62 nodeRules []rbacv1.PolicyRule
63
64
65 features featuregate.FeatureGate
66 }
67
68 var _ = authorizer.Authorizer(&NodeAuthorizer{})
69 var _ = authorizer.RuleResolver(&NodeAuthorizer{})
70
71
72 func NewAuthorizer(graph *Graph, identifier nodeidentifier.NodeIdentifier, rules []rbacv1.PolicyRule) *NodeAuthorizer {
73 return &NodeAuthorizer{
74 graph: graph,
75 identifier: identifier,
76 nodeRules: rules,
77 features: utilfeature.DefaultFeatureGate,
78 }
79 }
80
81 var (
82 configMapResource = api.Resource("configmaps")
83 secretResource = api.Resource("secrets")
84 resourceSlice = resourceapi.Resource("resourceslices")
85 pvcResource = api.Resource("persistentvolumeclaims")
86 pvResource = api.Resource("persistentvolumes")
87 resourceClaimResource = resourceapi.Resource("resourceclaims")
88 vaResource = storageapi.Resource("volumeattachments")
89 svcAcctResource = api.Resource("serviceaccounts")
90 leaseResource = coordapi.Resource("leases")
91 csiNodeResource = storageapi.Resource("csinodes")
92 )
93
94 func (r *NodeAuthorizer) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
95 if _, isNode := r.identifier.NodeIdentity(user); isNode {
96
97 return nil, nil, true, fmt.Errorf("node authorizer does not support user rule resolution")
98 }
99 return nil, nil, false, nil
100 }
101
102 func (r *NodeAuthorizer) Authorize(ctx context.Context, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
103 nodeName, isNode := r.identifier.NodeIdentity(attrs.GetUser())
104 if !isNode {
105
106 return authorizer.DecisionNoOpinion, "", nil
107 }
108 if len(nodeName) == 0 {
109
110 klog.V(2).Infof("NODE DENY: unknown node for user %q", attrs.GetUser().GetName())
111 return authorizer.DecisionNoOpinion, fmt.Sprintf("unknown node for user %q", attrs.GetUser().GetName()), nil
112 }
113
114
115 if attrs.IsResourceRequest() {
116 requestResource := schema.GroupResource{Group: attrs.GetAPIGroup(), Resource: attrs.GetResource()}
117 switch requestResource {
118 case secretResource:
119 return r.authorizeReadNamespacedObject(nodeName, secretVertexType, attrs)
120 case configMapResource:
121 return r.authorizeReadNamespacedObject(nodeName, configMapVertexType, attrs)
122 case pvcResource:
123 if attrs.GetSubresource() == "status" {
124 return r.authorizeStatusUpdate(nodeName, pvcVertexType, attrs)
125 }
126 return r.authorizeGet(nodeName, pvcVertexType, attrs)
127 case pvResource:
128 return r.authorizeGet(nodeName, pvVertexType, attrs)
129 case resourceClaimResource:
130 return r.authorizeGet(nodeName, resourceClaimVertexType, attrs)
131 case vaResource:
132 return r.authorizeGet(nodeName, vaVertexType, attrs)
133 case svcAcctResource:
134 return r.authorizeCreateToken(nodeName, serviceAccountVertexType, attrs)
135 case leaseResource:
136 return r.authorizeLease(nodeName, attrs)
137 case csiNodeResource:
138 return r.authorizeCSINode(nodeName, attrs)
139 case resourceSlice:
140 return r.authorizeResourceSlice(nodeName, attrs)
141 }
142
143 }
144
145
146 if rbac.RulesAllow(attrs, r.nodeRules...) {
147 return authorizer.DecisionAllow, "", nil
148 }
149 return authorizer.DecisionNoOpinion, "", nil
150 }
151
152
153 func (r *NodeAuthorizer) authorizeStatusUpdate(nodeName string, startingType vertexType, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
154 switch attrs.GetVerb() {
155 case "update", "patch":
156
157 default:
158 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
159 return authorizer.DecisionNoOpinion, "can only get/update/patch this type", nil
160 }
161
162 if attrs.GetSubresource() != "status" {
163 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
164 return authorizer.DecisionNoOpinion, "can only update status subresource", nil
165 }
166
167 return r.authorize(nodeName, startingType, attrs)
168 }
169
170
171 func (r *NodeAuthorizer) authorizeGet(nodeName string, startingType vertexType, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
172 if attrs.GetVerb() != "get" {
173 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
174 return authorizer.DecisionNoOpinion, "can only get individual resources of this type", nil
175 }
176 if len(attrs.GetSubresource()) > 0 {
177 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
178 return authorizer.DecisionNoOpinion, "cannot get subresource", nil
179 }
180 return r.authorize(nodeName, startingType, attrs)
181 }
182
183
184
185 func (r *NodeAuthorizer) authorizeReadNamespacedObject(nodeName string, startingType vertexType, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
186 switch attrs.GetVerb() {
187 case "get", "list", "watch":
188
189 default:
190 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
191 return authorizer.DecisionNoOpinion, "can only read resources of this type", nil
192 }
193
194 if len(attrs.GetSubresource()) > 0 {
195 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
196 return authorizer.DecisionNoOpinion, "cannot read subresource", nil
197 }
198 if len(attrs.GetNamespace()) == 0 {
199 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
200 return authorizer.DecisionNoOpinion, "can only read namespaced object of this type", nil
201 }
202 return r.authorize(nodeName, startingType, attrs)
203 }
204
205 func (r *NodeAuthorizer) authorize(nodeName string, startingType vertexType, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
206 if len(attrs.GetName()) == 0 {
207 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
208 return authorizer.DecisionNoOpinion, "No Object name found", nil
209 }
210
211 ok, err := r.hasPathFrom(nodeName, startingType, attrs.GetNamespace(), attrs.GetName())
212 if err != nil {
213 klog.V(2).InfoS("NODE DENY", "err", err)
214 return authorizer.DecisionNoOpinion, fmt.Sprintf("no relationship found between node '%s' and this object", nodeName), nil
215 }
216 if !ok {
217 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
218 return authorizer.DecisionNoOpinion, fmt.Sprintf("no relationship found between node '%s' and this object", nodeName), nil
219 }
220 return authorizer.DecisionAllow, "", nil
221 }
222
223
224
225 func (r *NodeAuthorizer) authorizeCreateToken(nodeName string, startingType vertexType, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
226 if attrs.GetVerb() != "create" || len(attrs.GetName()) == 0 {
227 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
228 return authorizer.DecisionNoOpinion, "can only create tokens for individual service accounts", nil
229 }
230
231 if attrs.GetSubresource() != "token" {
232 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
233 return authorizer.DecisionNoOpinion, "can only create token subresource of serviceaccount", nil
234 }
235
236 ok, err := r.hasPathFrom(nodeName, startingType, attrs.GetNamespace(), attrs.GetName())
237 if err != nil {
238 klog.V(2).Infof("NODE DENY: %v", err)
239 return authorizer.DecisionNoOpinion, fmt.Sprintf("no relationship found between node '%s' and this object", nodeName), nil
240 }
241 if !ok {
242 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
243 return authorizer.DecisionNoOpinion, fmt.Sprintf("no relationship found between node '%s' and this object", nodeName), nil
244 }
245 return authorizer.DecisionAllow, "", nil
246 }
247
248
249 func (r *NodeAuthorizer) authorizeLease(nodeName string, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
250
251 verb := attrs.GetVerb()
252 switch verb {
253 case "get", "create", "update", "patch", "delete":
254
255 default:
256 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
257 return authorizer.DecisionNoOpinion, "can only get, create, update, patch, or delete a node lease", nil
258 }
259
260
261 if attrs.GetNamespace() != api.NamespaceNodeLease {
262 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
263 return authorizer.DecisionNoOpinion, fmt.Sprintf("can only access leases in the %q system namespace", api.NamespaceNodeLease), nil
264 }
265
266
267
268
269 if verb != "create" && attrs.GetName() != nodeName {
270 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
271 return authorizer.DecisionNoOpinion, "can only access node lease with the same name as the requesting node", nil
272 }
273
274 return authorizer.DecisionAllow, "", nil
275 }
276
277
278 func (r *NodeAuthorizer) authorizeCSINode(nodeName string, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
279
280 verb := attrs.GetVerb()
281 switch verb {
282 case "get", "create", "update", "patch", "delete":
283
284 default:
285 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
286 return authorizer.DecisionNoOpinion, "can only get, create, update, patch, or delete a CSINode", nil
287 }
288
289 if len(attrs.GetSubresource()) > 0 {
290 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
291 return authorizer.DecisionNoOpinion, "cannot authorize CSINode subresources", nil
292 }
293
294
295
296
297 if verb != "create" && attrs.GetName() != nodeName {
298 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
299 return authorizer.DecisionNoOpinion, "can only access CSINode with the same name as the requesting node", nil
300 }
301
302 return authorizer.DecisionAllow, "", nil
303 }
304
305
306 func (r *NodeAuthorizer) authorizeResourceSlice(nodeName string, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
307 if len(attrs.GetSubresource()) > 0 {
308 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
309 return authorizer.DecisionNoOpinion, "cannot authorize ResourceSlice subresources", nil
310 }
311
312
313 verb := attrs.GetVerb()
314 switch verb {
315 case "get", "create", "update", "patch", "delete":
316
317 case "watch", "list":
318
319 return authorizer.DecisionAllow, "", nil
320 default:
321 klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
322 return authorizer.DecisionNoOpinion, "can only get, create, update, patch, or delete a ResourceSlice", nil
323 }
324
325
326
327
328
329 if verb == "create" {
330 return authorizer.DecisionAllow, "", nil
331 }
332
333
334
335 return r.authorize(nodeName, sliceVertexType, attrs)
336 }
337
338
339 func (r *NodeAuthorizer) hasPathFrom(nodeName string, startingType vertexType, startingNamespace, startingName string) (bool, error) {
340 r.graph.lock.RLock()
341 defer r.graph.lock.RUnlock()
342
343 nodeVertex, exists := r.graph.getVertex_rlocked(nodeVertexType, "", nodeName)
344 if !exists {
345 return false, fmt.Errorf("unknown node '%s' cannot get %s %s/%s", nodeName, vertexTypes[startingType], startingNamespace, startingName)
346 }
347
348 startingVertex, exists := r.graph.getVertex_rlocked(startingType, startingNamespace, startingName)
349 if !exists {
350 return false, fmt.Errorf("node '%s' cannot get unknown %s %s/%s", nodeName, vertexTypes[startingType], startingNamespace, startingName)
351 }
352
353
354 if r.graph.destinationEdgeIndex[startingVertex.ID()].has(nodeVertex.ID()) {
355 return true, nil
356 }
357
358 found := false
359 traversal := &traverse.VisitingDepthFirst{
360 EdgeFilter: func(edge graph.Edge) bool {
361 if destinationEdge, ok := edge.(*destinationEdge); ok {
362 if destinationEdge.DestinationID() != nodeVertex.ID() {
363
364 return false
365 }
366
367 found = true
368 }
369
370 return true
371 },
372 }
373 traversal.Walk(r.graph.graph, startingVertex, func(n graph.Node) bool {
374 if n.ID() == nodeVertex.ID() {
375
376 found = true
377 }
378
379 return found
380 })
381 if !found {
382 return false, fmt.Errorf("node '%s' cannot get %s %s/%s, no relationship to this object was found in the node authorizer graph", nodeName, vertexTypes[startingType], startingNamespace, startingName)
383 }
384 return true, nil
385 }
386
View as plain text