@@ -19,9 +19,16 @@ package controllers
19
19
import (
20
20
"context"
21
21
"crypto/rand"
22
+ "crypto/rsa"
22
23
"crypto/sha1"
24
+ "crypto/x509"
25
+ "crypto/x509/pkix"
23
26
"encoding/base64"
27
+ "encoding/pem"
24
28
"fmt"
29
+ "math/big"
30
+ rand2 "math/rand"
31
+ "time"
25
32
26
33
dsciv1 "github.com/opendatahub-io/opendatahub-operator/v2/apis/dscinitialization/v1"
27
34
rayv1 "github.com/ray-project/kuberay/ray-operator/apis/ray/v1"
@@ -32,18 +39,18 @@ import (
32
39
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33
40
"k8s.io/apimachinery/pkg/runtime"
34
41
"k8s.io/apimachinery/pkg/util/intstr"
35
- coreapply "k8s.io/client-go/applyconfigurations/core/v1"
36
- metav1apply "k8s.io/client-go/applyconfigurations/meta/v1"
37
- v1 "k8s.io/client-go/applyconfigurations/meta/v1"
38
- networkingapply "k8s.io/client-go/applyconfigurations/networking/v1"
39
- rbacapply "k8s.io/client-go/applyconfigurations/rbac/v1"
42
+ corev1ac "k8s.io/client-go/applyconfigurations/core/v1"
43
+ metav1ac "k8s.io/client-go/applyconfigurations/meta/v1"
44
+ networkingv1ac "k8s.io/client-go/applyconfigurations/networking/v1"
45
+ rbacv1ac "k8s.io/client-go/applyconfigurations/rbac/v1"
40
46
"k8s.io/client-go/kubernetes"
47
+ "k8s.io/utils/ptr"
41
48
ctrl "sigs.k8s.io/controller-runtime"
42
49
"sigs.k8s.io/controller-runtime/pkg/client"
43
50
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
44
51
45
52
routev1 "github.com/openshift/api/route/v1"
46
- routeapply "github.com/openshift/client-go/route/applyconfigurations/route/v1"
53
+ routev1ac "github.com/openshift/client-go/route/applyconfigurations/route/v1"
47
54
routev1client "github.com/openshift/client-go/route/clientset/versioned/typed/route/v1"
48
55
49
56
"github.com/project-codeflare/codeflare-operator/pkg/config"
@@ -144,6 +151,26 @@ func (r *RayClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
144
151
return ctrl.Result {}, nil
145
152
}
146
153
154
+ if isMTLSEnabled (r .Config ) {
155
+ caSecretName := caSecretNameFromCluster (cluster )
156
+ _ , err := r .kubeClient .CoreV1 ().Secrets (cluster .Namespace ).Get (ctx , caSecretName , metav1.GetOptions {})
157
+ if errors .IsNotFound (err ) {
158
+ key , cert , err := generateCACertificate ()
159
+ if err != nil {
160
+ logger .Error (err , "Failed to generate CA certificate" )
161
+ return ctrl.Result {RequeueAfter : requeueTime }, err
162
+ }
163
+ _ , err = r .kubeClient .CoreV1 ().Secrets (cluster .Namespace ).Apply (ctx , desiredCASecret (cluster , key , cert ), metav1.ApplyOptions {FieldManager : controllerName , Force : true })
164
+ if err != nil {
165
+ logger .Error (err , "Failed to create CA Secret" )
166
+ return ctrl.Result {RequeueAfter : requeueTime }, err
167
+ }
168
+ } else if err != nil {
169
+ logger .Error (err , "Failed to get CA Secret" )
170
+ return ctrl.Result {RequeueAfter : requeueTime }, err
171
+ }
172
+ }
173
+
147
174
if cluster .Status .State != "suspended" && isRayDashboardOAuthEnabled (r .Config ) && r .IsOpenShift {
148
175
logger .Info ("Creating OAuth Objects" )
149
176
_ , err := r .routeClient .Routes (cluster .Namespace ).Apply (ctx , desiredClusterRoute (cluster ), metav1.ApplyOptions {FieldManager : controllerName , Force : true })
@@ -152,7 +179,7 @@ func (r *RayClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
152
179
return ctrl.Result {RequeueAfter : requeueTime }, err
153
180
}
154
181
155
- _ , err = r .kubeClient .CoreV1 ().Secrets (cluster .Namespace ).Apply (ctx , desiredOAuthSecret (cluster , r ), metav1.ApplyOptions {FieldManager : controllerName , Force : true })
182
+ _ , err = r .kubeClient .CoreV1 ().Secrets (cluster .Namespace ).Apply (ctx , desiredOAuthSecret (cluster , r . CookieSalt ), metav1.ApplyOptions {FieldManager : controllerName , Force : true })
156
183
if err != nil {
157
184
logger .Error (err , "Failed to create OAuth Secret" )
158
185
return ctrl.Result {RequeueAfter : requeueTime }, err
@@ -244,28 +271,29 @@ func getIngressHost(cfg *config.KubeRayConfiguration, cluster *rayv1.RayCluster,
244
271
}
245
272
246
273
func isRayDashboardOAuthEnabled (cfg * config.KubeRayConfiguration ) bool {
247
- if cfg != nil && cfg .RayDashboardOAuthEnabled != nil {
248
- return * cfg .RayDashboardOAuthEnabled
249
- }
250
- return true
274
+ return cfg == nil || ptr .Deref (cfg .RayDashboardOAuthEnabled , true )
275
+ }
276
+
277
+ func isMTLSEnabled (cfg * config.KubeRayConfiguration ) bool {
278
+ return cfg == nil || ptr .Deref (cfg .MTLSEnabled , true )
251
279
}
252
280
253
281
func crbNameFromCluster (cluster * rayv1.RayCluster ) string {
254
282
return cluster .Name + "-" + cluster .Namespace + "-auth" // NOTE: potential naming conflicts ie {name: foo, ns: bar-baz} and {name: foo-bar, ns: baz}
255
283
}
256
284
257
- func desiredOAuthClusterRoleBinding (cluster * rayv1.RayCluster ) * rbacapply .ClusterRoleBindingApplyConfiguration {
258
- return rbacapply .ClusterRoleBinding (
285
+ func desiredOAuthClusterRoleBinding (cluster * rayv1.RayCluster ) * rbacv1ac .ClusterRoleBindingApplyConfiguration {
286
+ return rbacv1ac .ClusterRoleBinding (
259
287
crbNameFromCluster (cluster )).
260
288
WithLabels (map [string ]string {"ray.io/cluster-name" : cluster .Name }).
261
289
WithSubjects (
262
- rbacapply .Subject ().
290
+ rbacv1ac .Subject ().
263
291
WithKind ("ServiceAccount" ).
264
292
WithName (oauthServiceAccountNameFromCluster (cluster )).
265
293
WithNamespace (cluster .Namespace ),
266
294
).
267
295
WithRoleRef (
268
- rbacapply .RoleRef ().
296
+ rbacv1ac .RoleRef ().
269
297
WithAPIGroup ("rbac.authorization.k8s.io" ).
270
298
WithKind ("ClusterRole" ).
271
299
WithName ("system:auth-delegator" ),
@@ -276,16 +304,16 @@ func oauthServiceAccountNameFromCluster(cluster *rayv1.RayCluster) string {
276
304
return cluster .Name + "-oauth-proxy"
277
305
}
278
306
279
- func desiredServiceAccount (cluster * rayv1.RayCluster ) * coreapply .ServiceAccountApplyConfiguration {
280
- return coreapply .ServiceAccount (oauthServiceAccountNameFromCluster (cluster ), cluster .Namespace ).
307
+ func desiredServiceAccount (cluster * rayv1.RayCluster ) * corev1ac .ServiceAccountApplyConfiguration {
308
+ return corev1ac .ServiceAccount (oauthServiceAccountNameFromCluster (cluster ), cluster .Namespace ).
281
309
WithLabels (map [string ]string {"ray.io/cluster-name" : cluster .Name }).
282
310
WithAnnotations (map [string ]string {
283
311
"serviceaccounts.openshift.io/oauth-redirectreference.first" : "" +
284
312
`{"kind":"OAuthRedirectReference","apiVersion":"v1",` +
285
313
`"reference":{"kind":"Route","name":"` + dashboardNameFromCluster (cluster ) + `"}}` ,
286
314
}).
287
315
WithOwnerReferences (
288
- v1 .OwnerReference ().WithUID (cluster .UID ).WithName (cluster .Name ).WithKind (cluster .Kind ).WithAPIVersion (cluster .APIVersion ),
316
+ metav1ac .OwnerReference ().WithUID (cluster .UID ).WithName (cluster .Name ).WithKind (cluster .Kind ).WithAPIVersion (cluster .APIVersion ),
289
317
)
290
318
}
291
319
@@ -297,19 +325,19 @@ func rayClientNameFromCluster(cluster *rayv1.RayCluster) string {
297
325
return "rayclient-" + cluster .Name
298
326
}
299
327
300
- func desiredClusterRoute (cluster * rayv1.RayCluster ) * routeapply .RouteApplyConfiguration {
301
- return routeapply .Route (dashboardNameFromCluster (cluster ), cluster .Namespace ).
328
+ func desiredClusterRoute (cluster * rayv1.RayCluster ) * routev1ac .RouteApplyConfiguration {
329
+ return routev1ac .Route (dashboardNameFromCluster (cluster ), cluster .Namespace ).
302
330
WithLabels (map [string ]string {"ray.io/cluster-name" : cluster .Name }).
303
- WithSpec (routeapply .RouteSpec ().
304
- WithTo (routeapply .RouteTargetReference ().WithKind ("Service" ).WithName (oauthServiceNameFromCluster (cluster ))).
305
- WithPort (routeapply .RoutePort ().WithTargetPort (intstr .FromString ((oAuthServicePortName )))).
306
- WithTLS (routeapply .TLSConfig ().
331
+ WithSpec (routev1ac .RouteSpec ().
332
+ WithTo (routev1ac .RouteTargetReference ().WithKind ("Service" ).WithName (oauthServiceNameFromCluster (cluster ))).
333
+ WithPort (routev1ac .RoutePort ().WithTargetPort (intstr .FromString ((oAuthServicePortName )))).
334
+ WithTLS (routev1ac .TLSConfig ().
307
335
WithInsecureEdgeTerminationPolicy (routev1 .InsecureEdgeTerminationPolicyRedirect ).
308
336
WithTermination (routev1 .TLSTerminationReencrypt ),
309
337
),
310
338
).
311
339
WithOwnerReferences (
312
- v1 .OwnerReference ().WithUID (cluster .UID ).WithName (cluster .Name ).WithKind (cluster .Kind ).WithAPIVersion (cluster .APIVersion ),
340
+ metav1ac .OwnerReference ().WithUID (cluster .UID ).WithName (cluster .Name ).WithKind (cluster .Kind ).WithAPIVersion (cluster .APIVersion ),
313
341
)
314
342
}
315
343
@@ -321,14 +349,14 @@ func oauthServiceTLSSecretName(cluster *rayv1.RayCluster) string {
321
349
return cluster .Name + "-proxy-tls-secret"
322
350
}
323
351
324
- func desiredOAuthService (cluster * rayv1.RayCluster ) * coreapply .ServiceApplyConfiguration {
325
- return coreapply .Service (oauthServiceNameFromCluster (cluster ), cluster .Namespace ).
352
+ func desiredOAuthService (cluster * rayv1.RayCluster ) * corev1ac .ServiceApplyConfiguration {
353
+ return corev1ac .Service (oauthServiceNameFromCluster (cluster ), cluster .Namespace ).
326
354
WithLabels (map [string ]string {"ray.io/cluster-name" : cluster .Name }).
327
355
WithAnnotations (map [string ]string {"service.beta.openshift.io/serving-cert-secret-name" : oauthServiceTLSSecretName (cluster )}).
328
356
WithSpec (
329
- coreapply .ServiceSpec ().
357
+ corev1ac .ServiceSpec ().
330
358
WithPorts (
331
- coreapply .ServicePort ().
359
+ corev1ac .ServicePort ().
332
360
WithName (oAuthServicePortName ).
333
361
WithPort (oAuthServicePort ).
334
362
WithTargetPort (intstr .FromString (oAuthServicePortName )).
@@ -337,7 +365,7 @@ func desiredOAuthService(cluster *rayv1.RayCluster) *coreapply.ServiceApplyConfi
337
365
WithSelector (map [string ]string {"ray.io/cluster" : cluster .Name , "ray.io/node-type" : "head" }),
338
366
).
339
367
WithOwnerReferences (
340
- v1 .OwnerReference ().WithUID (cluster .UID ).WithName (cluster .Name ).WithKind (cluster .Kind ).WithAPIVersion (cluster .APIVersion ),
368
+ metav1ac .OwnerReference ().WithUID (cluster .UID ).WithName (cluster .Name ).WithKind (cluster .Kind ).WithAPIVersion (cluster .APIVersion ),
341
369
)
342
370
}
343
371
@@ -346,68 +374,121 @@ func oauthSecretNameFromCluster(cluster *rayv1.RayCluster) string {
346
374
}
347
375
348
376
// desiredOAuthSecret defines the desired OAuth secret object
349
- func desiredOAuthSecret (cluster * rayv1.RayCluster , r * RayClusterReconciler ) * coreapply .SecretApplyConfiguration {
377
+ func desiredOAuthSecret (cluster * rayv1.RayCluster , cookieSalt string ) * corev1ac .SecretApplyConfiguration {
350
378
// Generate the cookie secret for the OAuth proxy
351
379
hasher := sha1 .New () // REVIEW is SHA1 okay here?
352
- hasher .Write ([]byte (cluster .Name + r . CookieSalt ))
380
+ hasher .Write ([]byte (cluster .Name + cookieSalt ))
353
381
cookieSecret := base64 .StdEncoding .EncodeToString (hasher .Sum (nil ))
354
382
355
- return coreapply .Secret (oauthSecretNameFromCluster (cluster ), cluster .Namespace ).
383
+ return corev1ac .Secret (oauthSecretNameFromCluster (cluster ), cluster .Namespace ).
356
384
WithLabels (map [string ]string {"ray.io/cluster-name" : cluster .Name }).
357
385
WithStringData (map [string ]string {"cookie_secret" : cookieSecret }).
358
386
WithOwnerReferences (
359
- v1 .OwnerReference ().WithUID (cluster .UID ).WithName (cluster .Name ).WithKind (cluster .Kind ).WithAPIVersion (cluster .APIVersion ),
387
+ metav1ac .OwnerReference ().WithUID (cluster .UID ).WithName (cluster .Name ).WithKind (cluster .Kind ).WithAPIVersion (cluster .APIVersion ),
360
388
)
361
- // Create a Kubernetes secret to store the cookie secret
362
389
}
363
390
364
- func desiredNetworkPolicy (cluster * rayv1.RayCluster , kubeRayNamespaces []string ) * networkingapply.NetworkPolicyApplyConfiguration {
365
- return networkingapply .NetworkPolicy (cluster .Name , cluster .Namespace ).
391
+ func caSecretNameFromCluster (cluster * rayv1.RayCluster ) string {
392
+ return "ca-secret-" + cluster .Name
393
+ }
394
+
395
+ func desiredCASecret (cluster * rayv1.RayCluster , key , cert []byte ) * corev1ac.SecretApplyConfiguration {
396
+ return corev1ac .Secret (caSecretNameFromCluster (cluster ), cluster .Namespace ).
397
+ WithLabels (map [string ]string {"ray.io/cluster-name" : cluster .Name }).
398
+ WithData (map [string ][]byte {
399
+ corev1 .TLSPrivateKeyKey : key ,
400
+ corev1 .TLSCertKey : cert ,
401
+ }).
402
+ WithOwnerReferences (metav1ac .OwnerReference ().
403
+ WithUID (cluster .UID ).
404
+ WithName (cluster .Name ).
405
+ WithKind (cluster .Kind ).
406
+ WithAPIVersion (cluster .APIVersion ))
407
+ }
408
+
409
+ func generateCACertificate () ([]byte , []byte , error ) {
410
+ serialNumber := big .NewInt (rand2 .Int63 ())
411
+ cert := & x509.Certificate {
412
+ SerialNumber : serialNumber ,
413
+ Subject : pkix.Name {
414
+ Organization : []string {"OpenShift AI" },
415
+ },
416
+ NotBefore : time .Now (),
417
+ NotAfter : time .Now ().AddDate (1 , 0 , 0 ),
418
+ IsCA : true ,
419
+ ExtKeyUsage : []x509.ExtKeyUsage {x509 .ExtKeyUsageClientAuth , x509 .ExtKeyUsageServerAuth },
420
+ KeyUsage : x509 .KeyUsageDigitalSignature | x509 .KeyUsageCertSign ,
421
+ BasicConstraintsValid : true ,
422
+ }
423
+
424
+ certPrivateKey , err := rsa .GenerateKey (rand .Reader , 2048 )
425
+ if err != nil {
426
+ return nil , nil , err
427
+ }
428
+
429
+ privateKeyBytes := x509 .MarshalPKCS1PrivateKey (certPrivateKey )
430
+ privateKeyPem := pem .EncodeToMemory (
431
+ & pem.Block {
432
+ Type : "RSA PRIVATE KEY" ,
433
+ Bytes : privateKeyBytes ,
434
+ },
435
+ )
436
+ certBytes , err := x509 .CreateCertificate (rand .Reader , cert , cert , & certPrivateKey .PublicKey , certPrivateKey )
437
+ certPem := pem .EncodeToMemory (& pem.Block {
438
+ Type : "CERTIFICATE" ,
439
+ Bytes : certBytes ,
440
+ })
441
+
442
+ return privateKeyPem , certPem , nil
443
+ }
444
+
445
+ func desiredNetworkPolicy (cluster * rayv1.RayCluster , kubeRayNamespaces []string ) * networkingv1ac.NetworkPolicyApplyConfiguration {
446
+ return networkingv1ac .NetworkPolicy (cluster .Name , cluster .Namespace ).
366
447
WithLabels (map [string ]string {"ray.io/cluster-name" : cluster .Name }).
367
- WithSpec (networkingapply .NetworkPolicySpec ().
368
- WithPodSelector (metav1apply .LabelSelector ().WithMatchLabels (map [string ]string {"ray.io/cluster" : cluster .Name , "ray.io/node-type" : "head" })).
448
+ WithSpec (networkingv1ac .NetworkPolicySpec ().
449
+ WithPodSelector (metav1ac .LabelSelector ().WithMatchLabels (map [string ]string {"ray.io/cluster" : cluster .Name , "ray.io/node-type" : "head" })).
369
450
WithIngress (
370
- networkingapply .NetworkPolicyIngressRule ().
451
+ networkingv1ac .NetworkPolicyIngressRule ().
371
452
WithPorts (
372
- networkingapply .NetworkPolicyPort ().WithProtocol (corev1 .ProtocolTCP ).WithPort (intstr .FromInt (6379 )),
373
- networkingapply .NetworkPolicyPort ().WithProtocol (corev1 .ProtocolTCP ).WithPort (intstr .FromInt (10001 )),
374
- networkingapply .NetworkPolicyPort ().WithProtocol (corev1 .ProtocolTCP ).WithPort (intstr .FromInt (8080 )),
375
- networkingapply .NetworkPolicyPort ().WithProtocol (corev1 .ProtocolTCP ).WithPort (intstr .FromInt (8265 )),
453
+ networkingv1ac .NetworkPolicyPort ().WithProtocol (corev1 .ProtocolTCP ).WithPort (intstr .FromInt (6379 )),
454
+ networkingv1ac .NetworkPolicyPort ().WithProtocol (corev1 .ProtocolTCP ).WithPort (intstr .FromInt (10001 )),
455
+ networkingv1ac .NetworkPolicyPort ().WithProtocol (corev1 .ProtocolTCP ).WithPort (intstr .FromInt (8080 )),
456
+ networkingv1ac .NetworkPolicyPort ().WithProtocol (corev1 .ProtocolTCP ).WithPort (intstr .FromInt (8265 )),
376
457
).WithFrom (
377
- networkingapply .NetworkPolicyPeer ().WithPodSelector (metav1apply .LabelSelector ()),
458
+ networkingv1ac .NetworkPolicyPeer ().WithPodSelector (metav1ac .LabelSelector ()),
378
459
),
379
- networkingapply .NetworkPolicyIngressRule ().
460
+ networkingv1ac .NetworkPolicyIngressRule ().
380
461
WithFrom (
381
- networkingapply .NetworkPolicyPeer ().WithPodSelector (metav1apply .LabelSelector ().
462
+ networkingv1ac .NetworkPolicyPeer ().WithPodSelector (metav1ac .LabelSelector ().
382
463
WithMatchLabels (map [string ]string {"app.kubernetes.io/component" : "kuberay-operator" })).
383
- WithNamespaceSelector (metav1apply .LabelSelector ().
384
- WithMatchExpressions (metav1apply .LabelSelectorRequirement ().
464
+ WithNamespaceSelector (metav1ac .LabelSelector ().
465
+ WithMatchExpressions (metav1ac .LabelSelectorRequirement ().
385
466
WithKey (corev1 .LabelMetadataName ).
386
467
WithOperator (metav1 .LabelSelectorOpIn ).
387
468
WithValues (kubeRayNamespaces ... )))).
388
469
WithPorts (
389
- networkingapply .NetworkPolicyPort ().WithProtocol (corev1 .ProtocolTCP ).WithPort (intstr .FromInt (8265 )),
390
- networkingapply .NetworkPolicyPort ().WithProtocol (corev1 .ProtocolTCP ).WithPort (intstr .FromInt (10001 )),
470
+ networkingv1ac .NetworkPolicyPort ().WithProtocol (corev1 .ProtocolTCP ).WithPort (intstr .FromInt (8265 )),
471
+ networkingv1ac .NetworkPolicyPort ().WithProtocol (corev1 .ProtocolTCP ).WithPort (intstr .FromInt (10001 )),
391
472
),
392
- networkingapply .NetworkPolicyIngressRule ().
473
+ networkingv1ac .NetworkPolicyIngressRule ().
393
474
WithPorts (
394
- networkingapply .NetworkPolicyPort ().WithProtocol (corev1 .ProtocolTCP ).WithPort (intstr .FromInt (8080 )),
475
+ networkingv1ac .NetworkPolicyPort ().WithProtocol (corev1 .ProtocolTCP ).WithPort (intstr .FromInt (8080 )),
395
476
).
396
477
WithFrom (
397
- networkingapply .NetworkPolicyPeer ().WithNamespaceSelector (metav1apply .LabelSelector ().
398
- WithMatchExpressions (metav1apply .LabelSelectorRequirement ().
478
+ networkingv1ac .NetworkPolicyPeer ().WithNamespaceSelector (metav1ac .LabelSelector ().
479
+ WithMatchExpressions (metav1ac .LabelSelectorRequirement ().
399
480
WithKey (corev1 .LabelMetadataName ).
400
481
WithOperator (metav1 .LabelSelectorOpIn ).
401
482
WithValues ("openshift-monitoring" ))),
402
483
),
403
- networkingapply .NetworkPolicyIngressRule ().
484
+ networkingv1ac .NetworkPolicyIngressRule ().
404
485
WithPorts (
405
- networkingapply .NetworkPolicyPort ().WithProtocol (corev1 .ProtocolTCP ).WithPort (intstr .FromInt (8443 )),
486
+ networkingv1ac .NetworkPolicyPort ().WithProtocol (corev1 .ProtocolTCP ).WithPort (intstr .FromInt (8443 )),
406
487
),
407
488
),
408
489
).
409
490
WithOwnerReferences (
410
- v1 .OwnerReference ().WithUID (cluster .UID ).WithName (cluster .Name ).WithKind (cluster .Kind ).WithAPIVersion (cluster .APIVersion ),
491
+ metav1ac .OwnerReference ().WithUID (cluster .UID ).WithName (cluster .Name ).WithKind (cluster .Kind ).WithAPIVersion (cluster .APIVersion ),
411
492
)
412
493
}
413
494
0 commit comments