diff --git a/bundle/manifests/metrics_v1_service.yaml b/bundle/manifests/metrics_v1_service.yaml index 8c24a28cd4a..7c3d713a06d 100644 --- a/bundle/manifests/metrics_v1_service.yaml +++ b/bundle/manifests/metrics_v1_service.yaml @@ -1,6 +1,8 @@ apiVersion: v1 kind: Service metadata: + annotations: + service.beta.openshift.io/serving-cert-secret-name: kata-monitor-certs creationTimestamp: null labels: name: openshift-sandboxed-containers-monitor @@ -8,9 +10,9 @@ metadata: spec: ports: - name: metrics - port: 8090 + port: 8443 protocol: TCP - targetPort: 0 + targetPort: 8443 selector: name: openshift-sandboxed-containers-monitor status: diff --git a/bundle/manifests/openshift-sandboxed-containers-monitor_monitoring.coreos.com_v1_servicemonitor.yaml b/bundle/manifests/openshift-sandboxed-containers-monitor_monitoring.coreos.com_v1_servicemonitor.yaml index b921182acd3..7c36d706a1c 100644 --- a/bundle/manifests/openshift-sandboxed-containers-monitor_monitoring.coreos.com_v1_servicemonitor.yaml +++ b/bundle/manifests/openshift-sandboxed-containers-monitor_monitoring.coreos.com_v1_servicemonitor.yaml @@ -5,6 +5,14 @@ metadata: spec: endpoints: - port: metrics + scheme: https + tlsConfig: + ca: + configMap: + key: service-ca.crt + name: openshift-service-ca.crt + insecureSkipVerify: false + serverName: metrics.openshift-sandboxed-containers-operator.svc namespaceSelector: matchNames: - openshift-sandboxed-containers-operator diff --git a/config/kata-monitor/kata-monitor-service.yaml b/config/kata-monitor/kata-monitor-service.yaml index b4d5b66e224..7874028e1ee 100644 --- a/config/kata-monitor/kata-monitor-service.yaml +++ b/config/kata-monitor/kata-monitor-service.yaml @@ -5,10 +5,13 @@ metadata: namespace: openshift-sandboxed-containers-operator labels: name: openshift-sandboxed-containers-monitor + annotations: + service.beta.openshift.io/serving-cert-secret-name: kata-monitor-certs spec: selector: name: openshift-sandboxed-containers-monitor ports: - name: metrics - port: 8090 + port: 8443 protocol: TCP + targetPort: 8443 diff --git a/config/kata-monitor/kata-monitor-servicemonitor.yaml b/config/kata-monitor/kata-monitor-servicemonitor.yaml index c5110d9a3e0..f9d6499b9be 100644 --- a/config/kata-monitor/kata-monitor-servicemonitor.yaml +++ b/config/kata-monitor/kata-monitor-servicemonitor.yaml @@ -12,3 +12,11 @@ spec: name: openshift-sandboxed-containers-monitor endpoints: - port: metrics + scheme: https + tlsConfig: + ca: + configMap: + name: openshift-service-ca.crt + key: service-ca.crt + serverName: metrics.openshift-sandboxed-containers-operator.svc + insecureSkipVerify: false diff --git a/controllers/openshift_controller.go b/controllers/openshift_controller.go index bc6ac089c4e..804b113ce4e 100644 --- a/controllers/openshift_controller.go +++ b/controllers/openshift_controller.go @@ -401,11 +401,12 @@ func (r *KataConfigOpenShiftReconciler) removeLogLevel() error { return nil } -func (r *KataConfigOpenShiftReconciler) processDaemonsetForMonitor() *appsv1.DaemonSet { +func (r *KataConfigOpenShiftReconciler) processDaemonsetForMonitor(certVersion string) *appsv1.DaemonSet { var ( runPrivileged = false runUserID = int64(1001) runGroupID = int64(1001) + defaultMode = int32(0o644) ) kataMonitorImage := os.Getenv("RELATED_IMAGE_KATA_MONITOR") @@ -443,6 +444,9 @@ func (r *KataConfigOpenShiftReconciler) processDaemonsetForMonitor() *appsv1.Dae Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: dsLabels, + Annotations: map[string]string{ + "kata-monitor-certs/version": certVersion, + }, }, Spec: corev1.PodSpec{ ServiceAccountName: "monitor", @@ -465,7 +469,16 @@ func (r *KataConfigOpenShiftReconciler) processDaemonsetForMonitor() *appsv1.Dae Type: "osc_monitor.process", }, }, - Command: []string{"/usr/bin/kata-monitor", "--listen-address=:8090", "--log-level=debug", "--runtime-endpoint=/run/crio/crio.sock"}, + Command: []string{ + "/usr/bin/kata-monitor", + "--listen-address=:8443", + "--log-level=debug", + "--runtime-endpoint=/run/crio/crio.sock", + "--tls-cert-file=/etc/kata-monitor/certs/tls.crt", + "--tls-key-file=/etc/kata-monitor/certs/tls.key", + "--tls-min-version=" + string(r.TLSProfileSpec.MinTLSVersion), + "--tls-cipher-suites=" + r.tlsCipherSuitesEnvValue(r.TLSProfileSpec), + }, VolumeMounts: []corev1.VolumeMount{ { Name: "crio-sock", @@ -474,6 +487,11 @@ func (r *KataConfigOpenShiftReconciler) processDaemonsetForMonitor() *appsv1.Dae { Name: "sbs", MountPath: "/run/vc/sbs/", + }, + { + Name: "kata-monitor-certs", + MountPath: "/etc/kata-monitor/certs/", + ReadOnly: true, }}, }, }, @@ -494,6 +512,29 @@ func (r *KataConfigOpenShiftReconciler) processDaemonsetForMonitor() *appsv1.Dae }, }, }, + // kata-monitor-certs secret is created asynchronously by OpenShift's service-ca controller. + // The Service annotation 'service.beta.openshift.io/serving-cert-secret-name' triggers cert generation. + // If the DaemonSet is created before the secret exists, pods will remain Pending until the secret + // is mounted. This self-heals once service-ca populates the secret. + { + Name: "kata-monitor-certs", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "kata-monitor-certs", + DefaultMode: &defaultMode, + Items: []corev1.KeyToPath{ + { + Key: "tls.crt", + Path: "tls.crt", + }, + { + Key: "tls.key", + Path: "tls.key", + }, + }, + }, + }, + }, }, }, }, @@ -863,7 +904,13 @@ func (r *KataConfigOpenShiftReconciler) createScc() error { } func (r *KataConfigOpenShiftReconciler) createDaemonsetForMonitor() error { - ds := r.processDaemonsetForMonitor() + certVersion, _, err := r.getSecretVersion("kata-monitor-certs", OperatorNamespace) + if err != nil { + r.Log.Error(err, "Failed getting secret resource version", "name", "kata-monitor-certs") + return err + } + + ds := r.processDaemonsetForMonitor(certVersion) // Set KataConfig instance as the owner and controller if err := controllerutil.SetControllerReference(r.kataConfig, ds, r.Scheme); err != nil { r.Log.Error(err, "failed to set controller reference on the monitor daemonset") @@ -872,7 +919,7 @@ func (r *KataConfigOpenShiftReconciler) createDaemonsetForMonitor() error { r.Log.Info("controller reference set for the monitor daemonset") foundDS := &appsv1.DaemonSet{} - err := r.Client.Get(context.TODO(), types.NamespacedName{Name: ds.Name, Namespace: ds.Namespace}, foundDS) + err = r.Client.Get(context.TODO(), types.NamespacedName{Name: ds.Name, Namespace: ds.Namespace}, foundDS) if err != nil { if k8serrors.IsNotFound(err) { r.Log.Info("Creating a new installation monitor daemonset", "ds.Namespace", ds.Namespace, "ds.Name", ds.Name) @@ -1765,6 +1812,10 @@ func (r *KataConfigOpenShiftReconciler) SetupWithManager(mgr ctrl.Manager) error Watches( &corev1.ConfigMap{}, &ConfigMapEventHandler{r}, + ). + Watches( + &corev1.Secret{}, + &SecretEventHandler{r}, ) mcpAvailable, err := r.isMachineConfigPoolAvailable() @@ -2332,7 +2383,7 @@ func (r *KataConfigOpenShiftReconciler) checkDeletionEligibility() (ctrl.Result, } func (r *KataConfigOpenShiftReconciler) deleteDaemonsetForMonitor() error { - ds := r.processDaemonsetForMonitor() + ds := r.processDaemonsetForMonitor("") err := r.Client.Delete(context.TODO(), ds) if err != nil { if k8serrors.IsNotFound(err) { diff --git a/controllers/secret_event_handler.go b/controllers/secret_event_handler.go new file mode 100644 index 00000000000..9a55e8b21d2 --- /dev/null +++ b/controllers/secret_event_handler.go @@ -0,0 +1,70 @@ +package controllers + +import ( + "context" + + "k8s.io/client-go/util/workqueue" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +const monitorCertSecretName = "kata-monitor-certs" + +type SecretEventHandler struct { + reconciler *KataConfigOpenShiftReconciler +} + +func (sh *SecretEventHandler) Create(ctx context.Context, event event.CreateEvent, queue workqueue.TypedRateLimitingInterface[reconcile.Request]) { + if sh.reconciler.kataConfig == nil { + return + } + + secret := event.Object + if secret.GetNamespace() != OperatorNamespace || secret.GetName() != monitorCertSecretName { + return + } + + log := sh.reconciler.Log.WithName("SecretCreate").WithValues("secret name", secret.GetName()) + log.Info("kata-monitor-certs secret created") + + queue.Add(sh.reconciler.makeReconcileRequest()) +} + +func (sh *SecretEventHandler) Update(ctx context.Context, event event.UpdateEvent, queue workqueue.TypedRateLimitingInterface[reconcile.Request]) { + if sh.reconciler.kataConfig == nil { + return + } + + secret := event.ObjectNew + if secret.GetNamespace() != OperatorNamespace || secret.GetName() != monitorCertSecretName { + return + } + + if event.ObjectOld.GetResourceVersion() == secret.GetResourceVersion() { + return + } + + log := sh.reconciler.Log.WithName("SecretUpdate").WithValues("secret name", secret.GetName()) + log.Info("kata-monitor-certs secret updated") + + queue.Add(sh.reconciler.makeReconcileRequest()) +} + +func (sh *SecretEventHandler) Delete(ctx context.Context, event event.DeleteEvent, queue workqueue.TypedRateLimitingInterface[reconcile.Request]) { + if sh.reconciler.kataConfig == nil { + return + } + + secret := event.Object + if secret.GetNamespace() != OperatorNamespace || secret.GetName() != monitorCertSecretName { + return + } + + log := sh.reconciler.Log.WithName("SecretDelete").WithValues("secret name", secret.GetName()) + log.Info("kata-monitor-certs secret deleted") + + queue.Add(sh.reconciler.makeReconcileRequest()) +} + +func (sh *SecretEventHandler) Generic(ctx context.Context, event event.GenericEvent, queue workqueue.TypedRateLimitingInterface[reconcile.Request]) { +} diff --git a/controllers/utils.go b/controllers/utils.go index bdb46d35054..5b53474ee92 100644 --- a/controllers/utils.go +++ b/controllers/utils.go @@ -378,6 +378,22 @@ func (r *KataConfigOpenShiftReconciler) getConfigMapVersion(name, namespace stri return cm.GetResourceVersion(), true, nil } +func (r *KataConfigOpenShiftReconciler) getSecretVersion(name, namespace string) (string, bool, error) { + secret := &corev1.Secret{} + err := r.Get(context.TODO(), types.NamespacedName{ + Name: name, + Namespace: namespace, + }, secret) + if err != nil { + if k8serrors.IsNotFound(err) { + return "", false, nil + } + return "", false, err + } + + return secret.GetResourceVersion(), true, nil +} + // Method to get proxy environment variables if they are set // Returns a slice of corev1.EnvVar func getProxyEnvVars() []corev1.EnvVar {