@@ -36,35 +36,27 @@ const (
36
36
37
37
var DefaultID int64 = 2137
38
38
39
- func Mount (ctx context.Context , namespace , pvcName , localMountPoint string , needsRoot , debug bool ) error {
40
-
39
+ func Mount (ctx context.Context , namespace , pvcName , localMountPoint string , needsRoot , debug bool , image , imageSecret string ) error {
41
40
checkSSHFS ()
42
-
43
41
if err := validateMountPoint (localMountPoint ); err != nil {
44
42
return err
45
43
}
46
-
47
44
clientset , err := BuildKubeClient ()
48
45
if err != nil {
49
46
return err
50
47
}
51
-
52
48
pvc , err := checkPVCUsage (ctx , clientset , namespace , pvcName )
53
49
if err != nil {
54
50
return err
55
51
}
56
-
57
52
canBeMounted , podUsingPVC , err := checkPVAccessMode (ctx , clientset , pvc , namespace )
58
53
if err != nil {
59
54
return err
60
55
}
61
-
62
56
if canBeMounted {
63
- return handleRWX (ctx , clientset , namespace , pvcName , localMountPoint , needsRoot , debug )
57
+ return handleRWX (ctx , clientset , namespace , pvcName , localMountPoint , needsRoot , debug , image , imageSecret )
64
58
}
65
-
66
- return handleRWO (ctx , clientset , namespace , pvcName , localMountPoint , podUsingPVC , needsRoot , debug )
67
-
59
+ return handleRWO (ctx , clientset , namespace , pvcName , localMountPoint , podUsingPVC , needsRoot , debug , image , imageSecret )
68
60
}
69
61
70
62
func validateMountPoint (localMountPoint string ) error {
@@ -74,90 +66,79 @@ func validateMountPoint(localMountPoint string) error {
74
66
return nil
75
67
}
76
68
77
- func handleRWX (ctx context.Context , clientset * kubernetes.Clientset , namespace , pvcName , localMountPoint string , needsRoot bool , debug bool ) error {
78
-
69
+ func handleRWX (ctx context.Context , clientset * kubernetes.Clientset , namespace , pvcName , localMountPoint string , needsRoot , debug bool , image , imageSecret string ) error {
79
70
privateKey , publicKey , err := GenerateKeyPair (elliptic .P256 ())
80
71
if err != nil {
81
72
return fmt .Errorf ("error generating key pair: %v" , err )
82
73
}
83
-
84
74
if debug {
85
75
fmt .Printf ("Private Key:\n %s\n " , privateKey )
86
76
}
87
-
88
- podName , port , err := setupPod (ctx , clientset , namespace , pvcName , publicKey , "standalone" , DefaultSSHPort , "" , needsRoot )
77
+ podName , port , err := setupPod (ctx , clientset , namespace , pvcName , publicKey , "standalone" , DefaultSSHPort , "" , needsRoot , image , imageSecret )
89
78
if err != nil {
90
79
return err
91
80
}
92
-
93
81
if err := waitForPodReady (ctx , clientset , namespace , podName ); err != nil {
94
82
return err
95
83
}
96
-
97
84
if err := setupPortForwarding (namespace , podName , port ); err != nil {
98
85
return err
99
86
}
100
-
101
87
return mountPVCOverSSH (port , localMountPoint , pvcName , privateKey , needsRoot )
102
88
}
103
89
104
- func handleRWO (ctx context.Context , clientset * kubernetes.Clientset , namespace , pvcName , localMountPoint string , podUsingPVC string , needsRoot bool , debug bool ) error {
105
-
90
+ func handleRWO (ctx context.Context , clientset * kubernetes.Clientset , namespace , pvcName , localMountPoint string , podUsingPVC string , needsRoot , debug bool , image , imageSecret string ) error {
106
91
privateKey , publicKey , err := GenerateKeyPair (elliptic .P256 ())
107
92
if err != nil {
108
93
return fmt .Errorf ("error generating key pair: %v" , err )
109
94
}
110
-
111
95
if debug {
112
96
fmt .Printf ("Private Key:\n %s\n " , privateKey )
113
97
}
114
-
115
- podName , port , err := setupPod (ctx , clientset , namespace , pvcName , publicKey , "proxy" , ProxySSHPort , podUsingPVC , needsRoot )
98
+ podName , port , err := setupPod (ctx , clientset , namespace , pvcName , publicKey , "proxy" , ProxySSHPort , podUsingPVC , needsRoot , image , imageSecret )
116
99
if err != nil {
117
100
return err
118
101
}
119
-
120
102
if err := waitForPodReady (ctx , clientset , namespace , podName ); err != nil {
121
103
return err
122
104
}
123
-
124
105
proxyPodIP , err := getPodIP (ctx , clientset , namespace , podName )
125
106
if err != nil {
126
107
return err
127
108
}
128
-
129
- if err := createEphemeralContainer (ctx , clientset , namespace , podUsingPVC , privateKey , publicKey , proxyPodIP , needsRoot ); err != nil {
109
+ if err := createEphemeralContainer (ctx , clientset , namespace , podUsingPVC , privateKey , publicKey , proxyPodIP , needsRoot , image ); err != nil {
130
110
return err
131
111
}
132
-
133
112
if err := setupPortForwarding (namespace , podName , port ); err != nil {
134
113
return err
135
114
}
136
-
137
115
return mountPVCOverSSH (port , localMountPoint , pvcName , privateKey , needsRoot )
138
116
}
139
117
140
- func createEphemeralContainer (ctx context.Context , clientset * kubernetes.Clientset , namespace , podName , privateKey , publicKey , proxyPodIP string , needsRoot bool ) error {
141
- // Retrieve the existing pod to get the volume name
118
+ func createEphemeralContainer (ctx context.Context , clientset * kubernetes.Clientset , namespace , podName , privateKey , publicKey , proxyPodIP string , needsRoot bool , image string ) error {
142
119
existingPod , err := clientset .CoreV1 ().Pods (namespace ).Get (ctx , podName , metav1.GetOptions {})
143
120
if err != nil {
144
121
return fmt .Errorf ("failed to get existing pod: %v" , err )
145
122
}
146
-
147
123
volumeName , err := getPVCVolumeName (existingPod )
148
124
if err != nil {
149
125
return err
150
126
}
151
-
152
127
ephemeralContainerName := fmt .Sprintf ("volume-exposer-ephemeral-%s" , randSeq (5 ))
153
128
fmt .Printf ("Adding ephemeral container %s to pod %s with volume name %s\n " , ephemeralContainerName , podName , volumeName )
154
-
155
- image , securityContext := getEphemeralContainerSettings (needsRoot )
156
-
129
+ imageToUse := image
130
+ if imageToUse == "" {
131
+ if needsRoot {
132
+ imageToUse = PrivilegedImage
133
+ } else {
134
+ imageToUse = Image
135
+ }
136
+ }
137
+ securityContext := getSecurityContext (needsRoot )
157
138
ephemeralContainer := corev1.EphemeralContainer {
158
139
EphemeralContainerCommon : corev1.EphemeralContainerCommon {
159
140
Name : ephemeralContainerName ,
160
- Image : image ,
141
+ Image : imageToUse ,
161
142
ImagePullPolicy : corev1 .PullAlways ,
162
143
Env : []corev1.EnvVar {
163
144
{Name : "ROLE" , Value : "ephemeral" },
@@ -175,7 +156,6 @@ func createEphemeralContainer(ctx context.Context, clientset *kubernetes.Clients
175
156
},
176
157
},
177
158
}
178
-
179
159
patchData , err := json .Marshal (map [string ]interface {}{
180
160
"spec" : map [string ]interface {}{
181
161
"ephemeralContainers" : []corev1.EphemeralContainer {ephemeralContainer },
@@ -184,12 +164,10 @@ func createEphemeralContainer(ctx context.Context, clientset *kubernetes.Clients
184
164
if err != nil {
185
165
return fmt .Errorf ("failed to marshal ephemeral container spec: %v" , err )
186
166
}
187
-
188
167
_ , err = clientset .CoreV1 ().Pods (namespace ).Patch (ctx , podName , types .StrategicMergePatchType , patchData , metav1.PatchOptions {}, "ephemeralcontainers" )
189
168
if err != nil {
190
169
return fmt .Errorf ("failed to patch pod with ephemeral container: %v" , err )
191
170
}
192
-
193
171
fmt .Printf ("Successfully added ephemeral container %s to pod %s\n " , ephemeralContainerName , podName )
194
172
return nil
195
173
}
@@ -245,9 +223,9 @@ func checkPVCUsage(ctx context.Context, clientset *kubernetes.Clientset, namespa
245
223
return pvc , nil
246
224
}
247
225
248
- func setupPod (ctx context.Context , clientset * kubernetes.Clientset , namespace , pvcName , publicKey , role string , sshPort int , originalPodName string , needsRoot bool ) (string , int , error ) {
226
+ func setupPod (ctx context.Context , clientset * kubernetes.Clientset , namespace , pvcName , publicKey , role string , sshPort int , originalPodName string , needsRoot bool , image , imageSecret string ) (string , int , error ) {
249
227
podName , port := generatePodNameAndPort (role )
250
- pod := createPodSpec (podName , port , pvcName , publicKey , role , sshPort , originalPodName , needsRoot )
228
+ pod := createPodSpec (podName , port , pvcName , publicKey , role , sshPort , originalPodName , needsRoot , image , imageSecret )
251
229
if _ , err := clientset .CoreV1 ().Pods (namespace ).Create (ctx , pod , metav1.CreateOptions {}); err != nil {
252
230
return "" , 0 , fmt .Errorf ("failed to create pod: %v" , err )
253
231
}
@@ -339,35 +317,37 @@ func generatePodNameAndPort(role string) (string, int) {
339
317
return podName , port
340
318
}
341
319
342
- func createPodSpec (podName string , port int , pvcName , publicKey , role string , sshPort int , originalPodName string , needsRoot bool ) * corev1.Pod {
343
-
320
+ func createPodSpec (podName string , port int , pvcName , publicKey , role string , sshPort int , originalPodName string , needsRoot bool , image , imageSecret string ) * corev1.Pod {
344
321
envVars := []corev1.EnvVar {
345
322
{Name : "SSH_PUBLIC_KEY" , Value : publicKey },
346
323
{Name : "SSH_PORT" , Value : fmt .Sprintf ("%d" , sshPort )},
347
324
{Name : "NEEDS_ROOT" , Value : fmt .Sprintf ("%v" , needsRoot )},
348
325
}
349
-
350
- // Add the ROLE environment variable if the role is "standalone" or "proxy"
351
326
if role == "standalone" || role == "proxy" {
352
327
envVars = append (envVars , corev1.EnvVar {
353
328
Name : "ROLE" ,
354
329
Value : role ,
355
330
})
356
331
}
357
-
358
- image , securityContext := getEphemeralContainerSettings (needsRoot )
359
-
332
+ imageToUse := image
333
+ if imageToUse == "" {
334
+ if needsRoot {
335
+ imageToUse = PrivilegedImage
336
+ } else {
337
+ imageToUse = Image
338
+ }
339
+ }
340
+ securityContext := getSecurityContext (needsRoot )
360
341
runAsNonRoot := ! needsRoot
361
342
runAsUser := int64 (DefaultUserGroup )
362
343
runAsGroup := int64 (DefaultUserGroup )
363
344
if needsRoot {
364
345
runAsUser = 0
365
346
runAsGroup = 0
366
347
}
367
-
368
348
container := corev1.Container {
369
349
Name : "volume-exposer" ,
370
- Image : image ,
350
+ Image : imageToUse ,
371
351
ImagePullPolicy : corev1 .PullAlways ,
372
352
Ports : []corev1.ContainerPort {
373
353
{ContainerPort : int32 (sshPort )},
@@ -386,18 +366,18 @@ func createPodSpec(podName string, port int, pvcName, publicKey, role string, ss
386
366
},
387
367
},
388
368
}
389
-
390
369
labels := map [string ]string {
391
370
"app" : "volume-exposer" ,
392
371
"pvcName" : pvcName ,
393
372
"portNumber" : fmt .Sprintf ("%d" , port ),
394
373
}
395
-
396
- // Add the original pod name label if provided
397
374
if originalPodName != "" {
398
375
labels ["originalPodName" ] = originalPodName
399
376
}
400
-
377
+ imagePullSecrets := []corev1.LocalObjectReference {}
378
+ if imageSecret != "" {
379
+ imagePullSecrets = append (imagePullSecrets , corev1.LocalObjectReference {Name : imageSecret })
380
+ }
401
381
podSpec := & corev1.Pod {
402
382
ObjectMeta : metav1.ObjectMeta {
403
383
Name : podName ,
@@ -410,10 +390,9 @@ func createPodSpec(podName string, port int, pvcName, publicKey, role string, ss
410
390
RunAsUser : & runAsUser ,
411
391
RunAsGroup : & runAsGroup ,
412
392
},
393
+ ImagePullSecrets : imagePullSecrets ,
413
394
},
414
395
}
415
-
416
- // Only mount the volume if the role is not "proxy"
417
396
if role != "proxy" {
418
397
container .VolumeMounts = []corev1.VolumeMount {
419
398
{MountPath : "/volume" , Name : "my-pvc" },
@@ -428,10 +407,8 @@ func createPodSpec(podName string, port int, pvcName, publicKey, role string, ss
428
407
},
429
408
},
430
409
}
431
- // Update the container in the podSpec with the volume mounts
432
410
podSpec .Spec .Containers [0 ] = container
433
411
}
434
-
435
412
return podSpec
436
413
}
437
414
@@ -484,3 +461,35 @@ func getEphemeralContainerSettings(needsRoot bool) (string, *corev1.SecurityCont
484
461
}
485
462
return image , securityContext
486
463
}
464
+
465
+ func getSecurityContext (needsRoot bool ) * corev1.SecurityContext {
466
+ allowPrivilegeEscalationTrue := true
467
+ allowPrivilegeEscalationFalse := false
468
+ readOnlyRootFilesystemTrue := true
469
+ runAsNonRootTrue := true
470
+ seccompProfileRuntimeDefault := corev1.SeccompProfile {
471
+ Type : corev1 .SeccompProfileTypeRuntimeDefault ,
472
+ }
473
+ if needsRoot {
474
+ return & corev1.SecurityContext {
475
+ AllowPrivilegeEscalation : & allowPrivilegeEscalationTrue ,
476
+ ReadOnlyRootFilesystem : & readOnlyRootFilesystemTrue ,
477
+ Capabilities : & corev1.Capabilities {
478
+ Add : []corev1.Capability {"SYS_ADMIN" , "SYS_CHROOT" },
479
+ },
480
+ SeccompProfile : & seccompProfileRuntimeDefault ,
481
+ }
482
+ } else {
483
+ return & corev1.SecurityContext {
484
+ AllowPrivilegeEscalation : & allowPrivilegeEscalationFalse ,
485
+ ReadOnlyRootFilesystem : & readOnlyRootFilesystemTrue ,
486
+ Capabilities : & corev1.Capabilities {
487
+ Drop : []corev1.Capability {"ALL" },
488
+ },
489
+ SeccompProfile : & seccompProfileRuntimeDefault ,
490
+ RunAsUser : & DefaultID ,
491
+ RunAsGroup : & DefaultID ,
492
+ RunAsNonRoot : & runAsNonRootTrue ,
493
+ }
494
+ }
495
+ }
0 commit comments