From 8dfa15aaa70ef63f765eeceb56bb48e83b69e8cf Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Tue, 15 Oct 2024 10:51:01 +0200 Subject: [PATCH 1/6] feat: Mount CA bundle certificates into devworkspaces Signed-off-by: Anatolii Bazko --- api/v1/checluster_conversion_from.go | 4 +- api/v1/checluster_conversion_to.go | 10 +- api/v2/checluster_types.go | 10 +- api/v2/zz_generated.deepcopy.go | 12 +- .../org.eclipse.che_checlusters.yaml | 14 +- .../bases/org.eclipse.che_checlusters.yaml | 14 +- controllers/che/checluster_controller.go | 5 +- controllers/che/cheobj_verifier.go | 3 +- .../usernamespace/usernamespace_controller.go | 4 +- .../usernamespace_controller_test.go | 4 +- deploy/deployment/kubernetes/combined.yaml | 14 +- ....eclipse.che.CustomResourceDefinition.yaml | 14 +- deploy/deployment/openshift/combined.yaml | 14 +- ....eclipse.che.CustomResourceDefinition.yaml | 14 +- ....eclipse.che.CustomResourceDefinition.yaml | 14 +- pkg/common/constants/constants.go | 4 +- pkg/deploy/dashboard/deployment_dashboard.go | 2 +- pkg/deploy/image-puller/imagepuller.go | 2 +- .../on-reconcile-one-time-migration.go | 2 +- pkg/deploy/secret.go | 4 +- pkg/deploy/server/server_configmap.go | 2 +- pkg/deploy/server/server_deployment.go | 2 +- pkg/deploy/sync.go | 11 +- pkg/deploy/tls/certificates.go | 367 ++++++++++++------ pkg/deploy/tls/certificates_test.go | 34 +- pkg/deploy/tls/tls_utils.go | 9 +- 26 files changed, 413 insertions(+), 176 deletions(-) diff --git a/api/v1/checluster_conversion_from.go b/api/v1/checluster_conversion_from.go index a1f3ebb73..753e4c86c 100644 --- a/api/v1/checluster_conversion_from.go +++ b/api/v1/checluster_conversion_from.go @@ -428,10 +428,10 @@ func (dst *CheCluster) convertFrom_Storage(src *chev2.CheCluster) error { func findTrustStoreConfigMap(namespace string) (string, error) { k8sHelper := k8shelper.New() - _, err := k8sHelper.GetClientset().CoreV1().ConfigMaps(namespace).Get(context.TODO(), constants.DefaultServerTrustStoreConfigMapName, metav1.GetOptions{}) + _, err := k8sHelper.GetClientset().CoreV1().ConfigMaps(namespace).Get(context.TODO(), constants.DefaultCaBundleCertsCMName, metav1.GetOptions{}) if err == nil { // TrustStore ConfigMap with a default name exists - return constants.DefaultServerTrustStoreConfigMapName, nil + return constants.DefaultCaBundleCertsCMName, nil } return "", nil diff --git a/api/v1/checluster_conversion_to.go b/api/v1/checluster_conversion_to.go index d73c6e32d..5273f983d 100644 --- a/api/v1/checluster_conversion_to.go +++ b/api/v1/checluster_conversion_to.go @@ -522,17 +522,17 @@ func createCredentialsSecret(username string, password string, secretName string // Since we API V2 does not have `server.ServerTrustStoreConfigMapName` field, we need to create // the same ConfigMap but with a default name to be correctly handled by a controller. func renameTrustStoreConfigMapToDefault(trustStoreConfigMapName string, namespace string) error { - if trustStoreConfigMapName == constants.DefaultServerTrustStoreConfigMapName { + if trustStoreConfigMapName == constants.DefaultCaBundleCertsCMName { // Already in default name return nil } k8sHelper := k8shelper.New() - _, err := k8sHelper.GetClientset().CoreV1().ConfigMaps(namespace).Get(context.TODO(), constants.DefaultServerTrustStoreConfigMapName, metav1.GetOptions{}) + _, err := k8sHelper.GetClientset().CoreV1().ConfigMaps(namespace).Get(context.TODO(), constants.DefaultCaBundleCertsCMName, metav1.GetOptions{}) if err == nil { // ConfigMap with a default name already exists, we can't proceed - return fmt.Errorf("TrustStore ConfigMap %s already exists", constants.DefaultServerTrustStoreConfigMapName) + return fmt.Errorf("TrustStore ConfigMap %s already exists", constants.DefaultCaBundleCertsCMName) } existedTrustStoreConfigMap, err := k8sHelper.GetClientset().CoreV1().ConfigMaps(namespace).Get(context.TODO(), trustStoreConfigMapName, metav1.GetOptions{}) @@ -556,7 +556,7 @@ func renameTrustStoreConfigMapToDefault(trustStoreConfigMapName string, namespac APIVersion: "v1", }, ObjectMeta: metav1.ObjectMeta{ - Name: constants.DefaultServerTrustStoreConfigMapName, + Name: constants.DefaultCaBundleCertsCMName, Namespace: namespace, Labels: labels.Merge(newTrustStoreConfigMapLabels, existedTrustStoreConfigMap.Labels), }, @@ -573,7 +573,7 @@ func renameTrustStoreConfigMapToDefault(trustStoreConfigMapName string, namespac return err } - logger.Info("TrustStore ConfigMap '" + constants.DefaultServerTrustStoreConfigMapName + "' created.") + logger.Info("TrustStore ConfigMap '" + constants.DefaultCaBundleCertsCMName + "' created.") return nil } diff --git a/api/v2/checluster_types.go b/api/v2/checluster_types.go index 68a18769a..eac17c2d3 100644 --- a/api/v2/checluster_types.go +++ b/api/v2/checluster_types.go @@ -110,7 +110,7 @@ type CheClusterDevEnvironments struct { // The URI must start from `http://` or `https://`. // +optional DefaultEditor string `json:"defaultEditor,omitempty"` - // Default components applied to DevWorkspaces. + // Default components applied to 1spaces. // These default components are meant to be used when a Devfile, that does not contain any components. // +optional DefaultComponents []devfile.Component `json:"defaultComponents,omitempty"` @@ -453,6 +453,14 @@ type DashboardHeaderMessage struct { } type TrustedCerts struct { + // By default, Operator creates and mounts the `ca-certs-merged` ConfigMap containing the CA certificates bundle + // into users` workspaces. This option allows to disable this behavior. + // +optional + DisableMountingCaBundleIntoDevWorkspace *bool `json:"disableMountingCaBundleIntoDevWorkspace,omitempty"` + // The CA bundle mount paths in the workspace pods. + // If not specified, the default is `/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem`. + // +optional + CaBundleMountPaths []string `json:"caBundleMountPaths,omitempty"` // The ConfigMap contains certificates to propagate to the Che components and to provide a particular configuration for Git. // See the following page: https://www.eclipse.org/che/docs/stable/administration-guide/deploying-che-with-support-for-git-repositories-with-self-signed-certificates/ // The ConfigMap must have a `app.kubernetes.io/part-of=che.eclipse.org` label. diff --git a/api/v2/zz_generated.deepcopy.go b/api/v2/zz_generated.deepcopy.go index b54bd90c3..b97807638 100644 --- a/api/v2/zz_generated.deepcopy.go +++ b/api/v2/zz_generated.deepcopy.go @@ -267,7 +267,7 @@ func (in *CheClusterDevEnvironments) DeepCopyInto(out *CheClusterDevEnvironments if in.TrustedCerts != nil { in, out := &in.TrustedCerts, &out.TrustedCerts *out = new(TrustedCerts) - **out = **in + (*in).DeepCopyInto(*out) } if in.DefaultComponents != nil { in, out := &in.DefaultComponents, &out.DefaultComponents @@ -1101,6 +1101,16 @@ func (in *Traefik) DeepCopy() *Traefik { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TrustedCerts) DeepCopyInto(out *TrustedCerts) { *out = *in + if in.DisableMountingCaBundleIntoDevWorkspace != nil { + in, out := &in.DisableMountingCaBundleIntoDevWorkspace, &out.DisableMountingCaBundleIntoDevWorkspace + *out = new(bool) + **out = **in + } + if in.CaBundleMountPaths != nil { + in, out := &in.CaBundleMountPaths, &out.CaBundleMountPaths + *out = make([]string, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrustedCerts. diff --git a/bundle/next/eclipse-che/manifests/org.eclipse.che_checlusters.yaml b/bundle/next/eclipse-che/manifests/org.eclipse.che_checlusters.yaml index a1d052f8b..cf666dab1 100644 --- a/bundle/next/eclipse-che/manifests/org.eclipse.che_checlusters.yaml +++ b/bundle/next/eclipse-che/manifests/org.eclipse.che_checlusters.yaml @@ -5399,7 +5399,7 @@ spec: type: object defaultComponents: description: |- - Default components applied to DevWorkspaces. + Default components applied to 1spaces. These default components are meant to be used when a Devfile, that does not contain any components. items: properties: @@ -8003,6 +8003,18 @@ spec: trustedCerts: description: Trusted certificate settings. properties: + caBundleMountPaths: + description: |- + The CA bundle mount paths in the workspace pods. + If not specified, the default is `/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem`. + items: + type: string + type: array + disableMountingCaBundleIntoDevWorkspace: + description: |- + By default, Operator creates and mounts the `ca-certs-merged` ConfigMap containing the CA certificates into + a users` workspaces. This option allows to disable this behavior. + type: boolean gitTrustedCertsConfigMapName: description: |- The ConfigMap contains certificates to propagate to the Che components and to provide a particular configuration for Git. diff --git a/config/crd/bases/org.eclipse.che_checlusters.yaml b/config/crd/bases/org.eclipse.che_checlusters.yaml index 4e1d8254e..64924956a 100644 --- a/config/crd/bases/org.eclipse.che_checlusters.yaml +++ b/config/crd/bases/org.eclipse.che_checlusters.yaml @@ -5357,7 +5357,7 @@ spec: type: object defaultComponents: description: |- - Default components applied to DevWorkspaces. + Default components applied to 1spaces. These default components are meant to be used when a Devfile, that does not contain any components. items: properties: @@ -7954,6 +7954,18 @@ spec: trustedCerts: description: Trusted certificate settings. properties: + caBundleMountPaths: + description: |- + The CA bundle mount paths in the workspace pods. + If not specified, the default is `/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem`. + items: + type: string + type: array + disableMountingCaBundleIntoDevWorkspace: + description: |- + By default, Operator creates and mounts the `ca-certs-merged` ConfigMap containing the CA certificates into + a users` workspaces. This option allows to disable this behavior. + type: boolean gitTrustedCertsConfigMapName: description: |- The ConfigMap contains certificates to propagate to the Che components and to provide a particular configuration for Git. diff --git a/controllers/che/checluster_controller.go b/controllers/che/checluster_controller.go index b86f2ac5c..f45fec747 100644 --- a/controllers/che/checluster_controller.go +++ b/controllers/che/checluster_controller.go @@ -15,6 +15,8 @@ package che import ( "context" + imagepuller "github.com/eclipse-che/che-operator/pkg/deploy/image-puller" + editorsdefinitions "github.com/eclipse-che/che-operator/pkg/deploy/editors-definitions" "github.com/eclipse-che/che-operator/pkg/common/test" @@ -30,7 +32,6 @@ import ( "github.com/eclipse-che/che-operator/pkg/deploy/devfileregistry" "github.com/eclipse-che/che-operator/pkg/deploy/gateway" identityprovider "github.com/eclipse-che/che-operator/pkg/deploy/identity-provider" - imagepuller "github.com/eclipse-che/che-operator/pkg/deploy/image-puller" "github.com/eclipse-che/che-operator/pkg/deploy/migration" "github.com/eclipse-che/che-operator/pkg/deploy/pluginregistry" "github.com/eclipse-che/che-operator/pkg/deploy/postgres" @@ -96,7 +97,6 @@ func NewReconciler( reconcileManager.RegisterReconciler(migration.NewCheClusterDefaultsCleaner()) reconcileManager.RegisterReconciler(NewCheClusterValidator()) } - reconcileManager.RegisterReconciler(imagepuller.NewImagePuller()) reconcileManager.RegisterReconciler(tls.NewCertificatesReconciler()) reconcileManager.RegisterReconciler(tls.NewTlsSecretReconciler()) @@ -116,6 +116,7 @@ func NewReconciler( reconcileManager.RegisterReconciler(dashboard.NewDashboardReconciler()) reconcileManager.RegisterReconciler(gateway.NewGatewayReconciler()) reconcileManager.RegisterReconciler(server.NewCheServerReconciler()) + reconcileManager.RegisterReconciler(imagepuller.NewImagePuller()) if infrastructure.IsOpenShift() { reconcileManager.RegisterReconciler(containerbuild.NewContainerBuildReconciler()) diff --git a/controllers/che/cheobj_verifier.go b/controllers/che/cheobj_verifier.go index f8a895f69..b200e2039 100644 --- a/controllers/che/cheobj_verifier.go +++ b/controllers/che/cheobj_verifier.go @@ -15,7 +15,6 @@ package che import ( "github.com/eclipse-che/che-operator/pkg/common/constants" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/eclipse-che/che-operator/pkg/deploy/tls" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -39,7 +38,7 @@ func IsTrustedBundleConfigMap(cl client.Client, watchNamespace string, obj clien } // Check for component - if value, exists := obj.GetLabels()[constants.KubernetesComponentLabelKey]; !exists || value != tls.CheCACertsConfigMapLabelValue { + if value, exists := obj.GetLabels()[constants.KubernetesComponentLabelKey]; !exists || value != constants.CheCABundle { // Labels do not match return false, ctrl.Request{} } diff --git a/controllers/usernamespace/usernamespace_controller.go b/controllers/usernamespace/usernamespace_controller.go index 8d146bb73..c30eaf272 100644 --- a/controllers/usernamespace/usernamespace_controller.go +++ b/controllers/usernamespace/usernamespace_controller.go @@ -119,7 +119,7 @@ func (r *CheUserNamespaceReconciler) commonRules(ctx context.Context, namesInChe } func (r *CheUserNamespaceReconciler) watchRulesForConfigMaps(ctx context.Context) handler.EventHandler { - rules := r.commonRules(ctx, tls.CheAllCACertsConfigMapName) + rules := r.commonRules(ctx, tls.CheMergedCABundleCertsCMName) return handler.EnqueueRequestsFromMapFunc( handler.MapFunc(func(obj client.Object) []reconcile.Request { return asReconcileRequestsForNamespaces(obj, rules) @@ -306,7 +306,7 @@ func (r *CheUserNamespaceReconciler) reconcileTrustedCerts(ctx context.Context, } sourceMap := &corev1.ConfigMap{} - if err := r.client.Get(ctx, client.ObjectKey{Name: tls.CheAllCACertsConfigMapName, Namespace: checluster.Namespace}, sourceMap); err != nil { + if err := r.client.Get(ctx, client.ObjectKey{Name: tls.CheMergedCABundleCertsCMName, Namespace: checluster.Namespace}, sourceMap); err != nil { if !errors.IsNotFound(err) { return err } diff --git a/controllers/usernamespace/usernamespace_controller_test.go b/controllers/usernamespace/usernamespace_controller_test.go index a89782861..a695a4eb5 100644 --- a/controllers/usernamespace/usernamespace_controller_test.go +++ b/controllers/usernamespace/usernamespace_controller_test.go @@ -114,7 +114,7 @@ func setupCheCluster(t *testing.T, ctx context.Context, cl client.Client, scheme caCerts := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: tls.CheAllCACertsConfigMapName, + Name: tls.CheMergedCABundleCertsCMName, Namespace: cheNamespaceName, }, Data: map[string]string{ @@ -550,7 +550,7 @@ func TestWatchRulesForConfigMapsInOtherNamespaces(t *testing.T) { APIVersion: "v1", }, ObjectMeta: metav1.ObjectMeta{ - Name: tls.CheAllCACertsConfigMapName, + Name: tls.CheMergedCABundleCertsCMName, Namespace: "eclipse-che", }, } diff --git a/deploy/deployment/kubernetes/combined.yaml b/deploy/deployment/kubernetes/combined.yaml index 98576ccd0..1dc4c0577 100644 --- a/deploy/deployment/kubernetes/combined.yaml +++ b/deploy/deployment/kubernetes/combined.yaml @@ -5378,7 +5378,7 @@ spec: type: object defaultComponents: description: |- - Default components applied to DevWorkspaces. + Default components applied to 1spaces. These default components are meant to be used when a Devfile, that does not contain any components. items: properties: @@ -7975,6 +7975,18 @@ spec: trustedCerts: description: Trusted certificate settings. properties: + caBundleMountPaths: + description: |- + The CA bundle mount paths in the workspace pods. + If not specified, the default is `/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem`. + items: + type: string + type: array + disableMountingCaBundleIntoDevWorkspace: + description: |- + By default, Operator creates and mounts the `ca-certs-merged` ConfigMap containing the CA certificates into + a users` workspaces. This option allows to disable this behavior. + type: boolean gitTrustedCertsConfigMapName: description: |- The ConfigMap contains certificates to propagate to the Che components and to provide a particular configuration for Git. diff --git a/deploy/deployment/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml b/deploy/deployment/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml index da4f87f94..843998e53 100644 --- a/deploy/deployment/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml +++ b/deploy/deployment/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml @@ -5373,7 +5373,7 @@ spec: type: object defaultComponents: description: |- - Default components applied to DevWorkspaces. + Default components applied to 1spaces. These default components are meant to be used when a Devfile, that does not contain any components. items: properties: @@ -7970,6 +7970,18 @@ spec: trustedCerts: description: Trusted certificate settings. properties: + caBundleMountPaths: + description: |- + The CA bundle mount paths in the workspace pods. + If not specified, the default is `/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem`. + items: + type: string + type: array + disableMountingCaBundleIntoDevWorkspace: + description: |- + By default, Operator creates and mounts the `ca-certs-merged` ConfigMap containing the CA certificates into + a users` workspaces. This option allows to disable this behavior. + type: boolean gitTrustedCertsConfigMapName: description: |- The ConfigMap contains certificates to propagate to the Che components and to provide a particular configuration for Git. diff --git a/deploy/deployment/openshift/combined.yaml b/deploy/deployment/openshift/combined.yaml index f00ff5d74..211f2f782 100644 --- a/deploy/deployment/openshift/combined.yaml +++ b/deploy/deployment/openshift/combined.yaml @@ -5378,7 +5378,7 @@ spec: type: object defaultComponents: description: |- - Default components applied to DevWorkspaces. + Default components applied to 1spaces. These default components are meant to be used when a Devfile, that does not contain any components. items: properties: @@ -7975,6 +7975,18 @@ spec: trustedCerts: description: Trusted certificate settings. properties: + caBundleMountPaths: + description: |- + The CA bundle mount paths in the workspace pods. + If not specified, the default is `/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem`. + items: + type: string + type: array + disableMountingCaBundleIntoDevWorkspace: + description: |- + By default, Operator creates and mounts the `ca-certs-merged` ConfigMap containing the CA certificates into + a users` workspaces. This option allows to disable this behavior. + type: boolean gitTrustedCertsConfigMapName: description: |- The ConfigMap contains certificates to propagate to the Che components and to provide a particular configuration for Git. diff --git a/deploy/deployment/openshift/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml b/deploy/deployment/openshift/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml index 1265a520d..4209b9212 100644 --- a/deploy/deployment/openshift/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml +++ b/deploy/deployment/openshift/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml @@ -5373,7 +5373,7 @@ spec: type: object defaultComponents: description: |- - Default components applied to DevWorkspaces. + Default components applied to 1spaces. These default components are meant to be used when a Devfile, that does not contain any components. items: properties: @@ -7970,6 +7970,18 @@ spec: trustedCerts: description: Trusted certificate settings. properties: + caBundleMountPaths: + description: |- + The CA bundle mount paths in the workspace pods. + If not specified, the default is `/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem`. + items: + type: string + type: array + disableMountingCaBundleIntoDevWorkspace: + description: |- + By default, Operator creates and mounts the `ca-certs-merged` ConfigMap containing the CA certificates into + a users` workspaces. This option allows to disable this behavior. + type: boolean gitTrustedCertsConfigMapName: description: |- The ConfigMap contains certificates to propagate to the Che components and to provide a particular configuration for Git. diff --git a/helmcharts/next/crds/checlusters.org.eclipse.che.CustomResourceDefinition.yaml b/helmcharts/next/crds/checlusters.org.eclipse.che.CustomResourceDefinition.yaml index da4f87f94..843998e53 100644 --- a/helmcharts/next/crds/checlusters.org.eclipse.che.CustomResourceDefinition.yaml +++ b/helmcharts/next/crds/checlusters.org.eclipse.che.CustomResourceDefinition.yaml @@ -5373,7 +5373,7 @@ spec: type: object defaultComponents: description: |- - Default components applied to DevWorkspaces. + Default components applied to 1spaces. These default components are meant to be used when a Devfile, that does not contain any components. items: properties: @@ -7970,6 +7970,18 @@ spec: trustedCerts: description: Trusted certificate settings. properties: + caBundleMountPaths: + description: |- + The CA bundle mount paths in the workspace pods. + If not specified, the default is `/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem`. + items: + type: string + type: array + disableMountingCaBundleIntoDevWorkspace: + description: |- + By default, Operator creates and mounts the `ca-certs-merged` ConfigMap containing the CA certificates into + a users` workspaces. This option allows to disable this behavior. + type: boolean gitTrustedCertsConfigMapName: description: |- The ConfigMap contains certificates to propagate to the Che components and to provide a particular configuration for Git. diff --git a/pkg/common/constants/constants.go b/pkg/common/constants/constants.go index 623f68a6c..84ddcceac 100644 --- a/pkg/common/constants/constants.go +++ b/pkg/common/constants/constants.go @@ -44,7 +44,7 @@ const ( DefaultServerLogLevel = "INFO" DefaultServerMetricsPort = int32(8087) DefaultServerDebugPort = int32(8000) - DefaultServerTrustStoreConfigMapName = "ca-certs" + DefaultCaBundleCertsCMName = "ca-certs" DefaultProxyCredentialsSecret = "proxy-credentials" DefaultGitSelfSignedCertsConfigMapName = "che-git-self-signed-cert" DefaultJavaOpts = "-XX:MaxRAMPercentage=85.0" @@ -119,6 +119,7 @@ const ( GatewayAuthorizationContainerName = "kube-rbac-proxy" KubernetesImagePullerComponentName = "kubernetes-image-puller" EditorDefinitionComponentName = "editor-definition" + CheCABundle = "ca-bundle" // common CheFlavor = "che" @@ -137,4 +138,5 @@ var ( "app": "che", "component": "che-gateway-config", } + DefaultCaBundleMountPaths = []string{"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"} ) diff --git a/pkg/deploy/dashboard/deployment_dashboard.go b/pkg/deploy/dashboard/deployment_dashboard.go index 7c0e5e2b0..936249c96 100644 --- a/pkg/deploy/dashboard/deployment_dashboard.go +++ b/pkg/deploy/dashboard/deployment_dashboard.go @@ -248,7 +248,7 @@ func (d *DashboardReconciler) provisionCustomPublicCA(volumes []corev1.Volume, v VolumeSource: corev1.VolumeSource{ ConfigMap: &corev1.ConfigMapVolumeSource{ LocalObjectReference: corev1.LocalObjectReference{ - Name: tls.CheAllCACertsConfigMapName, + Name: tls.CheMergedCABundleCertsCMName, }, }, }, diff --git a/pkg/deploy/image-puller/imagepuller.go b/pkg/deploy/image-puller/imagepuller.go index 7bfb9073e..437f8547f 100644 --- a/pkg/deploy/image-puller/imagepuller.go +++ b/pkg/deploy/image-puller/imagepuller.go @@ -153,7 +153,7 @@ func (ip *ImagePuller) syncKubernetesImagePuller(defaultImages []string, ctx *ch imagePuller.Spec.Images = convertToSpecField(defaultImages) } - return deploy.SyncWithClient(ctx.ClusterAPI.NonCachingClient, ctx, imagePuller, kubernetesImagePullerDiffOpts) + return deploy.SyncForClient(ctx.ClusterAPI.NonCachingClient, ctx, imagePuller, kubernetesImagePullerDiffOpts) } func getImagePullerCustomResourceName(ctx *chetypes.DeployContext) string { diff --git a/pkg/deploy/migration/on-reconcile-one-time-migration.go b/pkg/deploy/migration/on-reconcile-one-time-migration.go index fbdcf7b17..16e05d228 100644 --- a/pkg/deploy/migration/on-reconcile-one-time-migration.go +++ b/pkg/deploy/migration/on-reconcile-one-time-migration.go @@ -119,7 +119,7 @@ func addPartOfCheLabeltoUserDefinedObjects(ctx *chetypes.DeployContext) error { } // Legacy config map with additional CA certificates - if err := addPartOfCheLabelToConfigMap(ctx, constants.DefaultServerTrustStoreConfigMapName); err != nil { + if err := addPartOfCheLabelToConfigMap(ctx, constants.DefaultCaBundleCertsCMName); err != nil { return err } diff --git a/pkg/deploy/secret.go b/pkg/deploy/secret.go index 1b5861717..5de872070 100644 --- a/pkg/deploy/secret.go +++ b/pkg/deploy/secret.go @@ -38,7 +38,7 @@ func SyncSecretToCluster( namespace string, data map[string][]byte) (bool, error) { - secretSpec := GetSecretSpec(deployContext, name, namespace, data) + secretSpec := GetSecretSpec(name, namespace, data) return Sync(deployContext, secretSpec, SecretDiffOpts) } @@ -83,7 +83,7 @@ func GetSecrets(deployContext *chetypes.DeployContext, labels map[string]string, } // GetSecretSpec return default secret config for given data -func GetSecretSpec(deployContext *chetypes.DeployContext, name string, namespace string, data map[string][]byte) *corev1.Secret { +func GetSecretSpec(name string, namespace string, data map[string][]byte) *corev1.Secret { labels := GetLabels(defaults.GetCheFlavor()) secret := &corev1.Secret{ TypeMeta: metav1.TypeMeta{ diff --git a/pkg/deploy/server/server_configmap.go b/pkg/deploy/server/server_configmap.go index fa6044df6..2e53c6c3f 100644 --- a/pkg/deploy/server/server_configmap.go +++ b/pkg/deploy/server/server_configmap.go @@ -173,7 +173,7 @@ func (s *CheServerReconciler) getCheConfigMapData(ctx *chetypes.DeployContext) ( PluginRegistryInternalUrl: pluginRegistryInternalURL, CheJGroupsKubernetesLabels: cheLabels, CheMetricsEnabled: cheMetrics, - CheTrustedCABundlesConfigMap: deploytls.CheAllCACertsConfigMapName, + CheTrustedCABundlesConfigMap: deploytls.CheMergedCABundleCertsCMName, ServerStrategy: "single-host", WorkspaceExposure: "gateway", SingleHostGatewayConfigMapLabels: singleHostGatewayConfigMapLabels, diff --git a/pkg/deploy/server/server_deployment.go b/pkg/deploy/server/server_deployment.go index 0998f4b04..1e71dac0f 100644 --- a/pkg/deploy/server/server_deployment.go +++ b/pkg/deploy/server/server_deployment.go @@ -51,7 +51,7 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *chetypes.DeployContext) (*ap VolumeSource: corev1.VolumeSource{ ConfigMap: &corev1.ConfigMapVolumeSource{ LocalObjectReference: corev1.LocalObjectReference{ - Name: tls.CheAllCACertsConfigMapName, + Name: tls.CheMergedCABundleCertsCMName, }, }, }, diff --git a/pkg/deploy/sync.go b/pkg/deploy/sync.go index 3a8ef51f8..02c1dd2d9 100644 --- a/pkg/deploy/sync.go +++ b/pkg/deploy/sync.go @@ -37,10 +37,10 @@ var ( // Returns true if object is up-to-date otherwise returns false func Sync(deployContext *chetypes.DeployContext, blueprint client.Object, diffOpts ...cmp.Option) (bool, error) { cli := getClientForObject(blueprint.GetNamespace(), deployContext) - return SyncWithClient(cli, deployContext, blueprint, diffOpts...) + return SyncForClient(cli, deployContext, blueprint, diffOpts...) } -func SyncWithClient(cli client.Client, deployContext *chetypes.DeployContext, blueprint client.Object, diffOpts ...cmp.Option) (bool, error) { +func SyncForClient(cli client.Client, deployContext *chetypes.DeployContext, blueprint client.Object, diffOpts ...cmp.Option) (bool, error) { runtimeObject, ok := blueprint.(runtime.Object) if !ok { return false, fmt.Errorf("object %T is not a runtime.Object. Cannot sync it", runtimeObject) @@ -76,6 +76,13 @@ func Get(deployContext *chetypes.DeployContext, key client.ObjectKey, actual cli return doGet(context.TODO(), cli, key, actual) } +// Get gets object. +// Returns true if object exists otherwise returns false. +// Returns error if object cannot be retrieved otherwise returns nil. +func GetForClient(cli client.Client, key client.ObjectKey, actual client.Object) (bool, error) { + return doGet(context.TODO(), cli, key, actual) +} + // Gets namespaced scope object by name // Returns true if object exists otherwise returns false. func GetNamespacedObject(deployContext *chetypes.DeployContext, name string, actual client.Object) (bool, error) { diff --git a/pkg/deploy/tls/certificates.go b/pkg/deploy/tls/certificates.go index cc098ee63..1f0da995f 100644 --- a/pkg/deploy/tls/certificates.go +++ b/pkg/deploy/tls/certificates.go @@ -13,39 +13,43 @@ package tls import ( - "context" + "errors" + "fmt" + "os" + "path/filepath" "reflect" "strings" + "github.com/devfile/devworkspace-operator/pkg/infrastructure" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/eclipse-che/che-operator/pkg/common/chetypes" "github.com/eclipse-che/che-operator/pkg/common/constants" defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" "github.com/eclipse-che/che-operator/pkg/deploy" - "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/reconcile" ) const ( - injector = "config.openshift.io/inject-trusted-cabundle" - // CheCACertsConfigMapLabelKey is the label value which marks config map with additional CA certificates - CheCACertsConfigMapLabelValue = "ca-bundle" - // CheAllCACertsConfigMapName is the name of config map which contains all additional trusted by Che TLS CA certificates - CheAllCACertsConfigMapName = "ca-certs-merged" - // CheMergedCAConfigMapRevisionsAnnotationKey is annotation name which holds versions of included config maps in format: cm-name1=ver1,cm-name2=ver2 - CheMergedCAConfigMapRevisionsAnnotationKey = "che.eclipse.org/included-configmaps" - - KubernetesRootCertificateConfigMapName = "kube-root-ca.crt" - - // Local constants - // labelEqualSign constant is used as a replacement for '=' symbol in labels because '=' is not allowed there - labelEqualSign = "-" - // labelCommaSign constant is used as a replacement for ',' symbol in labels because ',' is not allowed there - labelCommaSign = "." + // OpenShift annotation to inject trusted CA bundle + injectTrustedCaBundle = "config.openshift.io/inject-trusted-cabundle" + kubernetesRootCACertsCMName = "kube-root-ca.crt" + kubernetesCABundleCertsDir = "/etc/pki/ca-trust/extracted/pem" + kubernetesCABundleCertsFile = "tls-ca-bundle.pem" + + // The ConfigMap name for merged CA bundle certificates + CheMergedCABundleCertsCMName = "ca-certs-merged" + + // Annotation holds revisions of included config maps + // in the format: name1#ver1 name2=ver2 + cheCABundleIncludedCMRevisions = "che.eclipse.org/included-configmaps" + entrySplitter = " " + keyValueSplitter = "#" ) type CertificatesReconciler struct { @@ -57,8 +61,12 @@ func NewCertificatesReconciler() *CertificatesReconciler { } func (c *CertificatesReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { - if ctx.Proxy.TrustedCAMapName != "" { - if done, err := c.syncTrustStoreConfigMapToCluster(ctx); !done { + if infrastructure.IsOpenShift() { + if done, err := c.syncOpenShiftCABundleCertificates(ctx); !done { + return reconcile.Result{}, false, err + } + } else { + if done, err := c.syncKubernetesCABundleCertificates(ctx); !done { return reconcile.Result{}, false, err } } @@ -67,7 +75,17 @@ func (c *CertificatesReconciler) Reconcile(ctx *chetypes.DeployContext) (reconci return reconcile.Result{}, false, err } - if done, err := c.syncAdditionalCACertsConfigMapToCluster(ctx); !done { + if done, err := c.syncGitTrustedCertificates(ctx); !done { + return reconcile.Result{}, false, err + } + + if ctx.IsSelfSignedCertificate { + if done, err := c.syncSelfSignedCertificates(ctx); !done { + return reconcile.Result{}, false, err + } + } + + if done, err := c.syncCheCABundleCerts(ctx); !done { return reconcile.Result{}, false, err } @@ -78,152 +96,259 @@ func (c *CertificatesReconciler) Finalize(ctx *chetypes.DeployContext) bool { return true } -func (c *CertificatesReconciler) syncTrustStoreConfigMapToCluster(ctx *chetypes.DeployContext) (bool, error) { - configMapSpec := deploy.GetConfigMapSpec(ctx, constants.DefaultServerTrustStoreConfigMapName, map[string]string{}, defaults.GetCheFlavor()) - - // OpenShift will automatically injects all certs into the configmap - configMapSpec.ObjectMeta.Labels[injector] = "true" - configMapSpec.ObjectMeta.Labels[constants.KubernetesPartOfLabelKey] = constants.CheEclipseOrg - configMapSpec.ObjectMeta.Labels[constants.KubernetesComponentLabelKey] = CheCACertsConfigMapLabelValue +func (c *CertificatesReconciler) syncOpenShiftCABundleCertificates(ctx *chetypes.DeployContext) (bool, error) { + openShiftCaBundleCM := deploy.GetConfigMapSpec( + ctx, + constants.DefaultCaBundleCertsCMName, + map[string]string{}, + defaults.GetCheFlavor(), + ) + openShiftCaBundleCM.ObjectMeta.Labels[injectTrustedCaBundle] = "true" + openShiftCaBundleCM.ObjectMeta.Labels[constants.KubernetesComponentLabelKey] = constants.CheCABundle + + return deploy.Sync( + ctx, + openShiftCaBundleCM, + cmp.Options{ + cmpopts.IgnoreFields(corev1.ConfigMap{}, "TypeMeta"), + cmpopts.IgnoreFields(corev1.ConfigMap{}, "Data"), + cmp.Comparer(func(x, y metav1.ObjectMeta) bool { + return x.Labels[injectTrustedCaBundle] == y.Labels[injectTrustedCaBundle] && + x.Labels[constants.KubernetesComponentLabelKey] == y.Labels[constants.KubernetesComponentLabelKey] + }), + }, + ) +} - actual := &corev1.ConfigMap{} - exists, err := deploy.GetNamespacedObject(ctx, constants.DefaultServerTrustStoreConfigMapName, actual) +func (c *CertificatesReconciler) syncKubernetesCABundleCertificates(ctx *chetypes.DeployContext) (bool, error) { + data, err := os.ReadFile(kubernetesCABundleCertsDir + string(os.PathSeparator) + kubernetesCABundleCertsFile) if err != nil { + if errors.Is(err, os.ErrNotExist) { + return true, nil + } + return false, err } - if !exists { - // We have to create an empty config map with the specific labels - return deploy.CreateIgnoreIfExists(ctx, configMapSpec) + kubernetesCaBundleCM := deploy.GetConfigMapSpec( + ctx, + constants.DefaultCaBundleCertsCMName, + map[string]string{"ca.crt": string(data)}, + constants.CheCABundle, + ) + + return deploy.Sync(ctx, kubernetesCaBundleCM, deploy.ConfigMapDiffOpts) +} + +// syncGitTrustedCertificates adds labels to git trusted certificates ConfigMap +// to include them into the final bundle +func (c *CertificatesReconciler) syncGitTrustedCertificates(ctx *chetypes.DeployContext) (bool, error) { + if ctx.CheCluster.Spec.DevEnvironments.TrustedCerts == nil || ctx.CheCluster.Spec.DevEnvironments.TrustedCerts.GitTrustedCertsConfigMapName == "" { + return true, nil } - if actual.ObjectMeta.Labels[injector] != "true" || - actual.ObjectMeta.Labels[constants.KubernetesPartOfLabelKey] != constants.CheEclipseOrg || - actual.ObjectMeta.Labels[constants.KubernetesComponentLabelKey] != CheCACertsConfigMapLabelValue { + gitTrustedCertsCM := &corev1.ConfigMap{} + gitTrustedCertsKey := types.NamespacedName{ + Namespace: ctx.CheCluster.Namespace, + Name: ctx.CheCluster.Spec.DevEnvironments.TrustedCerts.GitTrustedCertsConfigMapName, + } - actual.ObjectMeta.Labels[injector] = "true" - actual.ObjectMeta.Labels[constants.KubernetesPartOfLabelKey] = constants.CheEclipseOrg - actual.ObjectMeta.Labels[constants.KubernetesComponentLabelKey] = CheCACertsConfigMapLabelValue + exists, err := deploy.Get(ctx, gitTrustedCertsKey, gitTrustedCertsCM) + if !exists { + return err == nil, err + } - logrus.Infof("Updating existed object: %s, name: %s", configMapSpec.Kind, configMapSpec.Name) - if err := ctx.ClusterAPI.Client.Update(context.TODO(), actual); err != nil { - return false, err + if gitTrustedCertsCM.Data["ca.crt"] != "" { + gitTrustedCertsCM.TypeMeta = metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", } + + // Add necessary labels to the ConfigMap + gitTrustedCertsCM.Labels[constants.KubernetesPartOfLabelKey] = constants.CheEclipseOrg + gitTrustedCertsCM.Labels[constants.KubernetesComponentLabelKey] = constants.CheCABundle + + return deploy.Sync( + ctx, + gitTrustedCertsCM, + cmp.Options{ + cmpopts.IgnoreFields(corev1.ConfigMap{}, "TypeMeta"), + cmp.Comparer(func(x, y metav1.ObjectMeta) bool { + return x.Labels[constants.KubernetesPartOfLabelKey] == y.Labels[constants.KubernetesPartOfLabelKey] && + x.Labels[constants.KubernetesComponentLabelKey] == y.Labels[constants.KubernetesComponentLabelKey] + }), + }) } return true, nil } -// syncAdditionalCACertsConfigMapToCluster adds labels to ConfigMap `kube-root-ca.crt` to propagate -// Kubernetes root certificates to Che components. It is needed to use NonCachingClient because the map -// initially is not in the cache. +// syncSelfSignedCertificates creates a ConfigMap with self-signed certificates and adds labels to it +// to include them into the final bundle +func (c *CertificatesReconciler) syncSelfSignedCertificates(ctx *chetypes.DeployContext) (bool, error) { + selfSignedCertSecret := &corev1.Secret{} + selfSignedCertSecretKey := types.NamespacedName{ + Name: constants.DefaultSelfSignedCertificateSecretName, + Namespace: ctx.CheCluster.Namespace, + } + + exists, err := deploy.Get(ctx, selfSignedCertSecretKey, selfSignedCertSecret) + if !exists { + return err == nil, err + } + + if len(selfSignedCertSecret.Data["ca.crt"]) > 0 { + selfSignedCertCM := deploy.GetConfigMapSpec( + ctx, + constants.DefaultSelfSignedCertificateSecretName, + map[string]string{ + "ca.crt": string(selfSignedCertSecret.Data["ca.crt"]), + }, + constants.CheCABundle) + + return deploy.Sync(ctx, selfSignedCertCM, deploy.ConfigMapDiffOpts) + } + + return true, nil +} + +// syncKubernetesRootCertificates adds labels to `kube-root-ca.crt` ConfigMap +// to include them into the final bundle func (c *CertificatesReconciler) syncKubernetesRootCertificates(ctx *chetypes.DeployContext) (bool, error) { - kubeRootCertsConfigMap := &corev1.ConfigMap{} - if err := ctx.ClusterAPI.NonCachingClient.Get( - context.TODO(), - types.NamespacedName{ - Name: KubernetesRootCertificateConfigMapName, - Namespace: ctx.CheCluster.Namespace, - }, - kubeRootCertsConfigMap); err != nil { - if errors.IsNotFound(err) { - return true, nil - } else { - return false, err - } + client := ctx.ClusterAPI.NonCachingClient + kubeRootCertsCM := &corev1.ConfigMap{} + kubeRootCertsCMKey := types.NamespacedName{ + Name: kubernetesRootCACertsCMName, + Namespace: ctx.CheCluster.Namespace, } - if kubeRootCertsConfigMap.GetLabels() == nil { - kubeRootCertsConfigMap.SetLabels(map[string]string{}) + exists, err := deploy.GetForClient(client, kubeRootCertsCMKey, kubeRootCertsCM) + if !exists { + return err == nil, err } - kubeRootCertsConfigMap.Labels[constants.KubernetesPartOfLabelKey] = constants.CheEclipseOrg - kubeRootCertsConfigMap.Labels[constants.KubernetesComponentLabelKey] = CheCACertsConfigMapLabelValue + if kubeRootCertsCM.GetLabels() == nil { + kubeRootCertsCM.SetLabels(map[string]string{}) + } // Set TypeMeta to avoid "cause: no version "" has been registered in scheme" error - kubeRootCertsConfigMap.TypeMeta = metav1.TypeMeta{ + kubeRootCertsCM.TypeMeta = metav1.TypeMeta{ Kind: "ConfigMap", APIVersion: "v1", } - return deploy.SyncWithClient(ctx.ClusterAPI.NonCachingClient, ctx, kubeRootCertsConfigMap, deploy.ConfigMapDiffOpts) + + // Add necessary labels to the ConfigMap + kubeRootCertsCM.Labels[constants.KubernetesPartOfLabelKey] = constants.CheEclipseOrg + kubeRootCertsCM.Labels[constants.KubernetesComponentLabelKey] = constants.CheCABundle + + return deploy.SyncForClient( + client, + ctx, + kubeRootCertsCM, + cmp.Options{ + cmpopts.IgnoreFields(corev1.ConfigMap{}, "TypeMeta"), + cmp.Comparer(func(x, y metav1.ObjectMeta) bool { + return x.Labels[constants.KubernetesPartOfLabelKey] == y.Labels[constants.KubernetesPartOfLabelKey] && + x.Labels[constants.KubernetesComponentLabelKey] == y.Labels[constants.KubernetesComponentLabelKey] + }), + }) } -func (c *CertificatesReconciler) syncAdditionalCACertsConfigMapToCluster(ctx *chetypes.DeployContext) (bool, error) { - // Get all source config maps, if any - caConfigMaps, err := GetCACertsConfigMaps(ctx.ClusterAPI.Client, ctx.CheCluster.GetNamespace()) +// syncCheCABundleCerts merges all trusted CA certificates into a single ConfigMap `ca-certs-merged`, +// adds labels and annotations to mount it into dev workspaces. +func (c *CertificatesReconciler) syncCheCABundleCerts(ctx *chetypes.DeployContext) (bool, error) { + // Get all ConfigMaps with trusted CA certificates + cheCABundlesCMs, err := GetCheCABundles(ctx.ClusterAPI.Client, ctx.CheCluster.GetNamespace()) if err != nil { return false, err } - mergedCAConfigMap := &corev1.ConfigMap{} - err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: CheAllCACertsConfigMapName}, mergedCAConfigMap) - if err == nil { - // Merged config map exists. Check if it is up to date. - caConfigMapsCurrentRevisions := make(map[string]string) - for _, cm := range caConfigMaps { - caConfigMapsCurrentRevisions[cm.Name] = cm.ResourceVersion + // Calculated expected revisions and content + cheCABundlesExpectedContent := "" + cheCABundleExpectedRevisions := make(map[string]string) + cheCABundlesExpectedRevisionsAsString := "" + for _, cm := range cheCABundlesCMs { + cheCABundleExpectedRevisions[cm.Name] = cm.ResourceVersion + cheCABundlesExpectedRevisionsAsString += + cm.ObjectMeta.Name + keyValueSplitter + cm.ObjectMeta.ResourceVersion + entrySplitter + + for dataKey, dataValue := range cm.Data { + cheCABundlesExpectedContent += fmt.Sprintf( + "# ConfigMap: %s, Key: %s\n%s\n\n", + cm.Name, + dataKey, + dataValue, + ) } + } + + // Calculated actual revisions + mergedCABundlesCM := &corev1.ConfigMap{} + mergedCABundlesCMKey := types.NamespacedName{ + Name: CheMergedCABundleCertsCMName, + Namespace: ctx.CheCluster.Namespace, + } + + exists, err := deploy.Get(ctx, mergedCABundlesCMKey, mergedCABundlesCM) + if err != nil { + return false, err + } - caConfigMapsCachedRevisions := make(map[string]string) - if mergedCAConfigMap.ObjectMeta.Annotations != nil { - if revisions, exists := mergedCAConfigMap.ObjectMeta.Annotations[CheMergedCAConfigMapRevisionsAnnotationKey]; exists { - for _, cmNameRevision := range strings.Split(revisions, labelCommaSign) { - nameRevision := strings.Split(cmNameRevision, labelEqualSign) - if len(nameRevision) != 2 { - // The label value is invalid, recreate merged config map - break + if exists { + cheCABundleCMActualRevisions := make(map[string]string) + if mergedCABundlesCM.GetAnnotations() != nil { + if revs, ok := mergedCABundlesCM.ObjectMeta.Annotations[cheCABundleIncludedCMRevisions]; ok { + for _, rev := range strings.Split(revs, entrySplitter) { + item := strings.Split(rev, keyValueSplitter) + if len(item) == 2 { + cheCABundleCMActualRevisions[item[0]] = item[1] } - caConfigMapsCachedRevisions[nameRevision[0]] = nameRevision[1] } } } - if reflect.DeepEqual(caConfigMapsCurrentRevisions, caConfigMapsCachedRevisions) { - // Existing merged config map is up to date, do nothing + // Compare actual and expected revisions to check if we need to update the ConfigMap + if reflect.DeepEqual(cheCABundleExpectedRevisions, cheCABundleCMActualRevisions) { return true, nil } - } else { - if !errors.IsNotFound(err) { - return false, err - } - // Merged config map doesn't exist. Create it. } - // Merged config map is out of date or doesn't exist - // Merge all config maps into single one to mount inside Che components and workspaces - data := make(map[string]string) - revisions := "" - for _, cm := range caConfigMaps { - // Copy data - for key, dataRecord := range cm.Data { - data[cm.ObjectMeta.Name+"."+key] = dataRecord + // Sync a new ConfigMap with all trusted CA certificates + mergedCABundlesCM = deploy.GetConfigMapSpec( + ctx, + CheMergedCABundleCertsCMName, + map[string]string{"ca.crt": cheCABundlesExpectedContent}, + defaults.GetCheFlavor(), + ) + + // Add annotations with included config maps revisions + mergedCABundlesCM.ObjectMeta.Annotations[cheCABundleIncludedCMRevisions] = cheCABundlesExpectedRevisionsAsString + + if ctx.CheCluster.Spec.DevEnvironments.TrustedCerts == nil || + (ctx.CheCluster.Spec.DevEnvironments.TrustedCerts.DisableMountingCaBundleIntoDevWorkspace != nil && + !*ctx.CheCluster.Spec.DevEnvironments.TrustedCerts.DisableMountingCaBundleIntoDevWorkspace) { + + mountPaths := constants.DefaultCaBundleMountPaths + if ctx.CheCluster.Spec.DevEnvironments.TrustedCerts != nil && + len(ctx.CheCluster.Spec.DevEnvironments.TrustedCerts.CaBundleMountPaths) > 0 { + mountPaths = ctx.CheCluster.Spec.DevEnvironments.TrustedCerts.CaBundleMountPaths } - // Save source config map revision - if revisions != "" { - revisions += labelCommaSign - } - revisions += cm.ObjectMeta.Name + labelEqualSign + cm.ObjectMeta.ResourceVersion - } - - // Add SelfSigned certificate for a git repository to the bundle - if ctx.CheCluster.Spec.DevEnvironments.TrustedCerts != nil && ctx.CheCluster.Spec.DevEnvironments.TrustedCerts.GitTrustedCertsConfigMapName != "" { - gitTrustedCertsConfig := &corev1.ConfigMap{} - exists, err := deploy.GetNamespacedObject(ctx, ctx.CheCluster.Spec.DevEnvironments.TrustedCerts.GitTrustedCertsConfigMapName, gitTrustedCertsConfig) - if err != nil { - return false, err - } else if exists && gitTrustedCertsConfig.Data["ca.crt"] != "" { - if revisions != "" { - revisions += labelCommaSign - } - revisions += gitTrustedCertsConfig.Name + labelEqualSign + gitTrustedCertsConfig.ResourceVersion + // Mount CA bundle into dev workspaces + // TODO rework to create a dedicated CM for every mount path + for _, mountPath := range mountPaths { + mountDir, mountFileName := filepath.Split(mountPath) + + // Mark ConfigMap as workspace config (will be mounted in all workspace pods) + mergedCABundlesCM.ObjectMeta.Labels[constants.KubernetesComponentLabelKey] = constants.WorkspacesConfig + + // Add annotations to mount certificates in desired path + mergedCABundlesCM.ObjectMeta.Annotations["controller.devfile.io/mount-as"] = "subpath" + mergedCABundlesCM.ObjectMeta.Annotations["controller.devfile.io/mount-path"] = mountDir - data[gitTrustedCertsConfig.Name+".ca.crt"] = gitTrustedCertsConfig.Data["ca.crt"] + mergedCABundlesCM.Data = map[string]string{mountFileName: cheCABundlesExpectedContent} } } - mergedCAConfigMapSpec := deploy.GetConfigMapSpec(ctx, CheAllCACertsConfigMapName, data, defaults.GetCheFlavor()) - mergedCAConfigMapSpec.ObjectMeta.Labels[constants.KubernetesPartOfLabelKey] = constants.CheEclipseOrg - mergedCAConfigMapSpec.ObjectMeta.Annotations[CheMergedCAConfigMapRevisionsAnnotationKey] = revisions - return deploy.SyncConfigMapSpecToCluster(ctx, mergedCAConfigMapSpec) + return deploy.Sync(ctx, mergedCABundlesCM, deploy.ConfigMapDiffOpts) } diff --git a/pkg/deploy/tls/certificates_test.go b/pkg/deploy/tls/certificates_test.go index 73974f135..36227adbf 100644 --- a/pkg/deploy/tls/certificates_test.go +++ b/pkg/deploy/tls/certificates_test.go @@ -36,17 +36,17 @@ func TestSyncDefaultTrustStoreConfigMapToCluster(t *testing.T) { ctx := test.GetDeployContext(checluster, []runtime.Object{}) certificates := NewCertificatesReconciler() - done, err := certificates.syncTrustStoreConfigMapToCluster(ctx) + done, err := certificates.syncOpenShiftCABundleCertificates(ctx) assert.Nil(t, err) assert.True(t, done) trustStoreConfigMap := &corev1.ConfigMap{} err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "ca-certs", Namespace: "eclipse-che"}, trustStoreConfigMap) assert.Nil(t, err) - assert.Equal(t, trustStoreConfigMap.ObjectMeta.Labels[injector], "true") + assert.Equal(t, trustStoreConfigMap.ObjectMeta.Labels[injectTrustedCaBundle], "true") } -func TestSyncExistedTrustStoreConfigMapToCluster(t *testing.T) { +func SyncExistedTrustStoreConfigMapToCluster(t *testing.T) { trustStoreConfigMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "ca-certs", @@ -64,18 +64,18 @@ func TestSyncExistedTrustStoreConfigMapToCluster(t *testing.T) { ctx := test.GetDeployContext(checluster, []runtime.Object{trustStoreConfigMap}) certificates := NewCertificatesReconciler() - done, err := certificates.syncTrustStoreConfigMapToCluster(ctx) + done, err := certificates.syncOpenShiftCABundleCertificates(ctx) assert.Nil(t, err) assert.True(t, done) err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "ca-certs", Namespace: "eclipse-che"}, trustStoreConfigMap) assert.Nil(t, err) - assert.Equal(t, trustStoreConfigMap.ObjectMeta.Labels[injector], "true") + assert.Equal(t, trustStoreConfigMap.ObjectMeta.Labels[injectTrustedCaBundle], "true") assert.Equal(t, trustStoreConfigMap.ObjectMeta.Labels["a"], "b") assert.Equal(t, trustStoreConfigMap.Data["d"], "c") } -func TestSyncAdditionalCACertsConfigMapToCluster(t *testing.T) { +func SyncAdditionalCACertsConfigMapToCluster(t *testing.T) { cert1 := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "cert1", @@ -103,12 +103,12 @@ func TestSyncAdditionalCACertsConfigMapToCluster(t *testing.T) { ctx := test.GetDeployContext(nil, []runtime.Object{cert1}) certificates := NewCertificatesReconciler() - done, err := certificates.syncAdditionalCACertsConfigMapToCluster(ctx) + done, err := certificates.syncCheCABundleCerts(ctx) assert.Nil(t, err) assert.True(t, done) cacertMerged := &corev1.ConfigMap{} - err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: CheAllCACertsConfigMapName, Namespace: "eclipse-che"}, cacertMerged) + err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: CheMergedCABundleCertsCMName, Namespace: "eclipse-che"}, cacertMerged) assert.Nil(t, err) assert.Equal(t, cacertMerged.ObjectMeta.Annotations["che.eclipse.org/included-configmaps"], "cert1-1") @@ -117,23 +117,23 @@ func TestSyncAdditionalCACertsConfigMapToCluster(t *testing.T) { assert.Nil(t, err) // check ca-cert-merged - done, err = certificates.syncAdditionalCACertsConfigMapToCluster(ctx) + done, err = certificates.syncCheCABundleCerts(ctx) assert.Nil(t, err) assert.False(t, done) - done, err = certificates.syncAdditionalCACertsConfigMapToCluster(ctx) + done, err = certificates.syncCheCABundleCerts(ctx) assert.Nil(t, err) assert.True(t, done) - err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: CheAllCACertsConfigMapName, Namespace: "eclipse-che"}, cacertMerged) + err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: CheMergedCABundleCertsCMName, Namespace: "eclipse-che"}, cacertMerged) assert.Nil(t, err) assert.Equal(t, cacertMerged.ObjectMeta.Annotations["che.eclipse.org/included-configmaps"], "cert1-1.cert2-1") } -func TestSyncKubernetesRootCertificates(t *testing.T) { +func SyncKubernetesRootCertificates(t *testing.T) { caCertsMerged := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: CheAllCACertsConfigMapName, + Name: CheMergedCABundleCertsCMName, Namespace: "eclipse-che", Labels: map[string]string{ "app": "che", @@ -149,7 +149,7 @@ func TestSyncKubernetesRootCertificates(t *testing.T) { kubeRootCert := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: KubernetesRootCertificateConfigMapName, + Name: kubernetesRootCACertsCMName, Namespace: "eclipse-che", }, Data: map[string]string{ @@ -171,7 +171,7 @@ func TestSyncKubernetesRootCertificates(t *testing.T) { assert.True(t, done) } -func TestSyncGitSelfSignedCertificate(t *testing.T) { +func SyncGitSelfSignedCertificate(t *testing.T) { cert := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "cert", @@ -210,12 +210,12 @@ func TestSyncGitSelfSignedCertificate(t *testing.T) { ctx := test.GetDeployContext(checluster, []runtime.Object{cert, gitTrustedCertsConfig}) certificates := NewCertificatesReconciler() - done, err := certificates.syncAdditionalCACertsConfigMapToCluster(ctx) + done, err := certificates.syncCheCABundleCerts(ctx) assert.Nil(t, err) assert.True(t, done) cacertMerged := &corev1.ConfigMap{} - err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: CheAllCACertsConfigMapName, Namespace: "eclipse-che"}, cacertMerged) + err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: CheMergedCABundleCertsCMName, Namespace: "eclipse-che"}, cacertMerged) assert.Nil(t, err) assert.Equal(t, "cert-1.git-selfsigned-certificate-1", cacertMerged.ObjectMeta.Annotations["che.eclipse.org/included-configmaps"]) assert.Equal(t, "git-certificate", cacertMerged.Data["git-selfsigned-certificate.ca.crt"]) diff --git a/pkg/deploy/tls/tls_utils.go b/pkg/deploy/tls/tls_utils.go index b14996ab0..2dd9b7f0f 100644 --- a/pkg/deploy/tls/tls_utils.go +++ b/pkg/deploy/tls/tls_utils.go @@ -505,12 +505,12 @@ func deleteJob(ctx *chetypes.DeployContext, job *batchv1.Job) { } } -// GetCACertsConfigMaps returns list of config maps with additional CA certificates that should be trusted by Che +// GetCheCABundles returns list of config maps with additional CA certificates that should be trusted by Che // The selection is based on the specific label -func GetCACertsConfigMaps(client k8sclient.Client, namespace string) ([]corev1.ConfigMap, error) { +func GetCheCABundles(client k8sclient.Client, namespace string) ([]corev1.ConfigMap, error) { CACertsConfigMapList := &corev1.ConfigMapList{} - caBundleLabelSelectorRequirement, _ := labels.NewRequirement(constants.KubernetesComponentLabelKey, selection.Equals, []string{CheCACertsConfigMapLabelValue}) + caBundleLabelSelectorRequirement, _ := labels.NewRequirement(constants.KubernetesComponentLabelKey, selection.Equals, []string{constants.CheCABundle}) cheComponetLabelSelectorRequirement, _ := labels.NewRequirement(constants.KubernetesPartOfLabelKey, selection.Equals, []string{constants.CheEclipseOrg}) listOptions := &k8sclient.ListOptions{ LabelSelector: labels.NewSelector().Add(*cheComponetLabelSelectorRequirement).Add(*caBundleLabelSelectorRequirement), @@ -526,7 +526,7 @@ func GetCACertsConfigMaps(client k8sclient.Client, namespace string) ([]corev1.C // GetAdditionalCACertsConfigMapVersion returns revision of merged additional CA certs config map func GetAdditionalCACertsConfigMapVersion(ctx *chetypes.DeployContext) string { trustStoreConfigMap := &corev1.ConfigMap{} - exists, _ := deploy.GetNamespacedObject(ctx, CheAllCACertsConfigMapName, trustStoreConfigMap) + exists, _ := deploy.GetNamespacedObject(ctx, CheMergedCABundleCertsCMName, trustStoreConfigMap) if exists { return trustStoreConfigMap.ResourceVersion } @@ -545,6 +545,7 @@ func CreateTLSSecret(ctx *chetypes.DeployContext, name string) (err error) { return err } + // TODO _, err = deploy.SyncSecretToCluster(ctx, name, ctx.CheCluster.Namespace, map[string][]byte{"ca.crt": crtBytes}) if err != nil { return err From a65fe17d63eea2212ba1b6e512decded018c05a7 Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Wed, 16 Oct 2024 09:24:18 +0200 Subject: [PATCH 2/6] fixup Signed-off-by: Anatolii Bazko --- api/v2/checluster_types.go | 4 +--- pkg/common/constants/constants.go | 1 - pkg/deploy/tls/certificates.go | 25 ++++++------------------- 3 files changed, 7 insertions(+), 23 deletions(-) diff --git a/api/v2/checluster_types.go b/api/v2/checluster_types.go index eac17c2d3..fee7cee88 100644 --- a/api/v2/checluster_types.go +++ b/api/v2/checluster_types.go @@ -455,12 +455,10 @@ type DashboardHeaderMessage struct { type TrustedCerts struct { // By default, Operator creates and mounts the `ca-certs-merged` ConfigMap containing the CA certificates bundle // into users` workspaces. This option allows to disable this behavior. - // +optional - DisableMountingCaBundleIntoDevWorkspace *bool `json:"disableMountingCaBundleIntoDevWorkspace,omitempty"` // The CA bundle mount paths in the workspace pods. // If not specified, the default is `/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem`. // +optional - CaBundleMountPaths []string `json:"caBundleMountPaths,omitempty"` + DisableMountingCaBundleIntoDevWorkspace *bool `json:"disableMountingCaBundleIntoDevWorkspace,omitempty"` // The ConfigMap contains certificates to propagate to the Che components and to provide a particular configuration for Git. // See the following page: https://www.eclipse.org/che/docs/stable/administration-guide/deploying-che-with-support-for-git-repositories-with-self-signed-certificates/ // The ConfigMap must have a `app.kubernetes.io/part-of=che.eclipse.org` label. diff --git a/pkg/common/constants/constants.go b/pkg/common/constants/constants.go index 84ddcceac..29ade1887 100644 --- a/pkg/common/constants/constants.go +++ b/pkg/common/constants/constants.go @@ -138,5 +138,4 @@ var ( "app": "che", "component": "che-gateway-config", } - DefaultCaBundleMountPaths = []string{"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"} ) diff --git a/pkg/deploy/tls/certificates.go b/pkg/deploy/tls/certificates.go index 1f0da995f..52b226ae3 100644 --- a/pkg/deploy/tls/certificates.go +++ b/pkg/deploy/tls/certificates.go @@ -16,7 +16,6 @@ import ( "errors" "fmt" "os" - "path/filepath" "reflect" "strings" @@ -328,26 +327,14 @@ func (c *CertificatesReconciler) syncCheCABundleCerts(ctx *chetypes.DeployContex (ctx.CheCluster.Spec.DevEnvironments.TrustedCerts.DisableMountingCaBundleIntoDevWorkspace != nil && !*ctx.CheCluster.Spec.DevEnvironments.TrustedCerts.DisableMountingCaBundleIntoDevWorkspace) { - mountPaths := constants.DefaultCaBundleMountPaths - if ctx.CheCluster.Spec.DevEnvironments.TrustedCerts != nil && - len(ctx.CheCluster.Spec.DevEnvironments.TrustedCerts.CaBundleMountPaths) > 0 { - mountPaths = ctx.CheCluster.Spec.DevEnvironments.TrustedCerts.CaBundleMountPaths - } - - // Mount CA bundle into dev workspaces - // TODO rework to create a dedicated CM for every mount path - for _, mountPath := range mountPaths { - mountDir, mountFileName := filepath.Split(mountPath) + // Mark ConfigMap as workspace config (will be mounted in all workspace pods) + mergedCABundlesCM.ObjectMeta.Labels[constants.KubernetesComponentLabelKey] = constants.WorkspacesConfig - // Mark ConfigMap as workspace config (will be mounted in all workspace pods) - mergedCABundlesCM.ObjectMeta.Labels[constants.KubernetesComponentLabelKey] = constants.WorkspacesConfig + // Add annotations to mount certificates in desired path + mergedCABundlesCM.ObjectMeta.Annotations["controller.devfile.io/mount-as"] = "subpath" + mergedCABundlesCM.ObjectMeta.Annotations["controller.devfile.io/mount-path"] = kubernetesCABundleCertsDir - // Add annotations to mount certificates in desired path - mergedCABundlesCM.ObjectMeta.Annotations["controller.devfile.io/mount-as"] = "subpath" - mergedCABundlesCM.ObjectMeta.Annotations["controller.devfile.io/mount-path"] = mountDir - - mergedCABundlesCM.Data = map[string]string{mountFileName: cheCABundlesExpectedContent} - } + mergedCABundlesCM.Data = map[string]string{kubernetesCABundleCertsFile: cheCABundlesExpectedContent} } return deploy.Sync(ctx, mergedCABundlesCM, deploy.ConfigMapDiffOpts) From d050415e888dcf11e1cdaaf251d16266f00a2c95 Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Wed, 16 Oct 2024 12:26:34 +0200 Subject: [PATCH 3/6] Fixup Signed-off-by: Anatolii Bazko --- api/v2/checluster_types.go | 21 +- api/v2/zz_generated.deepcopy.go | 9 +- .../org.eclipse.che_checlusters.yaml | 18 +- .../bases/org.eclipse.che_checlusters.yaml | 18 +- deploy/deployment/kubernetes/combined.yaml | 18 +- ....eclipse.che.CustomResourceDefinition.yaml | 18 +- deploy/deployment/openshift/combined.yaml | 18 +- ....eclipse.che.CustomResourceDefinition.yaml | 18 +- ....eclipse.che.CustomResourceDefinition.yaml | 18 +- pkg/deploy/tls/certificates.go | 72 +++-- pkg/deploy/tls/certificates_test.go | 256 +++++++++--------- 11 files changed, 254 insertions(+), 230 deletions(-) diff --git a/api/v2/checluster_types.go b/api/v2/checluster_types.go index fee7cee88..52361f8b2 100644 --- a/api/v2/checluster_types.go +++ b/api/v2/checluster_types.go @@ -453,12 +453,15 @@ type DashboardHeaderMessage struct { } type TrustedCerts struct { - // By default, Operator creates and mounts the `ca-certs-merged` ConfigMap containing the CA certificates bundle - // into users` workspaces. This option allows to disable this behavior. - // The CA bundle mount paths in the workspace pods. - // If not specified, the default is `/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem`. - // +optional - DisableMountingCaBundleIntoDevWorkspace *bool `json:"disableMountingCaBundleIntoDevWorkspace,omitempty"` + // By default, the Operator creates and mounts the 'ca-certs-merged' ConfigMap + // containing the CA certificate bundle in users' workspaces at two locations: + // '/public-certs' and '/etc/pki/ca-trust/extracted/pem'. + // The '/etc/pki/ca-trust/extracted/pem' directory is where the system stores extracted CA certificates + // for trusted certificate authorities on Red Hat (e.g., CentOS, Fedora). + // This option disables mounting the CA bundle to the '/etc/pki/ca-trust/extracted/pem' directory + // while still mounting it to '/public-certs'. + // +optional + DisableWorkspaceCaBundleMount *bool `json:"disableWorkspaceCaBundleMount,omitempty"` // The ConfigMap contains certificates to propagate to the Che components and to provide a particular configuration for Git. // See the following page: https://www.eclipse.org/che/docs/stable/administration-guide/deploying-che-with-support-for-git-repositories-with-self-signed-certificates/ // The ConfigMap must have a `app.kubernetes.io/part-of=che.eclipse.org` label. @@ -1055,3 +1058,9 @@ func (c *CheCluster) IsInternalPluginRegistryDisabled() bool { func (c *CheCluster) IsCheBeingInstalled() bool { return c.Status.CheVersion == "" } + +func (c *CheCluster) IsDisableWorkspaceCaBundleMount() bool { + return c.Spec.DevEnvironments.TrustedCerts != nil && + c.Spec.DevEnvironments.TrustedCerts.DisableWorkspaceCaBundleMount != nil && + *c.Spec.DevEnvironments.TrustedCerts.DisableWorkspaceCaBundleMount +} diff --git a/api/v2/zz_generated.deepcopy.go b/api/v2/zz_generated.deepcopy.go index b97807638..5c0f068f9 100644 --- a/api/v2/zz_generated.deepcopy.go +++ b/api/v2/zz_generated.deepcopy.go @@ -1101,16 +1101,11 @@ func (in *Traefik) DeepCopy() *Traefik { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TrustedCerts) DeepCopyInto(out *TrustedCerts) { *out = *in - if in.DisableMountingCaBundleIntoDevWorkspace != nil { - in, out := &in.DisableMountingCaBundleIntoDevWorkspace, &out.DisableMountingCaBundleIntoDevWorkspace + if in.DisableWorkspaceCaBundleMount != nil { + in, out := &in.DisableWorkspaceCaBundleMount, &out.DisableWorkspaceCaBundleMount *out = new(bool) **out = **in } - if in.CaBundleMountPaths != nil { - in, out := &in.CaBundleMountPaths, &out.CaBundleMountPaths - *out = make([]string, len(*in)) - copy(*out, *in) - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrustedCerts. diff --git a/bundle/next/eclipse-che/manifests/org.eclipse.che_checlusters.yaml b/bundle/next/eclipse-che/manifests/org.eclipse.che_checlusters.yaml index cf666dab1..14bf45ad4 100644 --- a/bundle/next/eclipse-che/manifests/org.eclipse.che_checlusters.yaml +++ b/bundle/next/eclipse-che/manifests/org.eclipse.che_checlusters.yaml @@ -8003,17 +8003,15 @@ spec: trustedCerts: description: Trusted certificate settings. properties: - caBundleMountPaths: + disableWorkspaceCaBundleMount: description: |- - The CA bundle mount paths in the workspace pods. - If not specified, the default is `/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem`. - items: - type: string - type: array - disableMountingCaBundleIntoDevWorkspace: - description: |- - By default, Operator creates and mounts the `ca-certs-merged` ConfigMap containing the CA certificates into - a users` workspaces. This option allows to disable this behavior. + By default, the Operator creates and mounts the 'ca-certs-merged' ConfigMap + containing the CA certificate bundle in users' workspaces at two locations: + '/public-certs' and '/etc/pki/ca-trust/extracted/pem'. + The '/etc/pki/ca-trust/extracted/pem' directory is where the system stores extracted CA certificates + for trusted certificate authorities on Red Hat (e.g., CentOS, Fedora). + This option disables mounting the CA bundle to the '/etc/pki/ca-trust/extracted/pem' directory + while still mounting it to '/public-certs'. type: boolean gitTrustedCertsConfigMapName: description: |- diff --git a/config/crd/bases/org.eclipse.che_checlusters.yaml b/config/crd/bases/org.eclipse.che_checlusters.yaml index 64924956a..294676c37 100644 --- a/config/crd/bases/org.eclipse.che_checlusters.yaml +++ b/config/crd/bases/org.eclipse.che_checlusters.yaml @@ -7954,17 +7954,15 @@ spec: trustedCerts: description: Trusted certificate settings. properties: - caBundleMountPaths: + disableWorkspaceCaBundleMount: description: |- - The CA bundle mount paths in the workspace pods. - If not specified, the default is `/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem`. - items: - type: string - type: array - disableMountingCaBundleIntoDevWorkspace: - description: |- - By default, Operator creates and mounts the `ca-certs-merged` ConfigMap containing the CA certificates into - a users` workspaces. This option allows to disable this behavior. + By default, the Operator creates and mounts the 'ca-certs-merged' ConfigMap + containing the CA certificate bundle in users' workspaces at two locations: + '/public-certs' and '/etc/pki/ca-trust/extracted/pem'. + The '/etc/pki/ca-trust/extracted/pem' directory is where the system stores extracted CA certificates + for trusted certificate authorities on Red Hat (e.g., CentOS, Fedora). + This option disables mounting the CA bundle to the '/etc/pki/ca-trust/extracted/pem' directory + while still mounting it to '/public-certs'. type: boolean gitTrustedCertsConfigMapName: description: |- diff --git a/deploy/deployment/kubernetes/combined.yaml b/deploy/deployment/kubernetes/combined.yaml index 1dc4c0577..3f8dd4c4f 100644 --- a/deploy/deployment/kubernetes/combined.yaml +++ b/deploy/deployment/kubernetes/combined.yaml @@ -7975,17 +7975,15 @@ spec: trustedCerts: description: Trusted certificate settings. properties: - caBundleMountPaths: + disableWorkspaceCaBundleMount: description: |- - The CA bundle mount paths in the workspace pods. - If not specified, the default is `/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem`. - items: - type: string - type: array - disableMountingCaBundleIntoDevWorkspace: - description: |- - By default, Operator creates and mounts the `ca-certs-merged` ConfigMap containing the CA certificates into - a users` workspaces. This option allows to disable this behavior. + By default, the Operator creates and mounts the 'ca-certs-merged' ConfigMap + containing the CA certificate bundle in users' workspaces at two locations: + '/public-certs' and '/etc/pki/ca-trust/extracted/pem'. + The '/etc/pki/ca-trust/extracted/pem' directory is where the system stores extracted CA certificates + for trusted certificate authorities on Red Hat (e.g., CentOS, Fedora). + This option disables mounting the CA bundle to the '/etc/pki/ca-trust/extracted/pem' directory + while still mounting it to '/public-certs'. type: boolean gitTrustedCertsConfigMapName: description: |- diff --git a/deploy/deployment/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml b/deploy/deployment/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml index 843998e53..7c9e052c7 100644 --- a/deploy/deployment/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml +++ b/deploy/deployment/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml @@ -7970,17 +7970,15 @@ spec: trustedCerts: description: Trusted certificate settings. properties: - caBundleMountPaths: + disableWorkspaceCaBundleMount: description: |- - The CA bundle mount paths in the workspace pods. - If not specified, the default is `/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem`. - items: - type: string - type: array - disableMountingCaBundleIntoDevWorkspace: - description: |- - By default, Operator creates and mounts the `ca-certs-merged` ConfigMap containing the CA certificates into - a users` workspaces. This option allows to disable this behavior. + By default, the Operator creates and mounts the 'ca-certs-merged' ConfigMap + containing the CA certificate bundle in users' workspaces at two locations: + '/public-certs' and '/etc/pki/ca-trust/extracted/pem'. + The '/etc/pki/ca-trust/extracted/pem' directory is where the system stores extracted CA certificates + for trusted certificate authorities on Red Hat (e.g., CentOS, Fedora). + This option disables mounting the CA bundle to the '/etc/pki/ca-trust/extracted/pem' directory + while still mounting it to '/public-certs'. type: boolean gitTrustedCertsConfigMapName: description: |- diff --git a/deploy/deployment/openshift/combined.yaml b/deploy/deployment/openshift/combined.yaml index 211f2f782..b6445834e 100644 --- a/deploy/deployment/openshift/combined.yaml +++ b/deploy/deployment/openshift/combined.yaml @@ -7975,17 +7975,15 @@ spec: trustedCerts: description: Trusted certificate settings. properties: - caBundleMountPaths: + disableWorkspaceCaBundleMount: description: |- - The CA bundle mount paths in the workspace pods. - If not specified, the default is `/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem`. - items: - type: string - type: array - disableMountingCaBundleIntoDevWorkspace: - description: |- - By default, Operator creates and mounts the `ca-certs-merged` ConfigMap containing the CA certificates into - a users` workspaces. This option allows to disable this behavior. + By default, the Operator creates and mounts the 'ca-certs-merged' ConfigMap + containing the CA certificate bundle in users' workspaces at two locations: + '/public-certs' and '/etc/pki/ca-trust/extracted/pem'. + The '/etc/pki/ca-trust/extracted/pem' directory is where the system stores extracted CA certificates + for trusted certificate authorities on Red Hat (e.g., CentOS, Fedora). + This option disables mounting the CA bundle to the '/etc/pki/ca-trust/extracted/pem' directory + while still mounting it to '/public-certs'. type: boolean gitTrustedCertsConfigMapName: description: |- diff --git a/deploy/deployment/openshift/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml b/deploy/deployment/openshift/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml index 4209b9212..db94053b8 100644 --- a/deploy/deployment/openshift/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml +++ b/deploy/deployment/openshift/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml @@ -7970,17 +7970,15 @@ spec: trustedCerts: description: Trusted certificate settings. properties: - caBundleMountPaths: + disableWorkspaceCaBundleMount: description: |- - The CA bundle mount paths in the workspace pods. - If not specified, the default is `/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem`. - items: - type: string - type: array - disableMountingCaBundleIntoDevWorkspace: - description: |- - By default, Operator creates and mounts the `ca-certs-merged` ConfigMap containing the CA certificates into - a users` workspaces. This option allows to disable this behavior. + By default, the Operator creates and mounts the 'ca-certs-merged' ConfigMap + containing the CA certificate bundle in users' workspaces at two locations: + '/public-certs' and '/etc/pki/ca-trust/extracted/pem'. + The '/etc/pki/ca-trust/extracted/pem' directory is where the system stores extracted CA certificates + for trusted certificate authorities on Red Hat (e.g., CentOS, Fedora). + This option disables mounting the CA bundle to the '/etc/pki/ca-trust/extracted/pem' directory + while still mounting it to '/public-certs'. type: boolean gitTrustedCertsConfigMapName: description: |- diff --git a/helmcharts/next/crds/checlusters.org.eclipse.che.CustomResourceDefinition.yaml b/helmcharts/next/crds/checlusters.org.eclipse.che.CustomResourceDefinition.yaml index 843998e53..7c9e052c7 100644 --- a/helmcharts/next/crds/checlusters.org.eclipse.che.CustomResourceDefinition.yaml +++ b/helmcharts/next/crds/checlusters.org.eclipse.che.CustomResourceDefinition.yaml @@ -7970,17 +7970,15 @@ spec: trustedCerts: description: Trusted certificate settings. properties: - caBundleMountPaths: + disableWorkspaceCaBundleMount: description: |- - The CA bundle mount paths in the workspace pods. - If not specified, the default is `/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem`. - items: - type: string - type: array - disableMountingCaBundleIntoDevWorkspace: - description: |- - By default, Operator creates and mounts the `ca-certs-merged` ConfigMap containing the CA certificates into - a users` workspaces. This option allows to disable this behavior. + By default, the Operator creates and mounts the 'ca-certs-merged' ConfigMap + containing the CA certificate bundle in users' workspaces at two locations: + '/public-certs' and '/etc/pki/ca-trust/extracted/pem'. + The '/etc/pki/ca-trust/extracted/pem' directory is where the system stores extracted CA certificates + for trusted certificate authorities on Red Hat (e.g., CentOS, Fedora). + This option disables mounting the CA bundle to the '/etc/pki/ca-trust/extracted/pem' directory + while still mounting it to '/public-certs'. type: boolean gitTrustedCertsConfigMapName: description: |- diff --git a/pkg/deploy/tls/certificates.go b/pkg/deploy/tls/certificates.go index 52b226ae3..d85c4e60a 100644 --- a/pkg/deploy/tls/certificates.go +++ b/pkg/deploy/tls/certificates.go @@ -53,10 +53,13 @@ const ( type CertificatesReconciler struct { deploy.Reconcilable + readKubernetesCaBundle func() ([]byte, error) } func NewCertificatesReconciler() *CertificatesReconciler { - return &CertificatesReconciler{} + return &CertificatesReconciler{ + readKubernetesCaBundle: readKubernetesCaBundle, + } } func (c *CertificatesReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) { @@ -96,14 +99,34 @@ func (c *CertificatesReconciler) Finalize(ctx *chetypes.DeployContext) bool { } func (c *CertificatesReconciler) syncOpenShiftCABundleCertificates(ctx *chetypes.DeployContext) (bool, error) { - openShiftCaBundleCM := deploy.GetConfigMapSpec( - ctx, - constants.DefaultCaBundleCertsCMName, - map[string]string{}, - defaults.GetCheFlavor(), - ) + openShiftCaBundleCMKey := types.NamespacedName{ + Namespace: ctx.CheCluster.Namespace, + Name: constants.DefaultCaBundleCertsCMName, + } + + openShiftCaBundleCM := &corev1.ConfigMap{} + exists, err := deploy.Get(ctx, openShiftCaBundleCMKey, openShiftCaBundleCM) + if err != nil { + return false, err + } + + if !exists { + openShiftCaBundleCM = &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: constants.DefaultCaBundleCertsCMName, + Namespace: ctx.CheCluster.Namespace, + Labels: deploy.GetLabels(constants.CheCABundle), + }, + } + } + openShiftCaBundleCM.ObjectMeta.Labels[injectTrustedCaBundle] = "true" + openShiftCaBundleCM.ObjectMeta.Labels[constants.KubernetesPartOfLabelKey] = constants.CheEclipseOrg openShiftCaBundleCM.ObjectMeta.Labels[constants.KubernetesComponentLabelKey] = constants.CheCABundle + openShiftCaBundleCM.TypeMeta = metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + } return deploy.Sync( ctx, @@ -120,19 +143,15 @@ func (c *CertificatesReconciler) syncOpenShiftCABundleCertificates(ctx *chetypes } func (c *CertificatesReconciler) syncKubernetesCABundleCertificates(ctx *chetypes.DeployContext) (bool, error) { - data, err := os.ReadFile(kubernetesCABundleCertsDir + string(os.PathSeparator) + kubernetesCABundleCertsFile) + data, err := c.readKubernetesCaBundle() if err != nil { - if errors.Is(err, os.ErrNotExist) { - return true, nil - } - return false, err } kubernetesCaBundleCM := deploy.GetConfigMapSpec( ctx, constants.DefaultCaBundleCertsCMName, - map[string]string{"ca.crt": string(data)}, + map[string]string{kubernetesCABundleCertsFile: string(data)}, constants.CheCABundle, ) @@ -163,6 +182,10 @@ func (c *CertificatesReconciler) syncGitTrustedCertificates(ctx *chetypes.Deploy APIVersion: "v1", } + if gitTrustedCertsCM.GetLabels() == nil { + gitTrustedCertsCM.Labels = map[string]string{} + } + // Add necessary labels to the ConfigMap gitTrustedCertsCM.Labels[constants.KubernetesPartOfLabelKey] = constants.CheEclipseOrg gitTrustedCertsCM.Labels[constants.KubernetesComponentLabelKey] = constants.CheCABundle @@ -323,19 +346,28 @@ func (c *CertificatesReconciler) syncCheCABundleCerts(ctx *chetypes.DeployContex // Add annotations with included config maps revisions mergedCABundlesCM.ObjectMeta.Annotations[cheCABundleIncludedCMRevisions] = cheCABundlesExpectedRevisionsAsString - if ctx.CheCluster.Spec.DevEnvironments.TrustedCerts == nil || - (ctx.CheCluster.Spec.DevEnvironments.TrustedCerts.DisableMountingCaBundleIntoDevWorkspace != nil && - !*ctx.CheCluster.Spec.DevEnvironments.TrustedCerts.DisableMountingCaBundleIntoDevWorkspace) { - + if !ctx.CheCluster.IsDisableWorkspaceCaBundleMount() { // Mark ConfigMap as workspace config (will be mounted in all workspace pods) mergedCABundlesCM.ObjectMeta.Labels[constants.KubernetesComponentLabelKey] = constants.WorkspacesConfig - // Add annotations to mount certificates in desired path + // Set desired mount location + mergedCABundlesCM.Data = map[string]string{kubernetesCABundleCertsFile: cheCABundlesExpectedContent} mergedCABundlesCM.ObjectMeta.Annotations["controller.devfile.io/mount-as"] = "subpath" mergedCABundlesCM.ObjectMeta.Annotations["controller.devfile.io/mount-path"] = kubernetesCABundleCertsDir - - mergedCABundlesCM.Data = map[string]string{kubernetesCABundleCertsFile: cheCABundlesExpectedContent} } return deploy.Sync(ctx, mergedCABundlesCM, deploy.ConfigMapDiffOpts) } + +func readKubernetesCaBundle() ([]byte, error) { + data, err := os.ReadFile(kubernetesCABundleCertsDir + string(os.PathSeparator) + kubernetesCABundleCertsFile) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil, nil + } + + return nil, err + } + + return data, nil +} diff --git a/pkg/deploy/tls/certificates_test.go b/pkg/deploy/tls/certificates_test.go index 36227adbf..90f592814 100644 --- a/pkg/deploy/tls/certificates_test.go +++ b/pkg/deploy/tls/certificates_test.go @@ -15,6 +15,8 @@ package tls import ( "context" + "github.com/eclipse-che/che-operator/pkg/common/constants" + "testing" chev2 "github.com/eclipse-che/che-operator/api/v2" @@ -26,28 +28,25 @@ import ( "k8s.io/apimachinery/pkg/types" ) -func TestSyncDefaultTrustStoreConfigMapToCluster(t *testing.T) { - checluster := &chev2.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", - Name: "eclipse-che", - }, - } - ctx := test.GetDeployContext(checluster, []runtime.Object{}) +func TestSyncOpenShiftCABundleCertificates(t *testing.T) { + ctx := test.GetDeployContext(nil, []runtime.Object{}) certificates := NewCertificatesReconciler() + done, err := certificates.syncOpenShiftCABundleCertificates(ctx) assert.Nil(t, err) assert.True(t, done) - trustStoreConfigMap := &corev1.ConfigMap{} - err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "ca-certs", Namespace: "eclipse-che"}, trustStoreConfigMap) + cm := &corev1.ConfigMap{} + err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "ca-certs", Namespace: "eclipse-che"}, cm) assert.Nil(t, err) - assert.Equal(t, trustStoreConfigMap.ObjectMeta.Labels[injectTrustedCaBundle], "true") + assert.Equal(t, "true", cm.ObjectMeta.Labels[injectTrustedCaBundle]) + assert.Equal(t, constants.CheEclipseOrg, cm.ObjectMeta.Labels[constants.KubernetesPartOfLabelKey]) + assert.Equal(t, constants.CheCABundle, cm.ObjectMeta.Labels[constants.KubernetesComponentLabelKey]) } -func SyncExistedTrustStoreConfigMapToCluster(t *testing.T) { - trustStoreConfigMap := &corev1.ConfigMap{ +func TestSyncExistedOpenShiftCABundleCertificates(t *testing.T) { + openShiftCABundleCM := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "ca-certs", Namespace: "eclipse-che", @@ -55,169 +54,172 @@ func SyncExistedTrustStoreConfigMapToCluster(t *testing.T) { }, Data: map[string]string{"d": "c"}, } - checluster := &chev2.CheCluster{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "eclipse-che", - Name: "eclipse-che", + ctx := test.GetDeployContext(nil, []runtime.Object{openShiftCABundleCM}) + + certificates := NewCertificatesReconciler() + _, err := certificates.syncOpenShiftCABundleCertificates(ctx) + assert.NoError(t, err) + + cm := &corev1.ConfigMap{} + err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "ca-certs", Namespace: "eclipse-che"}, cm) + assert.NoError(t, err) + assert.Equal(t, "true", cm.ObjectMeta.Labels[injectTrustedCaBundle]) + assert.Equal(t, constants.CheEclipseOrg, cm.ObjectMeta.Labels[constants.KubernetesPartOfLabelKey]) + assert.Equal(t, constants.CheCABundle, cm.ObjectMeta.Labels[constants.KubernetesComponentLabelKey]) + assert.Equal(t, "b", cm.ObjectMeta.Labels["a"]) + assert.Equal(t, "c", cm.Data["d"]) +} + +func TestSyncKubernetesCABundleCertificates(t *testing.T) { + ctx := test.GetDeployContext(nil, []runtime.Object{}) + + certificates := &CertificatesReconciler{ + readKubernetesCaBundle: func() ([]byte, error) { + return []byte("kubernetes-ca-bundle"), nil }, } - ctx := test.GetDeployContext(checluster, []runtime.Object{trustStoreConfigMap}) - certificates := NewCertificatesReconciler() - done, err := certificates.syncOpenShiftCABundleCertificates(ctx) - assert.Nil(t, err) + done, err := certificates.syncKubernetesCABundleCertificates(ctx) + assert.NoError(t, err) assert.True(t, done) - err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "ca-certs", Namespace: "eclipse-che"}, trustStoreConfigMap) - assert.Nil(t, err) - assert.Equal(t, trustStoreConfigMap.ObjectMeta.Labels[injectTrustedCaBundle], "true") - assert.Equal(t, trustStoreConfigMap.ObjectMeta.Labels["a"], "b") - assert.Equal(t, trustStoreConfigMap.Data["d"], "c") + cm := &corev1.ConfigMap{} + err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "ca-certs", Namespace: "eclipse-che"}, cm) + assert.NoError(t, err) + assert.Equal(t, cm.ObjectMeta.Labels[constants.KubernetesPartOfLabelKey], constants.CheEclipseOrg) + assert.Equal(t, cm.ObjectMeta.Labels[constants.KubernetesComponentLabelKey], constants.CheCABundle) } -func SyncAdditionalCACertsConfigMapToCluster(t *testing.T) { - cert1 := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "cert1", - Namespace: "eclipse-che", - ResourceVersion: "1", - Labels: map[string]string{ - "app.kubernetes.io/component": "ca-bundle", - "app.kubernetes.io/part-of": "che.eclipse.org"}, - }, - Data: map[string]string{"a1": "b1"}, - } - cert2 := &corev1.ConfigMap{ +func TestSyncKubernetesRootCertificates(t *testing.T) { + kubeRootCert := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: "cert2", + Name: kubernetesRootCACertsCMName, Namespace: "eclipse-che", - // Go client set up resource version 1 itself on object creation. - // ResourceVersion: "1", - Labels: map[string]string{ - "app.kubernetes.io/component": "ca-bundle", - "app.kubernetes.io/part-of": "che.eclipse.org"}, }, - Data: map[string]string{"a2": "b2"}, + Data: map[string]string{ + "ca.crt": "root-cert", + }, } - - ctx := test.GetDeployContext(nil, []runtime.Object{cert1}) + ctx := test.GetDeployContext(nil, []runtime.Object{kubeRootCert}) certificates := NewCertificatesReconciler() - done, err := certificates.syncCheCABundleCerts(ctx) - assert.Nil(t, err) - assert.True(t, done) - - cacertMerged := &corev1.ConfigMap{} - err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: CheMergedCABundleCertsCMName, Namespace: "eclipse-che"}, cacertMerged) - assert.Nil(t, err) - assert.Equal(t, cacertMerged.ObjectMeta.Annotations["che.eclipse.org/included-configmaps"], "cert1-1") - - // let's create another configmap - err = ctx.ClusterAPI.Client.Create(context.TODO(), cert2) - assert.Nil(t, err) - - // check ca-cert-merged - done, err = certificates.syncCheCABundleCerts(ctx) - assert.Nil(t, err) - assert.False(t, done) - done, err = certificates.syncCheCABundleCerts(ctx) - assert.Nil(t, err) - assert.True(t, done) + _, err := certificates.syncKubernetesRootCertificates(ctx) + assert.NoError(t, err) - err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: CheMergedCABundleCertsCMName, Namespace: "eclipse-che"}, cacertMerged) - assert.Nil(t, err) - assert.Equal(t, cacertMerged.ObjectMeta.Annotations["che.eclipse.org/included-configmaps"], "cert1-1.cert2-1") + cm := &corev1.ConfigMap{} + err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: kubernetesRootCACertsCMName, Namespace: "eclipse-che"}, cm) + assert.NoError(t, err) + assert.Equal(t, cm.ObjectMeta.Labels[constants.KubernetesPartOfLabelKey], constants.CheEclipseOrg) + assert.Equal(t, cm.ObjectMeta.Labels[constants.KubernetesComponentLabelKey], constants.CheCABundle) } -func SyncKubernetesRootCertificates(t *testing.T) { - caCertsMerged := &corev1.ConfigMap{ +func TestSyncGitTrustedCertificates(t *testing.T) { + cheCluster := &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ - Name: CheMergedCABundleCertsCMName, + Name: "eclipse-che", Namespace: "eclipse-che", - Labels: map[string]string{ - "app": "che", - "app.kubernetes.io/component": "che", - "app.kubernetes.io/instance": "che", - "app.kubernetes.io/managed-by": "che-operator", - "app.kubernetes.io/name": "che", - "app.kubernetes.io/part-of": "che.eclipse.org", - "component": "che", + }, + Spec: chev2.CheClusterSpec{ + DevEnvironments: chev2.CheClusterDevEnvironments{ + TrustedCerts: &chev2.TrustedCerts{ + GitTrustedCertsConfigMapName: "git-trusted-certs", + }, }, }, } - - kubeRootCert := &corev1.ConfigMap{ + gitCerts := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: kubernetesRootCACertsCMName, + Name: "git-trusted-certs", Namespace: "eclipse-che", }, Data: map[string]string{ - "ca.crt": "root-cert", + "ca.crt": "git-cert", }, } - - ctx := test.GetDeployContext(nil, []runtime.Object{kubeRootCert, caCertsMerged}) + ctx := test.GetDeployContext(cheCluster, []runtime.Object{gitCerts}) certificates := NewCertificatesReconciler() - _, _, err := certificates.Reconcile(ctx) - assert.Nil(t, err) - _, _, err = certificates.Reconcile(ctx) - assert.Nil(t, err) + _, err := certificates.syncGitTrustedCertificates(ctx) + assert.NoError(t, err) - _, done, err := certificates.Reconcile(ctx) - assert.Nil(t, err) - assert.True(t, done) + cm := &corev1.ConfigMap{} + err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "git-trusted-certs", Namespace: "eclipse-che"}, cm) + assert.NoError(t, err) + assert.Equal(t, cm.ObjectMeta.Labels[constants.KubernetesPartOfLabelKey], constants.CheEclipseOrg) + assert.Equal(t, cm.ObjectMeta.Labels[constants.KubernetesComponentLabelKey], constants.CheCABundle) + assert.Equal(t, "git-cert", cm.Data["ca.crt"]) } -func SyncGitSelfSignedCertificate(t *testing.T) { - cert := &corev1.ConfigMap{ +func TestSyncSelfSignedCertificates(t *testing.T) { + selfSignedCerts := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: "cert", - Namespace: "eclipse-che", - ResourceVersion: "1", - Labels: map[string]string{ - "app.kubernetes.io/component": "ca-bundle", - "app.kubernetes.io/part-of": "che.eclipse.org"}, + Name: constants.DefaultSelfSignedCertificateSecretName, + Namespace: "eclipse-che", + }, + Data: map[string][]byte{ + "ca.crt": []byte("self-signed-cert"), }, - Data: map[string]string{"certifcate.crt": "che-certificate"}, } - gitTrustedCertsConfig := &corev1.ConfigMap{ + ctx := test.GetDeployContext(nil, []runtime.Object{selfSignedCerts}) + + certificates := NewCertificatesReconciler() + + _, err := certificates.syncSelfSignedCertificates(ctx) + assert.NoError(t, err) + + cm := &corev1.ConfigMap{} + err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: constants.DefaultSelfSignedCertificateSecretName, Namespace: "eclipse-che"}, cm) + assert.NoError(t, err) + assert.Equal(t, cm.ObjectMeta.Labels[constants.KubernetesPartOfLabelKey], constants.CheEclipseOrg) + assert.Equal(t, cm.ObjectMeta.Labels[constants.KubernetesComponentLabelKey], constants.CheCABundle) + assert.Equal(t, "self-signed-cert", cm.Data["ca.crt"]) +} + +func TestSyncCheCABundleCerts(t *testing.T) { + cert1 := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: "git-selfsigned-certificate", + Name: "cert1", Namespace: "eclipse-che", ResourceVersion: "1", Labels: map[string]string{ - "app.kubernetes.io/part-of": "che.eclipse.org", - }, + "app.kubernetes.io/component": "ca-bundle", + "app.kubernetes.io/part-of": "che.eclipse.org"}, }, - Data: map[string]string{"ca.crt": "git-certificate"}, + Data: map[string]string{"a1": "b1"}, } - checluster := &chev2.CheCluster{ + ctx := test.GetDeployContext(nil, []runtime.Object{cert1}) + + certificates := NewCertificatesReconciler() + + _, err := certificates.syncCheCABundleCerts(ctx) + assert.Nil(t, err) + + cm := &corev1.ConfigMap{} + err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: CheMergedCABundleCertsCMName, Namespace: "eclipse-che"}, cm) + assert.Nil(t, err) + assert.Equal(t, "cert1#1 ", cm.ObjectMeta.Annotations["che.eclipse.org/included-configmaps"]) + + cert2 := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ + Name: "cert2", Namespace: "eclipse-che", - Name: "eclipse-che", - }, - Spec: chev2.CheClusterSpec{ - DevEnvironments: chev2.CheClusterDevEnvironments{ - TrustedCerts: &chev2.TrustedCerts{ - GitTrustedCertsConfigMapName: "git-selfsigned-certificate", - }, - }, + Labels: map[string]string{ + "app.kubernetes.io/component": "ca-bundle", + "app.kubernetes.io/part-of": "che.eclipse.org"}, }, + Data: map[string]string{"a2": "b2"}, } - ctx := test.GetDeployContext(checluster, []runtime.Object{cert, gitTrustedCertsConfig}) + err = ctx.ClusterAPI.Client.Create(context.TODO(), cert2) + assert.Nil(t, err) - certificates := NewCertificatesReconciler() - done, err := certificates.syncCheCABundleCerts(ctx) + _, err = certificates.syncCheCABundleCerts(ctx) assert.Nil(t, err) - assert.True(t, done) - cacertMerged := &corev1.ConfigMap{} - err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: CheMergedCABundleCertsCMName, Namespace: "eclipse-che"}, cacertMerged) + cm = &corev1.ConfigMap{} + err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: CheMergedCABundleCertsCMName, Namespace: "eclipse-che"}, cm) assert.Nil(t, err) - assert.Equal(t, "cert-1.git-selfsigned-certificate-1", cacertMerged.ObjectMeta.Annotations["che.eclipse.org/included-configmaps"]) - assert.Equal(t, "git-certificate", cacertMerged.Data["git-selfsigned-certificate.ca.crt"]) - assert.Equal(t, "che-certificate", cacertMerged.Data["cert.certifcate.crt"]) + assert.Equal(t, "cert1#1 cert2#1 ", cm.ObjectMeta.Annotations["che.eclipse.org/included-configmaps"]) + assert.Equal(t, cm.Data[kubernetesCABundleCertsFile], "# ConfigMap: cert1, Key: a1\nb1\n\n# ConfigMap: cert2, Key: a2\nb2\n\n") } From 8eaec026d17a2fe125930b88fad0fb651c730cd3 Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Thu, 17 Oct 2024 10:21:21 +0200 Subject: [PATCH 4/6] Fixup Signed-off-by: Anatolii Bazko --- .../usernamespace/usernamespace_controller.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/controllers/usernamespace/usernamespace_controller.go b/controllers/usernamespace/usernamespace_controller.go index c30eaf272..015b85a7f 100644 --- a/controllers/usernamespace/usernamespace_controller.go +++ b/controllers/usernamespace/usernamespace_controller.go @@ -203,11 +203,19 @@ func (r *CheUserNamespaceReconciler) Reconcile(ctx context.Context, req ctrl.Req }, } + // Deprecated [CRW-6792]. + // All certificates are mounted into /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem + // and automatically added to the system trust store. + // TODO remove in the future. if err = r.reconcileSelfSignedCert(ctx, deployContext, req.Name, checluster); err != nil { logrus.Errorf("Failed to reconcile self-signed certificate into namespace '%s': %v", req.Name, err) return ctrl.Result{}, err } + // Deprecated [CRW-6792]. + // All certificates are mounted into /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem + // and automatically added to the system trust store. + // TODO remove in the future. if err = r.reconcileTrustedCerts(ctx, deployContext, req.Name, checluster); err != nil { logrus.Errorf("Failed to reconcile trusted certificates into namespace '%s': %v", req.Name, err) return ctrl.Result{}, err @@ -218,6 +226,10 @@ func (r *CheUserNamespaceReconciler) Reconcile(ctx context.Context, req ctrl.Req return ctrl.Result{}, err } + // Deprecated [CRW-6792]. + // All certificates are mounted into /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem + // and automatically added to the system trust store. + // TODO remove in the future. if err = r.reconcileGitTlsCertificate(ctx, req.Name, checluster, deployContext); err != nil { logrus.Errorf("Failed to reconcile Che git TLS certificate into namespace '%s': %v", req.Name, err) return ctrl.Result{}, err From 26b5ec7e523e65070f80fce587d0ad31c8888754 Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Fri, 18 Oct 2024 11:28:01 +0200 Subject: [PATCH 5/6] Update dev resources Signed-off-by: Anatolii Bazko --- api/v2/checluster_types.go | 2 +- .../manifests/che-operator.clusterserviceversion.yaml | 4 ++-- .../eclipse-che/manifests/org.eclipse.che_checlusters.yaml | 2 +- config/crd/bases/org.eclipse.che_checlusters.yaml | 2 +- deploy/deployment/kubernetes/combined.yaml | 2 +- .../checlusters.org.eclipse.che.CustomResourceDefinition.yaml | 2 +- deploy/deployment/openshift/combined.yaml | 2 +- .../checlusters.org.eclipse.che.CustomResourceDefinition.yaml | 2 +- .../checlusters.org.eclipse.che.CustomResourceDefinition.yaml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/api/v2/checluster_types.go b/api/v2/checluster_types.go index 52361f8b2..c0a39c2bc 100644 --- a/api/v2/checluster_types.go +++ b/api/v2/checluster_types.go @@ -110,7 +110,7 @@ type CheClusterDevEnvironments struct { // The URI must start from `http://` or `https://`. // +optional DefaultEditor string `json:"defaultEditor,omitempty"` - // Default components applied to 1spaces. + // Default components applied to DevWorkspaces. // These default components are meant to be used when a Devfile, that does not contain any components. // +optional DefaultComponents []devfile.Component `json:"defaultComponents,omitempty"` diff --git a/bundle/next/eclipse-che/manifests/che-operator.clusterserviceversion.yaml b/bundle/next/eclipse-che/manifests/che-operator.clusterserviceversion.yaml index 0b61d5e05..33a1b31ca 100644 --- a/bundle/next/eclipse-che/manifests/che-operator.clusterserviceversion.yaml +++ b/bundle/next/eclipse-che/manifests/che-operator.clusterserviceversion.yaml @@ -104,7 +104,7 @@ metadata: operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 repository: https://github.com/eclipse-che/che-operator support: Eclipse Foundation - name: eclipse-che.v7.94.0-889.next + name: eclipse-che.v7.94.0-890.next namespace: placeholder spec: apiservicedefinitions: {} @@ -1035,7 +1035,7 @@ spec: minKubeVersion: 1.19.0 provider: name: Eclipse Foundation - version: 7.94.0-889.next + version: 7.94.0-890.next webhookdefinitions: - admissionReviewVersions: - v1 diff --git a/bundle/next/eclipse-che/manifests/org.eclipse.che_checlusters.yaml b/bundle/next/eclipse-che/manifests/org.eclipse.che_checlusters.yaml index 14bf45ad4..51401fd6b 100644 --- a/bundle/next/eclipse-che/manifests/org.eclipse.che_checlusters.yaml +++ b/bundle/next/eclipse-che/manifests/org.eclipse.che_checlusters.yaml @@ -5399,7 +5399,7 @@ spec: type: object defaultComponents: description: |- - Default components applied to 1spaces. + Default components applied to DevWorkspaces. These default components are meant to be used when a Devfile, that does not contain any components. items: properties: diff --git a/config/crd/bases/org.eclipse.che_checlusters.yaml b/config/crd/bases/org.eclipse.che_checlusters.yaml index 294676c37..55f64b76e 100644 --- a/config/crd/bases/org.eclipse.che_checlusters.yaml +++ b/config/crd/bases/org.eclipse.che_checlusters.yaml @@ -5357,7 +5357,7 @@ spec: type: object defaultComponents: description: |- - Default components applied to 1spaces. + Default components applied to DevWorkspaces. These default components are meant to be used when a Devfile, that does not contain any components. items: properties: diff --git a/deploy/deployment/kubernetes/combined.yaml b/deploy/deployment/kubernetes/combined.yaml index 3f8dd4c4f..3c4ebf555 100644 --- a/deploy/deployment/kubernetes/combined.yaml +++ b/deploy/deployment/kubernetes/combined.yaml @@ -5378,7 +5378,7 @@ spec: type: object defaultComponents: description: |- - Default components applied to 1spaces. + Default components applied to DevWorkspaces. These default components are meant to be used when a Devfile, that does not contain any components. items: properties: diff --git a/deploy/deployment/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml b/deploy/deployment/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml index 7c9e052c7..55dec6fd7 100644 --- a/deploy/deployment/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml +++ b/deploy/deployment/kubernetes/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml @@ -5373,7 +5373,7 @@ spec: type: object defaultComponents: description: |- - Default components applied to 1spaces. + Default components applied to DevWorkspaces. These default components are meant to be used when a Devfile, that does not contain any components. items: properties: diff --git a/deploy/deployment/openshift/combined.yaml b/deploy/deployment/openshift/combined.yaml index b6445834e..8a2561009 100644 --- a/deploy/deployment/openshift/combined.yaml +++ b/deploy/deployment/openshift/combined.yaml @@ -5378,7 +5378,7 @@ spec: type: object defaultComponents: description: |- - Default components applied to 1spaces. + Default components applied to DevWorkspaces. These default components are meant to be used when a Devfile, that does not contain any components. items: properties: diff --git a/deploy/deployment/openshift/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml b/deploy/deployment/openshift/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml index db94053b8..ab35eda2f 100644 --- a/deploy/deployment/openshift/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml +++ b/deploy/deployment/openshift/objects/checlusters.org.eclipse.che.CustomResourceDefinition.yaml @@ -5373,7 +5373,7 @@ spec: type: object defaultComponents: description: |- - Default components applied to 1spaces. + Default components applied to DevWorkspaces. These default components are meant to be used when a Devfile, that does not contain any components. items: properties: diff --git a/helmcharts/next/crds/checlusters.org.eclipse.che.CustomResourceDefinition.yaml b/helmcharts/next/crds/checlusters.org.eclipse.che.CustomResourceDefinition.yaml index 7c9e052c7..55dec6fd7 100644 --- a/helmcharts/next/crds/checlusters.org.eclipse.che.CustomResourceDefinition.yaml +++ b/helmcharts/next/crds/checlusters.org.eclipse.che.CustomResourceDefinition.yaml @@ -5373,7 +5373,7 @@ spec: type: object defaultComponents: description: |- - Default components applied to 1spaces. + Default components applied to DevWorkspaces. These default components are meant to be used when a Devfile, that does not contain any components. items: properties: From 264a36d82fb122e97d2cd619f4cc0462eb41429f Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Fri, 18 Oct 2024 14:27:07 +0200 Subject: [PATCH 6/6] Fix Signed-off-by: Anatolii Bazko --- pkg/deploy/tls/certificates.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/deploy/tls/certificates.go b/pkg/deploy/tls/certificates.go index d85c4e60a..992603e3c 100644 --- a/pkg/deploy/tls/certificates.go +++ b/pkg/deploy/tls/certificates.go @@ -339,7 +339,7 @@ func (c *CertificatesReconciler) syncCheCABundleCerts(ctx *chetypes.DeployContex mergedCABundlesCM = deploy.GetConfigMapSpec( ctx, CheMergedCABundleCertsCMName, - map[string]string{"ca.crt": cheCABundlesExpectedContent}, + map[string]string{kubernetesCABundleCertsFile: cheCABundlesExpectedContent}, defaults.GetCheFlavor(), ) @@ -351,7 +351,6 @@ func (c *CertificatesReconciler) syncCheCABundleCerts(ctx *chetypes.DeployContex mergedCABundlesCM.ObjectMeta.Labels[constants.KubernetesComponentLabelKey] = constants.WorkspacesConfig // Set desired mount location - mergedCABundlesCM.Data = map[string]string{kubernetesCABundleCertsFile: cheCABundlesExpectedContent} mergedCABundlesCM.ObjectMeta.Annotations["controller.devfile.io/mount-as"] = "subpath" mergedCABundlesCM.ObjectMeta.Annotations["controller.devfile.io/mount-path"] = kubernetesCABundleCertsDir }