1
16
17 package v1beta1
18
19 import (
20 "fmt"
21
22 authentication "k8s.io/api/authentication/v1"
23 authorization "k8s.io/api/authorization/v1"
24 corev1 "k8s.io/api/core/v1"
25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 "k8s.io/klog/v2"
27 )
28
29 func newCloneSourceHandler(dataVolume *DataVolume, dsGet dsGetFunc) (CloneSourceHandler, error) {
30 var pvcSource *DataVolumeSourcePVC
31 var snapshotSource *DataVolumeSourceSnapshot
32
33 if dataVolume.Spec.Source != nil {
34 if dataVolume.Spec.Source.PVC != nil {
35 pvcSource = dataVolume.Spec.Source.PVC
36 } else if dataVolume.Spec.Source.Snapshot != nil {
37 snapshotSource = dataVolume.Spec.Source.Snapshot
38 }
39 } else if dataVolume.Spec.SourceRef != nil && dataVolume.Spec.SourceRef.Kind == DataVolumeDataSource {
40 ns := dataVolume.Namespace
41 if dataVolume.Spec.SourceRef.Namespace != nil && *dataVolume.Spec.SourceRef.Namespace != "" {
42 ns = *dataVolume.Spec.SourceRef.Namespace
43 }
44 dataSource, err := dsGet(ns, dataVolume.Spec.SourceRef.Name)
45 if err != nil {
46 return CloneSourceHandler{}, err
47 }
48 if dataSource.Spec.Source.PVC != nil {
49 pvcSource = dataSource.Spec.Source.PVC
50 } else if dataSource.Spec.Source.Snapshot != nil {
51 snapshotSource = dataSource.Spec.Source.Snapshot
52 }
53 }
54
55 switch {
56 case pvcSource != nil:
57 return CloneSourceHandler{
58 CloneType: pvcClone,
59 TokenResource: tokenResourcePvc,
60 UserCloneAuthFunc: CanUserClonePVC,
61 SACloneAuthFunc: CanServiceAccountClonePVC,
62 SourceName: pvcSource.Name,
63 SourceNamespace: pvcSource.Namespace,
64 }, nil
65 case snapshotSource != nil:
66 return CloneSourceHandler{
67 CloneType: snapshotClone,
68 TokenResource: tokenResourceSnapshot,
69 UserCloneAuthFunc: CanUserCloneSnapshot,
70 SACloneAuthFunc: CanServiceAccountCloneSnapshot,
71 SourceName: snapshotSource.Name,
72 SourceNamespace: snapshotSource.Namespace,
73 }, nil
74 default:
75 return CloneSourceHandler{
76 CloneType: noClone,
77 }, nil
78 }
79 }
80
81 var (
82 tokenResourcePvc = metav1.GroupVersionResource{
83 Group: "",
84 Version: "v1",
85 Resource: "persistentvolumeclaims",
86 }
87
88 tokenResourceSnapshot = metav1.GroupVersionResource{
89 Group: "snapshot.storage.k8s.io",
90 Version: "v1",
91 Resource: "volumesnapshots",
92 }
93 )
94
95 type cloneType int
96
97 const (
98 noClone cloneType = iota
99 pvcClone
100 snapshotClone
101 )
102
103
104
105
106
107 type CloneSourceHandler struct {
108 CloneType cloneType
109 TokenResource metav1.GroupVersionResource
110 UserCloneAuthFunc UserCloneAuthFunc
111 SACloneAuthFunc ServiceAccountCloneAuthFunc
112 SourceName string
113 SourceNamespace string
114 }
115
116
117
118
119
120 type CloneAuthResponse struct {
121 Handler CloneSourceHandler
122 Allowed bool
123 Reason string
124 }
125
126 type createSarFunc func(*authorization.SubjectAccessReview) (*authorization.SubjectAccessReview, error)
127 type dsGetFunc func(string, string) (*DataSource, error)
128
129
130 type AuthorizationHelperProxy interface {
131 CreateSar(*authorization.SubjectAccessReview) (*authorization.SubjectAccessReview, error)
132 GetNamespace(string) (*corev1.Namespace, error)
133 GetDataSource(string, string) (*DataSource, error)
134 }
135
136
137 type UserCloneAuthFunc func(createSar createSarFunc, sourceNamespace, pvcName, targetNamespace string, userInfo authentication.UserInfo) (bool, string, error)
138
139
140 type ServiceAccountCloneAuthFunc func(createSar createSarFunc, pvcNamespace, pvcName, saNamespace, saName string) (bool, string, error)
141
142
143 func CanUserClonePVC(createSar createSarFunc, sourceNamespace, pvcName, targetNamespace string,
144 userInfo authentication.UserInfo) (bool, string, error) {
145 if sourceNamespace == targetNamespace {
146 return true, "", nil
147 }
148
149 var newExtra map[string]authorization.ExtraValue
150 if len(userInfo.Extra) > 0 {
151 newExtra = make(map[string]authorization.ExtraValue)
152 for k, v := range userInfo.Extra {
153 newExtra[k] = authorization.ExtraValue(v)
154 }
155 }
156
157 sarSpec := authorization.SubjectAccessReviewSpec{
158 User: userInfo.Username,
159 Groups: userInfo.Groups,
160 Extra: newExtra,
161 }
162
163 return sendSubjectAccessReviewsPvc(createSar, sourceNamespace, pvcName, sarSpec)
164 }
165
166
167 func CanServiceAccountClonePVC(createSar createSarFunc, pvcNamespace, pvcName, saNamespace, saName string) (bool, string, error) {
168 if pvcNamespace == saNamespace {
169 return true, "", nil
170 }
171
172 user := fmt.Sprintf("system:serviceaccount:%s:%s", saNamespace, saName)
173
174 sarSpec := authorization.SubjectAccessReviewSpec{
175 User: user,
176 Groups: []string{
177 "system:serviceaccounts",
178 "system:serviceaccounts:" + saNamespace,
179 "system:authenticated",
180 },
181 }
182
183 return sendSubjectAccessReviewsPvc(createSar, pvcNamespace, pvcName, sarSpec)
184 }
185
186
187 func CanUserCloneSnapshot(createSar createSarFunc, sourceNamespace, pvcName, targetNamespace string,
188 userInfo authentication.UserInfo) (bool, string, error) {
189 if sourceNamespace == targetNamespace {
190 return true, "", nil
191 }
192
193 var newExtra map[string]authorization.ExtraValue
194 if len(userInfo.Extra) > 0 {
195 newExtra = make(map[string]authorization.ExtraValue)
196 for k, v := range userInfo.Extra {
197 newExtra[k] = authorization.ExtraValue(v)
198 }
199 }
200
201 sarSpec := authorization.SubjectAccessReviewSpec{
202 User: userInfo.Username,
203 Groups: userInfo.Groups,
204 Extra: newExtra,
205 }
206
207 return sendSubjectAccessReviewsSnapshot(createSar, sourceNamespace, pvcName, sarSpec)
208 }
209
210
211 func CanServiceAccountCloneSnapshot(createSar createSarFunc, pvcNamespace, pvcName, saNamespace, saName string) (bool, string, error) {
212 if pvcNamespace == saNamespace {
213 return true, "", nil
214 }
215
216 user := fmt.Sprintf("system:serviceaccount:%s:%s", saNamespace, saName)
217
218 sarSpec := authorization.SubjectAccessReviewSpec{
219 User: user,
220 Groups: []string{
221 "system:serviceaccounts",
222 "system:serviceaccounts:" + saNamespace,
223 "system:authenticated",
224 },
225 }
226
227 return sendSubjectAccessReviewsSnapshot(createSar, pvcNamespace, pvcName, sarSpec)
228 }
229
230 func sendSubjectAccessReviewsPvc(createSar createSarFunc, namespace, name string, sarSpec authorization.SubjectAccessReviewSpec) (bool, string, error) {
231 allowed := false
232
233 for _, ra := range getResourceAttributesPvc(namespace, name) {
234 sar := &authorization.SubjectAccessReview{
235 Spec: sarSpec,
236 }
237 sar.Spec.ResourceAttributes = &ra
238
239 klog.V(3).Infof("Sending SubjectAccessReview %+v", sar)
240
241 response, err := createSar(sar)
242 if err != nil {
243 return false, "", err
244 }
245
246 klog.V(3).Infof("SubjectAccessReview response %+v", response)
247
248 if response.Status.Allowed {
249 allowed = true
250 break
251 }
252 }
253
254 if !allowed {
255 return false, fmt.Sprintf("User %s has insufficient permissions in clone source namespace %s", sarSpec.User, namespace), nil
256 }
257
258 return true, "", nil
259 }
260
261 func sendSubjectAccessReviewsSnapshot(createSar createSarFunc, namespace, name string, sarSpec authorization.SubjectAccessReviewSpec) (bool, string, error) {
262
263 sar := &authorization.SubjectAccessReview{
264 Spec: sarSpec,
265 }
266 explicitResourceAttr := getExplicitResourceAttributeSnapshot(namespace, name)
267 sar.Spec.ResourceAttributes = &explicitResourceAttr
268
269 klog.V(3).Infof("Sending SubjectAccessReview %+v", sar)
270
271 response, err := createSar(sar)
272 if err != nil {
273 return false, "", err
274 }
275
276 klog.V(3).Infof("SubjectAccessReview response %+v", response)
277
278 if response.Status.Allowed {
279 return true, "", nil
280 }
281
282
283 for _, ra := range getImplicitResourceAttributesSnapshot(namespace, name) {
284 sar = &authorization.SubjectAccessReview{
285 Spec: sarSpec,
286 }
287 sar.Spec.ResourceAttributes = &ra
288
289 klog.V(3).Infof("Sending SubjectAccessReview %+v", sar)
290
291 response, err = createSar(sar)
292 if err != nil {
293 return false, "", err
294 }
295
296 klog.V(3).Infof("SubjectAccessReview response %+v", response)
297
298 if !response.Status.Allowed {
299 return false, fmt.Sprintf("User %s has insufficient permissions in clone source namespace %s", sarSpec.User, namespace), nil
300 }
301 }
302
303 return true, "", nil
304 }
305
306 func getResourceAttributesPvc(namespace, name string) []authorization.ResourceAttributes {
307 return []authorization.ResourceAttributes{
308 {
309 Namespace: namespace,
310 Verb: "create",
311 Group: SchemeGroupVersion.Group,
312 Resource: "datavolumes",
313 Subresource: DataVolumeCloneSourceSubresource,
314 Name: name,
315 },
316 {
317 Namespace: namespace,
318 Verb: "create",
319 Resource: "pods",
320 Name: name,
321 },
322 }
323 }
324
325 func getExplicitResourceAttributeSnapshot(namespace, name string) authorization.ResourceAttributes {
326 return authorization.ResourceAttributes{
327 Namespace: namespace,
328 Verb: "create",
329 Group: SchemeGroupVersion.Group,
330 Resource: "datavolumes",
331 Subresource: DataVolumeCloneSourceSubresource,
332 Name: name,
333 }
334 }
335
336 func getImplicitResourceAttributesSnapshot(namespace, name string) []authorization.ResourceAttributes {
337 return []authorization.ResourceAttributes{
338 {
339 Namespace: namespace,
340 Verb: "create",
341 Resource: "pods",
342 Name: name,
343 },
344 {
345 Namespace: namespace,
346 Verb: "create",
347 Resource: "pvcs",
348 Name: name,
349 },
350 }
351 }
352
View as plain text