chore: Refactor certificates reconsiler (#1171)
* chore: Refactor certificates reconsiler Signed-off-by: Anatolii Bazko <abazko@redhat.com>pull/1204/head
parent
e3b6d1b8d1
commit
1e9fa6a078
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/eclipse-che/che-operator/pkg/deploy/pluginregistry"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy/postgres"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy/server"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy/tls"
|
||||
|
||||
identity_provider "github.com/eclipse-che/che-operator/pkg/deploy/identity-provider"
|
||||
"github.com/eclipse-che/che-operator/pkg/util"
|
||||
|
|
@ -96,6 +97,8 @@ func NewReconciler(
|
|||
openShiftOAuthUser := openshiftoauth.NewOpenShiftOAuthUser()
|
||||
reconcileManager.RegisterReconciler(openShiftOAuthUser)
|
||||
reconcileManager.RegisterReconciler(openshiftoauth.NewOpenShiftOAuth(openShiftOAuthUser))
|
||||
reconcileManager.RegisterReconciler(tls.NewCertificatesReconciler())
|
||||
reconcileManager.RegisterReconciler(tls.NewTlsSecretReconciler())
|
||||
|
||||
return &CheClusterReconciler{
|
||||
Scheme: scheme,
|
||||
|
|
@ -249,6 +252,22 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||
CheCluster: checluster,
|
||||
}
|
||||
|
||||
// Read proxy configuration
|
||||
proxy, err := GetProxyConfiguration(deployContext)
|
||||
if err != nil {
|
||||
r.Log.Error(err, "Error on reading proxy configuration")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
deployContext.Proxy = proxy
|
||||
|
||||
// Detect whether self-signed certificate is used
|
||||
isSelfSignedCertificate, err := tls.IsSelfSignedCertificateUsed(deployContext)
|
||||
if err != nil {
|
||||
r.Log.Error(err, "Failed to detect if self-signed certificate used.")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
deployContext.IsSelfSignedCertificate = isSelfSignedCertificate
|
||||
|
||||
if isCheGoingToBeUpdated(checluster) {
|
||||
// Current operator is newer than deployed Che
|
||||
backupCR, err := getBackupCRForUpdate(deployContext)
|
||||
|
|
@ -299,94 +318,6 @@ func (r *CheClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||
return ctrl.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
|
||||
// Read proxy configuration
|
||||
proxy, err := GetProxyConfiguration(deployContext)
|
||||
if err != nil {
|
||||
r.Log.Error(err, "Error on reading proxy configuration")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
// Assign Proxy to the deploy context
|
||||
deployContext.Proxy = proxy
|
||||
|
||||
if proxy.TrustedCAMapName != "" {
|
||||
provisioned, err := r.putOpenShiftCertsIntoConfigMap(deployContext)
|
||||
if !provisioned {
|
||||
configMapName := checluster.Spec.Server.ServerTrustStoreConfigMapName
|
||||
if err != nil {
|
||||
r.Log.Error(err, "Error on provisioning", "config map", configMapName)
|
||||
} else {
|
||||
r.Log.Error(err, "Waiting on provisioning", "config map", configMapName)
|
||||
}
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// Detect whether self-signed certificate is used
|
||||
selfSignedCertUsed, err := deploy.IsSelfSignedCertificateUsed(deployContext)
|
||||
if err != nil {
|
||||
r.Log.Error(err, "Failed to detect if self-signed certificate used.")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
if util.IsOpenShift {
|
||||
// create a secret with router tls cert when on OpenShift infra and router is configured with a self signed certificate
|
||||
if selfSignedCertUsed ||
|
||||
// To use Openshift v4 OAuth, the OAuth endpoints are served from a namespace
|
||||
// and NOT from the Openshift API Master URL (as in v3)
|
||||
// So we also need the self-signed certificate to access them (same as the Che server)
|
||||
(util.IsOpenShift4 && checluster.IsOpenShiftOAuthEnabled() && !checluster.Spec.Server.TlsSupport) {
|
||||
if err := deploy.CreateTLSSecretFromEndpoint(deployContext, "", deploy.CheTLSSelfSignedCertificateSecretName); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
if util.IsOpenShift && checluster.IsOpenShiftOAuthEnabled() {
|
||||
// create a secret with OpenShift API crt to be added to keystore that RH SSO will consume
|
||||
apiUrl, apiInternalUrl, err := util.GetOpenShiftAPIUrls()
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to get OpenShift cluster public hostname. A secret with API crt will not be created and consumed by RH-SSO/Keycloak")
|
||||
} else {
|
||||
baseURL := map[bool]string{true: apiInternalUrl, false: apiUrl}[apiInternalUrl != ""]
|
||||
if err := deploy.CreateTLSSecretFromEndpoint(deployContext, baseURL, "openshift-api-crt"); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Handle Che TLS certificates on Kubernetes infrastructure
|
||||
if checluster.Spec.Server.TlsSupport {
|
||||
if checluster.Spec.K8s.TlsSecretName != "" {
|
||||
// Self-signed certificate should be created to secure Che ingresses
|
||||
result, err := deploy.K8sHandleCheTLSSecrets(deployContext)
|
||||
if result.Requeue || result.RequeueAfter > 0 {
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
if !tests {
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
} else if selfSignedCertUsed {
|
||||
// Use default self-signed ingress certificate
|
||||
if err := deploy.CreateTLSSecretFromEndpoint(deployContext, "", deploy.CheTLSSelfSignedCertificateSecretName); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that CA certificates from all marked config maps are merged into single config map to be propageted to Che components
|
||||
done, err = deploy.SyncAdditionalCACertsConfigMapToCluster(deployContext)
|
||||
if err != nil {
|
||||
r.Log.Error(err, "Error updating additional CA config map")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
if !done && !tests {
|
||||
// Config map update is in progress
|
||||
// Return and do not force reconcile. When update finishes it will trigger reconcile loop.
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// Create service account "che" for che-server component.
|
||||
// "che" is the one which token is used to create workspace objects.
|
||||
// Notice: Also we have on more "che-workspace" SA used by plugins like exec, terminal, metrics with limited privileges.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ package che
|
|||
|
||||
import (
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy/tls"
|
||||
"github.com/eclipse-che/che-operator/pkg/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
|
@ -46,7 +47,7 @@ func IsTrustedBundleConfigMap(cl client.Client, watchNamespace string, obj clien
|
|||
// No, it is not form CR
|
||||
|
||||
// Check for component
|
||||
if value, exists := obj.GetLabels()[deploy.KubernetesComponentLabelKey]; !exists || value != deploy.CheCACertsConfigMapLabelValue {
|
||||
if value, exists := obj.GetLabels()[deploy.KubernetesComponentLabelKey]; !exists || value != tls.CheCACertsConfigMapLabelValue {
|
||||
// Labels do not match
|
||||
return false, ctrl.Request{}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019-2021 Red Hat, Inc.
|
||||
// This program and the accompanying materials are made
|
||||
// available under the terms of the Eclipse Public License 2.0
|
||||
// which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0
|
||||
//
|
||||
// Contributors:
|
||||
// Red Hat, Inc. - initial API and implementation
|
||||
//
|
||||
|
||||
package che
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
injector = "config.openshift.io/inject-trusted-cabundle"
|
||||
)
|
||||
|
||||
func SyncTrustStoreConfigMapToCluster(deployContext *deploy.DeployContext) (bool, error) {
|
||||
name := deployContext.CheCluster.Spec.Server.ServerTrustStoreConfigMapName
|
||||
configMapSpec := deploy.GetConfigMapSpec(deployContext, name, map[string]string{}, deploy.DefaultCheFlavor(deployContext.CheCluster))
|
||||
|
||||
// OpenShift will automatically injects all certs into the configmap
|
||||
configMapSpec.ObjectMeta.Labels[injector] = "true"
|
||||
|
||||
actual := &corev1.ConfigMap{}
|
||||
exists, err := deploy.GetNamespacedObject(deployContext, name, actual)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
// We have to create an empty config map with the specific labels
|
||||
return deploy.Create(deployContext, configMapSpec)
|
||||
}
|
||||
|
||||
if actual.ObjectMeta.Labels[injector] != "true" {
|
||||
actual.ObjectMeta.Labels[injector] = "true"
|
||||
logrus.Infof("Updating existed object: %s, name: %s", configMapSpec.Kind, configMapSpec.Name)
|
||||
err := deployContext.ClusterAPI.Client.Update(context.TODO(), actual)
|
||||
return true, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019-2021 Red Hat, Inc.
|
||||
// This program and the accompanying materials are made
|
||||
// available under the terms of the Eclipse Public License 2.0
|
||||
// which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0
|
||||
//
|
||||
// Contributors:
|
||||
// Red Hat, Inc. - initial API and implementation
|
||||
//
|
||||
|
||||
package che
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"testing"
|
||||
|
||||
orgv1 "github.com/eclipse-che/che-operator/api/v1"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
)
|
||||
|
||||
func TestSyncTrustStoreConfigMapToCluster(t *testing.T) {
|
||||
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
|
||||
corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
|
||||
cli := fake.NewFakeClientWithScheme(scheme.Scheme)
|
||||
deployContext := &deploy.DeployContext{
|
||||
CheCluster: &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "eclipse-che",
|
||||
Name: "eclipse-che",
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
Server: orgv1.CheClusterSpecServer{
|
||||
ServerTrustStoreConfigMapName: "trust",
|
||||
},
|
||||
},
|
||||
},
|
||||
ClusterAPI: deploy.ClusterAPI{
|
||||
Client: cli,
|
||||
NonCachingClient: cli,
|
||||
Scheme: scheme.Scheme,
|
||||
},
|
||||
}
|
||||
|
||||
done, err := SyncTrustStoreConfigMapToCluster(deployContext)
|
||||
if !done || err != nil {
|
||||
t.Fatalf("Failed to sync config map: %v", err)
|
||||
}
|
||||
|
||||
actual := &corev1.ConfigMap{}
|
||||
err = cli.Get(context.TODO(), types.NamespacedName{Name: "trust", Namespace: "eclipse-che"}, actual)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get config map: %v", err)
|
||||
}
|
||||
if actual.ObjectMeta.Labels[injector] != "true" {
|
||||
t.Fatalf("Failed to sync config map")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncExistedTrustStoreConfigMapToCluster(t *testing.T) {
|
||||
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
|
||||
corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
|
||||
cli := fake.NewFakeClientWithScheme(scheme.Scheme)
|
||||
deployContext := &deploy.DeployContext{
|
||||
CheCluster: &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "eclipse-che",
|
||||
Name: "eclipse-che",
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
Server: orgv1.CheClusterSpecServer{
|
||||
ServerTrustStoreConfigMapName: "trust",
|
||||
},
|
||||
},
|
||||
},
|
||||
ClusterAPI: deploy.ClusterAPI{
|
||||
Client: cli,
|
||||
NonCachingClient: cli,
|
||||
Scheme: scheme.Scheme,
|
||||
},
|
||||
}
|
||||
|
||||
cm := &corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "trust",
|
||||
Namespace: "eclipse-che",
|
||||
Labels: map[string]string{"a": "b"},
|
||||
},
|
||||
Data: map[string]string{"d": "c"},
|
||||
}
|
||||
err := cli.Create(context.TODO(), cm)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create config map: %v", err)
|
||||
}
|
||||
|
||||
done, err := SyncTrustStoreConfigMapToCluster(deployContext)
|
||||
if !done || err != nil {
|
||||
t.Fatalf("Failed to sync config map: %v", err)
|
||||
}
|
||||
|
||||
actual := &corev1.ConfigMap{}
|
||||
err = cli.Get(context.TODO(), types.NamespacedName{Name: "trust", Namespace: "eclipse-che"}, actual)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get config map: %v", err)
|
||||
}
|
||||
if actual.ObjectMeta.Labels[injector] != "true" || actual.ObjectMeta.Labels["a"] != "b" {
|
||||
t.Fatalf("Failed to sync config map")
|
||||
}
|
||||
if actual.Data["d"] != "c" {
|
||||
t.Fatalf("Failed to sync config map")
|
||||
}
|
||||
}
|
||||
|
|
@ -66,14 +66,3 @@ func GetProxyConfiguration(deployContext *deploy.DeployContext) (*deploy.Proxy,
|
|||
}
|
||||
return cheClusterProxyConf, nil
|
||||
}
|
||||
|
||||
func (r *CheClusterReconciler) putOpenShiftCertsIntoConfigMap(deployContext *deploy.DeployContext) (bool, error) {
|
||||
if deployContext.CheCluster.Spec.Server.ServerTrustStoreConfigMapName == "" {
|
||||
deployContext.CheCluster.Spec.Server.ServerTrustStoreConfigMapName = deploy.DefaultServerTrustStoreConfigMapName()
|
||||
if err := deploy.UpdateCheCRSpec(deployContext, "truststore configmap", deploy.DefaultServerTrustStoreConfigMapName()); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
return SyncTrustStoreConfigMapToCluster(deployContext)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
|
||||
orgv1 "github.com/eclipse-che/che-operator/api/v1"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy/tls"
|
||||
"github.com/eclipse-che/che-operator/pkg/util"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
|
@ -220,7 +221,7 @@ func backupConfigMaps(bctx *BackupContext, destDir string) (bool, error) {
|
|||
return true, err
|
||||
}
|
||||
|
||||
caBundlesConfigmaps, err := deploy.GetCACertsConfigMaps(bctx.r.nonCachingClient, bctx.cheCR.GetNamespace())
|
||||
caBundlesConfigmaps, err := tls.GetCACertsConfigMaps(bctx.r.nonCachingClient, bctx.cheCR.GetNamespace())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ package usernamespace
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy/tls"
|
||||
"github.com/eclipse-che/che-operator/pkg/util"
|
||||
|
||||
"github.com/devfile/devworkspace-operator/pkg/constants"
|
||||
|
|
@ -128,7 +129,7 @@ func (r *CheUserNamespaceReconciler) commonRules(ctx context.Context, namesInChe
|
|||
}
|
||||
|
||||
func (r *CheUserNamespaceReconciler) watchRulesForConfigMaps(ctx context.Context) handler.EventHandler {
|
||||
rules := r.commonRules(ctx, deploy.CheAllCACertsConfigMapName)
|
||||
rules := r.commonRules(ctx, tls.CheAllCACertsConfigMapName)
|
||||
return handler.EnqueueRequestsFromMapFunc(
|
||||
handler.MapFunc(func(obj client.Object) []reconcile.Request {
|
||||
return asReconcileRequestsForNamespaces(obj, rules)
|
||||
|
|
@ -312,7 +313,7 @@ func (r *CheUserNamespaceReconciler) reconcileTrustedCerts(ctx context.Context,
|
|||
}
|
||||
|
||||
sourceMap := &corev1.ConfigMap{}
|
||||
if err := r.client.Get(ctx, client.ObjectKey{Name: deploy.CheAllCACertsConfigMapName, Namespace: checluster.Namespace}, sourceMap); err != nil {
|
||||
if err := r.client.Get(ctx, client.ObjectKey{Name: tls.CheAllCACertsConfigMapName, Namespace: checluster.Namespace}, sourceMap); err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
v1 "github.com/eclipse-che/che-operator/api/v1"
|
||||
"github.com/eclipse-che/che-operator/controllers/devworkspace"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy/tls"
|
||||
"github.com/eclipse-che/che-operator/pkg/util"
|
||||
configv1 "github.com/openshift/api/config/v1"
|
||||
projectv1 "github.com/openshift/api/project/v1"
|
||||
|
|
@ -93,7 +94,7 @@ func setupCheCluster(t *testing.T, ctx context.Context, cl client.Client, scheme
|
|||
|
||||
caCerts := &corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: deploy.CheAllCACertsConfigMapName,
|
||||
Name: tls.CheAllCACertsConfigMapName,
|
||||
Namespace: cheNamespaceName,
|
||||
},
|
||||
Data: map[string]string{
|
||||
|
|
@ -483,7 +484,7 @@ func TestWatchRulesForConfigMapsInOtherNamespaces(t *testing.T) {
|
|||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: deploy.CheAllCACertsConfigMapName,
|
||||
Name: tls.CheAllCACertsConfigMapName,
|
||||
Namespace: "eclipse-che",
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy/tls"
|
||||
"github.com/eclipse-che/che-operator/pkg/util"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
|
@ -38,7 +39,7 @@ func (d *Dashboard) getDashboardDeploymentSpec() (*appsv1.Deployment, error) {
|
|||
|
||||
volumes, volumeMounts = d.provisionCustomPublicCA(volumes, volumeMounts)
|
||||
|
||||
selfSignedCertSecretExist, err := deploy.IsSelfSignedCASecretExists(d.deployContext)
|
||||
selfSignedCertSecretExist, err := tls.IsSelfSignedCASecretExists(d.deployContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -243,7 +244,7 @@ func (d *Dashboard) provisionCustomPublicCA(volumes []corev1.Volume, volumeMount
|
|||
VolumeSource: corev1.VolumeSource{
|
||||
ConfigMap: &corev1.ConfigMapVolumeSource{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: deploy.CheAllCACertsConfigMapName,
|
||||
Name: tls.CheAllCACertsConfigMapName,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -26,10 +26,11 @@ type ProvisioningStatus struct {
|
|||
}
|
||||
|
||||
type DeployContext struct {
|
||||
CheCluster *orgv1.CheCluster
|
||||
ClusterAPI ClusterAPI
|
||||
Proxy *Proxy
|
||||
DefaultCheHost string
|
||||
CheCluster *orgv1.CheCluster
|
||||
ClusterAPI ClusterAPI
|
||||
Proxy *Proxy
|
||||
DefaultCheHost string
|
||||
IsSelfSignedCertificate bool
|
||||
}
|
||||
|
||||
type ClusterAPI struct {
|
||||
|
|
|
|||
|
|
@ -128,6 +128,9 @@ const (
|
|||
// Name of the secret that holds self-signed certificate for git connections
|
||||
GitSelfSignedCertsConfigMapName = "che-git-self-signed-cert"
|
||||
|
||||
CheTLSSelfSignedCertificateSecretName = "self-signed-certificate"
|
||||
DefaultCheTLSSecretName = "che-tls"
|
||||
|
||||
// limits
|
||||
DefaultDashboardMemoryLimit = "256Mi"
|
||||
DefaultDashboardMemoryRequest = "32Mi"
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
orgv1 "github.com/eclipse-che/che-operator/api/v1"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy/postgres"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy/tls"
|
||||
"github.com/eclipse-che/che-operator/pkg/util"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
|
@ -100,7 +101,7 @@ func GetSpecKeycloakDeployment(
|
|||
}
|
||||
}
|
||||
|
||||
cmResourceVersions := deploy.GetAdditionalCACertsConfigMapVersion(deployContext)
|
||||
cmResourceVersions := tls.GetAdditionalCACertsConfigMapVersion(deployContext)
|
||||
terminationGracePeriodSeconds := int64(30)
|
||||
cheCertSecretVersion := getSecretResourceVersion("self-signed-certificate", deployContext.CheCluster.Namespace, deployContext.ClusterAPI)
|
||||
openshiftApiCertSecretVersion := getSecretResourceVersion("openshift-api-crt", deployContext.CheCluster.Namespace, deployContext.ClusterAPI)
|
||||
|
|
@ -136,7 +137,7 @@ func GetSpecKeycloakDeployment(
|
|||
customPublicCertsVolumeSource = corev1.VolumeSource{
|
||||
ConfigMap: &corev1.ConfigMapVolumeSource{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: deploy.CheAllCACertsConfigMapName,
|
||||
Name: tls.CheAllCACertsConfigMapName,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,9 +138,11 @@ func (oou *OpenShiftOAuthUser) Create(ctx *deploy.DeployContext) (bool, error) {
|
|||
return false, err
|
||||
}
|
||||
|
||||
ctx.CheCluster.Status.OpenShiftOAuthUserCredentialsSecret = OpenShiftOAuthUserCredentialsSecret
|
||||
if err := deploy.UpdateCheCRStatus(ctx, "openShiftOAuthUserCredentialsSecret", OpenShiftOAuthUserCredentialsSecret); err != nil {
|
||||
return false, err
|
||||
if ctx.CheCluster.Status.OpenShiftOAuthUserCredentialsSecret != OpenShiftOAuthUserCredentialsSecret {
|
||||
ctx.CheCluster.Status.OpenShiftOAuthUserCredentialsSecret = OpenShiftOAuthUserCredentialsSecret
|
||||
if err := deploy.UpdateCheCRStatus(ctx, "openShiftOAuthUserCredentialsSecret", OpenShiftOAuthUserCredentialsSecret); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := deploy.AppendFinalizer(ctx, OpenshiftOauthUserFinalizerName); err != nil {
|
||||
|
|
|
|||
|
|
@ -30,25 +30,6 @@ var roleDiffOpts = cmp.Options{
|
|||
cmpopts.IgnoreFields(rbac.PolicyRule{}, "ResourceNames", "NonResourceURLs"),
|
||||
}
|
||||
|
||||
func SyncTLSRoleToCluster(deployContext *DeployContext) (bool, error) {
|
||||
tlsPolicyRule := []rbac.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{
|
||||
"",
|
||||
},
|
||||
Resources: []string{
|
||||
"secrets",
|
||||
},
|
||||
Verbs: []string{
|
||||
"create",
|
||||
"get",
|
||||
"patch",
|
||||
},
|
||||
},
|
||||
}
|
||||
return SyncRoleToCluster(deployContext, CheTLSJobRoleName, tlsPolicyRule)
|
||||
}
|
||||
|
||||
func SyncExecRoleToCluster(deployContext *DeployContext) (bool, error) {
|
||||
execPolicyRule := []rbac.PolicyRule{
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,13 +18,10 @@ import (
|
|||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/sirupsen/logrus"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
k8slabels "k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
var SecretDiffOpts = cmp.Options{
|
||||
|
|
@ -101,24 +98,3 @@ func GetSecretSpec(deployContext *DeployContext, name string, namespace string,
|
|||
|
||||
return secret
|
||||
}
|
||||
|
||||
// CreateTLSSecretFromEndpoint creates TLS secret with given name which contains certificates obtained from the given url.
|
||||
// If the url is empty string, then cluster default certificate will be obtained.
|
||||
// Does nothing if secret with given name already exists.
|
||||
func CreateTLSSecretFromEndpoint(deployContext *DeployContext, url string, name string) (err error) {
|
||||
secret := &corev1.Secret{}
|
||||
if err := deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: deployContext.CheCluster.Namespace}, secret); err != nil && errors.IsNotFound(err) {
|
||||
crtBytes, err := GetEndpointTLSCrtBytes(deployContext, url)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to extract certificate for secret %s. Failed to create a secret with a self signed crt: %s", name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = SyncSecretToCluster(deployContext, name, deployContext.CheCluster.Namespace, map[string][]byte{"ca.crt": crtBytes})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
deploytls "github.com/eclipse-che/che-operator/pkg/deploy/tls"
|
||||
|
||||
"github.com/eclipse-che/che-operator/pkg/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
|
@ -246,7 +247,7 @@ func (s *Server) getCheConfigMapData() (cheEnv map[string]string, err error) {
|
|||
CheServerSecureExposerJwtProxyImage: deploy.DefaultCheServerSecureExposerJwtProxyImage(s.deployContext.CheCluster),
|
||||
CheJGroupsKubernetesLabels: cheLabels,
|
||||
CheMetricsEnabled: cheMetrics,
|
||||
CheTrustedCABundlesConfigMap: deploy.CheAllCACertsConfigMapName,
|
||||
CheTrustedCABundlesConfigMap: deploytls.CheAllCACertsConfigMapName,
|
||||
ServerStrategy: ingressStrategy,
|
||||
WorkspaceExposure: workspaceExposure,
|
||||
SingleHostGatewayConfigMapLabels: singleHostGatewayConfigMapLabels,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
identity_provider "github.com/eclipse-che/che-operator/pkg/deploy/identity-provider"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy/postgres"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy/tls"
|
||||
|
||||
orgv1 "github.com/eclipse-che/che-operator/api/v1"
|
||||
"github.com/eclipse-che/che-operator/pkg/util"
|
||||
|
|
@ -29,13 +30,13 @@ import (
|
|||
)
|
||||
|
||||
func (s Server) getDeploymentSpec() (*appsv1.Deployment, error) {
|
||||
selfSignedCASecretExists, err := deploy.IsSelfSignedCASecretExists(s.deployContext)
|
||||
selfSignedCASecretExists, err := tls.IsSelfSignedCASecretExists(s.deployContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmResourceVersions := GetCheConfigMapVersion(s.deployContext)
|
||||
cmResourceVersions += "," + deploy.GetAdditionalCACertsConfigMapVersion(s.deployContext)
|
||||
cmResourceVersions += "," + tls.GetAdditionalCACertsConfigMapVersion(s.deployContext)
|
||||
|
||||
terminationGracePeriodSeconds := int64(30)
|
||||
cheFlavor := deploy.DefaultCheFlavor(s.deployContext.CheCluster)
|
||||
|
|
@ -50,7 +51,7 @@ func (s Server) getDeploymentSpec() (*appsv1.Deployment, error) {
|
|||
VolumeSource: corev1.VolumeSource{
|
||||
ConfigMap: &corev1.ConfigMapVolumeSource{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: deploy.CheAllCACertsConfigMapName,
|
||||
Name: tls.CheAllCACertsConfigMapName,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,188 @@
|
|||
//
|
||||
// Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
// This program and the accompanying materials are made
|
||||
// available under the terms of the Eclipse Public License 2.0
|
||||
// which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0
|
||||
//
|
||||
// Contributors:
|
||||
// Red Hat, Inc. - initial API and implementation
|
||||
//
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
|
||||
// Local constants
|
||||
// labelEqualSign consyant is used as a replacement for '=' symbol in labels because '=' is not allowed there
|
||||
labelEqualSign = "-"
|
||||
// labelCommaSign consyant is used as a replacement for ',' symbol in labels because ',' is not allowed there
|
||||
labelCommaSign = "."
|
||||
)
|
||||
|
||||
type CertificatesReconciler struct {
|
||||
deploy.Reconcilable
|
||||
}
|
||||
|
||||
func NewCertificatesReconciler() *CertificatesReconciler {
|
||||
return &CertificatesReconciler{}
|
||||
}
|
||||
|
||||
func (c *CertificatesReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
|
||||
if ctx.Proxy.TrustedCAMapName != "" {
|
||||
if ctx.CheCluster.Spec.Server.ServerTrustStoreConfigMapName == "" {
|
||||
ctx.CheCluster.Spec.Server.ServerTrustStoreConfigMapName = deploy.DefaultServerTrustStoreConfigMapName()
|
||||
if err := deploy.UpdateCheCRSpec(ctx, "ServerTrustStoreConfigMapName", deploy.DefaultServerTrustStoreConfigMapName()); err != nil {
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
|
||||
actual := &corev1.ConfigMap{}
|
||||
err := ctx.ClusterAPI.NonCachingClient.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: deploy.DefaultServerTrustStoreConfigMapName()}, actual)
|
||||
if err == nil {
|
||||
if actual.ObjectMeta.Labels[deploy.KubernetesPartOfLabelKey] != deploy.CheEclipseOrg {
|
||||
if actual.ObjectMeta.Labels == nil {
|
||||
actual.ObjectMeta.Labels = make(map[string]string)
|
||||
}
|
||||
actual.ObjectMeta.Labels[deploy.KubernetesPartOfLabelKey] = deploy.CheEclipseOrg
|
||||
err := ctx.ClusterAPI.NonCachingClient.Update(context.TODO(), actual)
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result, done, err := c.syncTrustStoreConfigMapToCluster(ctx)
|
||||
if !done {
|
||||
return result, done, err
|
||||
}
|
||||
}
|
||||
|
||||
return c.syncAdditionalCACertsConfigMapToCluster(ctx)
|
||||
}
|
||||
|
||||
func (c *CertificatesReconciler) Finalize(ctx *deploy.DeployContext) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CertificatesReconciler) syncTrustStoreConfigMapToCluster(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
|
||||
name := ctx.CheCluster.Spec.Server.ServerTrustStoreConfigMapName
|
||||
configMapSpec := deploy.GetConfigMapSpec(ctx, name, map[string]string{}, deploy.DefaultCheFlavor(ctx.CheCluster))
|
||||
|
||||
// OpenShift will automatically injects all certs into the configmap
|
||||
configMapSpec.ObjectMeta.Labels[injector] = "true"
|
||||
configMapSpec.ObjectMeta.Labels[deploy.KubernetesPartOfLabelKey] = deploy.CheEclipseOrg
|
||||
configMapSpec.ObjectMeta.Labels[deploy.KubernetesComponentLabelKey] = CheCACertsConfigMapLabelValue
|
||||
|
||||
actual := &corev1.ConfigMap{}
|
||||
exists, err := deploy.GetNamespacedObject(ctx, name, actual)
|
||||
if err != nil {
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
// We have to create an empty config map with the specific labels
|
||||
done, err := deploy.Create(ctx, configMapSpec)
|
||||
return reconcile.Result{}, done, err
|
||||
}
|
||||
|
||||
if actual.ObjectMeta.Labels[injector] != "true" ||
|
||||
actual.ObjectMeta.Labels[deploy.KubernetesPartOfLabelKey] != deploy.CheEclipseOrg ||
|
||||
actual.ObjectMeta.Labels[deploy.KubernetesComponentLabelKey] != CheCACertsConfigMapLabelValue {
|
||||
|
||||
actual.ObjectMeta.Labels[injector] = "true"
|
||||
actual.ObjectMeta.Labels[deploy.KubernetesPartOfLabelKey] = deploy.CheEclipseOrg
|
||||
actual.ObjectMeta.Labels[deploy.KubernetesComponentLabelKey] = CheCACertsConfigMapLabelValue
|
||||
|
||||
logrus.Infof("Updating existed object: %s, name: %s", configMapSpec.Kind, configMapSpec.Name)
|
||||
if err := ctx.ClusterAPI.Client.Update(context.TODO(), actual); err != nil {
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
}
|
||||
|
||||
return reconcile.Result{}, true, nil
|
||||
}
|
||||
|
||||
func (c *CertificatesReconciler) syncAdditionalCACertsConfigMapToCluster(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
|
||||
// Get all source config maps, if any
|
||||
caConfigMaps, err := GetCACertsConfigMaps(ctx.ClusterAPI.Client, ctx.CheCluster.GetNamespace())
|
||||
if err != nil {
|
||||
return reconcile.Result{}, 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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
caConfigMapsCachedRevisions[nameRevision[0]] = nameRevision[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(caConfigMapsCurrentRevisions, caConfigMapsCachedRevisions) {
|
||||
// Existing merged config map is up to date, do nothing
|
||||
return reconcile.Result{}, true, nil
|
||||
}
|
||||
} else {
|
||||
if !errors.IsNotFound(err) {
|
||||
return reconcile.Result{}, 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
|
||||
}
|
||||
|
||||
// Save source config map revision
|
||||
if revisions != "" {
|
||||
revisions += labelCommaSign
|
||||
}
|
||||
revisions += cm.ObjectMeta.Name + labelEqualSign + cm.ObjectMeta.ResourceVersion
|
||||
}
|
||||
|
||||
mergedCAConfigMapSpec := deploy.GetConfigMapSpec(ctx, CheAllCACertsConfigMapName, data, deploy.DefaultCheFlavor(ctx.CheCluster))
|
||||
mergedCAConfigMapSpec.ObjectMeta.Labels[deploy.KubernetesPartOfLabelKey] = deploy.CheEclipseOrg
|
||||
mergedCAConfigMapSpec.ObjectMeta.Annotations[CheMergedCAConfigMapRevisionsAnnotationKey] = revisions
|
||||
done, err := deploy.SyncConfigMapSpecToCluster(ctx, mergedCAConfigMapSpec)
|
||||
return reconcile.Result{}, done, err
|
||||
}
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
//
|
||||
// Copyright (c) 2021 Red Hat, Inc.
|
||||
// This program and the accompanying materials are made
|
||||
// available under the terms of the Eclipse Public License 2.0
|
||||
// which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0
|
||||
//
|
||||
// Contributors:
|
||||
// Red Hat, Inc. - initial API and implementation
|
||||
//
|
||||
package tls
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"testing"
|
||||
|
||||
orgv1 "github.com/eclipse-che/che-operator/api/v1"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
func TestSyncTrustStoreConfigMapToCluster(t *testing.T) {
|
||||
checluster := &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "eclipse-che",
|
||||
Name: "eclipse-che",
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
Server: orgv1.CheClusterSpecServer{
|
||||
ServerTrustStoreConfigMapName: "trust",
|
||||
},
|
||||
},
|
||||
}
|
||||
ctx := deploy.GetTestDeployContext(checluster, []runtime.Object{})
|
||||
|
||||
certificates := NewCertificatesReconciler()
|
||||
_, done, err := certificates.syncTrustStoreConfigMapToCluster(ctx)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, done)
|
||||
|
||||
trustStoreConfigMap := &corev1.ConfigMap{}
|
||||
err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "trust", Namespace: "eclipse-che"}, trustStoreConfigMap)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, trustStoreConfigMap.ObjectMeta.Labels[injector], "true")
|
||||
}
|
||||
|
||||
func TestSyncExistedTrustStoreConfigMapToCluster(t *testing.T) {
|
||||
trustStoreConfigMap := &corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "trust",
|
||||
Namespace: "eclipse-che",
|
||||
Labels: map[string]string{"a": "b"},
|
||||
},
|
||||
Data: map[string]string{"d": "c"},
|
||||
}
|
||||
checluster := &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "eclipse-che",
|
||||
Name: "eclipse-che",
|
||||
},
|
||||
Spec: orgv1.CheClusterSpec{
|
||||
Server: orgv1.CheClusterSpecServer{
|
||||
ServerTrustStoreConfigMapName: "trust",
|
||||
},
|
||||
},
|
||||
}
|
||||
ctx := deploy.GetTestDeployContext(checluster, []runtime.Object{trustStoreConfigMap})
|
||||
|
||||
certificates := NewCertificatesReconciler()
|
||||
_, done, err := certificates.syncTrustStoreConfigMapToCluster(ctx)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, done)
|
||||
|
||||
err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "trust", Namespace: "eclipse-che"}, trustStoreConfigMap)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, trustStoreConfigMap.ObjectMeta.Labels[injector], "true")
|
||||
assert.Equal(t, trustStoreConfigMap.ObjectMeta.Labels["a"], "b")
|
||||
assert.Equal(t, trustStoreConfigMap.Data["d"], "c")
|
||||
}
|
||||
|
||||
func TestSyncAdditionalCACertsConfigMapToCluster(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{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "cert2",
|
||||
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"},
|
||||
}
|
||||
|
||||
ctx := deploy.GetTestDeployContext(nil, []runtime.Object{cert1})
|
||||
|
||||
certificates := NewCertificatesReconciler()
|
||||
_, done, err := certificates.syncAdditionalCACertsConfigMapToCluster(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)
|
||||
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.syncAdditionalCACertsConfigMapToCluster(ctx)
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, done)
|
||||
|
||||
_, done, err = certificates.syncAdditionalCACertsConfigMapToCluster(ctx)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, done)
|
||||
|
||||
err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: CheAllCACertsConfigMapName, Namespace: "eclipse-che"}, cacertMerged)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, cacertMerged.ObjectMeta.Annotations["che.eclipse.org/included-configmaps"], "cert1-1.cert2-1")
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// Copyright (c) 2021 Red Hat, Inc.
|
||||
// This program and the accompanying materials are made
|
||||
// available under the terms of the Eclipse Public License 2.0
|
||||
// which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0
|
||||
//
|
||||
// Contributors:
|
||||
// Red Hat, Inc. - initial API and implementation
|
||||
//
|
||||
package tls
|
||||
|
||||
import "github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
|
||||
func init() {
|
||||
err := deploy.InitTestDefaultsFromDeployment("../../../config/manager/manager.yaml")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
// This program and the accompanying materials are made
|
||||
// available under the terms of the Eclipse Public License 2.0
|
||||
// which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0
|
||||
//
|
||||
// Contributors:
|
||||
// Red Hat, Inc. - initial API and implementation
|
||||
//
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
"github.com/eclipse-che/che-operator/pkg/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
type TlsSecretReconciler struct {
|
||||
deploy.Reconcilable
|
||||
}
|
||||
|
||||
func NewTlsSecretReconciler() *TlsSecretReconciler {
|
||||
return &TlsSecretReconciler{}
|
||||
}
|
||||
|
||||
func (t *TlsSecretReconciler) Reconcile(ctx *deploy.DeployContext) (reconcile.Result, bool, error) {
|
||||
if util.IsOpenShift {
|
||||
// create a secret with router tls cert when on OpenShift infra and router is configured with a self signed certificate
|
||||
if ctx.IsSelfSignedCertificate ||
|
||||
// To use Openshift v4 OAuth, the OAuth endpoints are served from a namespace
|
||||
// and NOT from the Openshift API Master URL (as in v3)
|
||||
// So we also need the self-signed certificate to access them (same as the Che server)
|
||||
(util.IsOpenShift4 && ctx.CheCluster.IsOpenShiftOAuthEnabled() && !ctx.CheCluster.Spec.Server.TlsSupport) {
|
||||
if err := CreateTLSSecretFromEndpoint(ctx, "", deploy.CheTLSSelfSignedCertificateSecretName); err != nil {
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
}
|
||||
|
||||
if util.IsOpenShift && ctx.CheCluster.IsOpenShiftOAuthEnabled() {
|
||||
// create a secret with OpenShift API crt to be added to keystore that RH SSO will consume
|
||||
apiUrl, apiInternalUrl, err := util.GetOpenShiftAPIUrls()
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to get OpenShift cluster public hostname. A secret with API crt will not be created and consumed by RH-SSO/Keycloak")
|
||||
} else {
|
||||
baseURL := map[bool]string{true: apiInternalUrl, false: apiUrl}[apiInternalUrl != ""]
|
||||
if err := CreateTLSSecretFromEndpoint(ctx, baseURL, "openshift-api-crt"); err != nil {
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Handle Che TLS certificates on Kubernetes infrastructure
|
||||
if ctx.CheCluster.Spec.Server.TlsSupport {
|
||||
if ctx.CheCluster.Spec.K8s.TlsSecretName != "" {
|
||||
// Self-signed certificate should be created to secure Che ingresses
|
||||
result, err := K8sHandleCheTLSSecrets(ctx)
|
||||
if result.Requeue || result.RequeueAfter > 0 {
|
||||
return result, false, err
|
||||
}
|
||||
} else if ctx.IsSelfSignedCertificate {
|
||||
// Use default self-signed ingress certificate
|
||||
if err := CreateTLSSecretFromEndpoint(ctx, "", deploy.CheTLSSelfSignedCertificateSecretName); err != nil {
|
||||
return reconcile.Result{}, false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reconcile.Result{}, true, nil
|
||||
}
|
||||
|
||||
func (t *TlsSecretReconciler) Finalize(ctx *deploy.DeployContext) error {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright (c) 2019-2021 Red Hat, Inc.
|
||||
// Copyright (c) 2012-2021 Red Hat, Inc.
|
||||
// This program and the accompanying materials are made
|
||||
// available under the terms of the Eclipse Public License 2.0
|
||||
// which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
|
|
@ -9,7 +9,8 @@
|
|||
// Contributors:
|
||||
// Red Hat, Inc. - initial API and implementation
|
||||
//
|
||||
package deploy
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -19,53 +20,40 @@ import (
|
|||
stderrors "errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/eclipse-che/che-operator/pkg/util"
|
||||
routev1 "github.com/openshift/api/route/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
networking "k8s.io/api/networking/v1"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
// TLS related constants
|
||||
const (
|
||||
CheTLSJobServiceAccountName = "che-tls-job-service-account"
|
||||
CheTLSJobRoleName = "che-tls-job-role"
|
||||
CheTLSJobRoleBindingName = "che-tls-job-role-binding"
|
||||
CheTLSJobName = "che-tls-job"
|
||||
CheTLSJobComponentName = "che-create-tls-secret-job"
|
||||
CheTLSSelfSignedCertificateSecretName = "self-signed-certificate"
|
||||
DefaultCheTLSSecretName = "che-tls"
|
||||
|
||||
// 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"
|
||||
|
||||
// Local constants
|
||||
// labelEqualSign consyant is used as a replacement for '=' symbol in labels because '=' is not allowed there
|
||||
labelEqualSign = "-"
|
||||
// labelCommaSign consyant is used as a replacement for ',' symbol in labels because ',' is not allowed there
|
||||
labelCommaSign = "."
|
||||
CheTLSJobServiceAccountName = "che-tls-job-service-account"
|
||||
CheTLSJobRoleName = "che-tls-job-role"
|
||||
CheTLSJobRoleBindingName = "che-tls-job-role-binding"
|
||||
CheTLSJobName = "che-tls-job"
|
||||
CheTLSJobComponentName = "che-create-tls-secret-job"
|
||||
)
|
||||
|
||||
// IsSelfSignedCASecretExists checks if CheTLSSelfSignedCertificateSecretName exists so depending components can mount it
|
||||
func IsSelfSignedCASecretExists(deployContext *DeployContext) (bool, error) {
|
||||
func IsSelfSignedCASecretExists(ctx *deploy.DeployContext) (bool, error) {
|
||||
cheTLSSelfSignedCertificateSecret := &corev1.Secret{}
|
||||
err := deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: deployContext.CheCluster.Namespace, Name: CheTLSSelfSignedCertificateSecretName}, cheTLSSelfSignedCertificateSecret)
|
||||
err := ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: deploy.CheTLSSelfSignedCertificateSecretName}, cheTLSSelfSignedCertificateSecret)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
return false, nil
|
||||
|
|
@ -76,12 +64,12 @@ func IsSelfSignedCASecretExists(deployContext *DeployContext) (bool, error) {
|
|||
}
|
||||
|
||||
// IsSelfSignedCertificateUsed detects whether endpoints are/should be secured by self-signed certificate.
|
||||
func IsSelfSignedCertificateUsed(deployContext *DeployContext) (bool, error) {
|
||||
func IsSelfSignedCertificateUsed(ctx *deploy.DeployContext) (bool, error) {
|
||||
if util.IsTestMode() {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
cheCASecretExist, err := IsSelfSignedCASecretExists(deployContext)
|
||||
cheCASecretExist, err := IsSelfSignedCASecretExists(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
@ -92,11 +80,11 @@ func IsSelfSignedCertificateUsed(deployContext *DeployContext) (bool, error) {
|
|||
|
||||
if !util.IsOpenShift {
|
||||
// Handle custom tls secret for Che ingresses
|
||||
cheTLSSecretName := deployContext.CheCluster.Spec.K8s.TlsSecretName
|
||||
cheTLSSecretName := ctx.CheCluster.Spec.K8s.TlsSecretName
|
||||
if cheTLSSecretName != "" {
|
||||
// The secret is specified in CR
|
||||
cheTLSSecret := &corev1.Secret{}
|
||||
err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: deployContext.CheCluster.Namespace, Name: cheTLSSecretName}, cheTLSSecret)
|
||||
err = ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: cheTLSSecretName}, cheTLSSecret)
|
||||
if err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
// Failed to get secret, return error to restart reconcile loop.
|
||||
|
|
@ -115,7 +103,7 @@ func IsSelfSignedCertificateUsed(deployContext *DeployContext) (bool, error) {
|
|||
}
|
||||
|
||||
// Get route/ingress TLS certificates chain
|
||||
peerCertificates, err := GetEndpointTLSCrtChain(deployContext, "")
|
||||
peerCertificates, err := GetEndpointTLSCrtChain(ctx, "")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
@ -133,26 +121,26 @@ func IsSelfSignedCertificateUsed(deployContext *DeployContext) (bool, error) {
|
|||
|
||||
// GetEndpointTLSCrtChain retrieves TLS certificates chain from given endpoint.
|
||||
// If endpoint is not specified, then a test route/ingress will be created and used to get router certificates.
|
||||
func GetEndpointTLSCrtChain(deployContext *DeployContext, endpointURL string) ([]*x509.Certificate, error) {
|
||||
func GetEndpointTLSCrtChain(ctx *deploy.DeployContext, endpointURL string) ([]*x509.Certificate, error) {
|
||||
if util.IsTestMode() {
|
||||
return nil, stderrors.New("Not allowed for tests")
|
||||
}
|
||||
|
||||
var useTestEndpoint bool = len(endpointURL) < 1
|
||||
var requestURL string
|
||||
cheFlavor := DefaultCheFlavor(deployContext.CheCluster)
|
||||
cheFlavor := deploy.DefaultCheFlavor(ctx.CheCluster)
|
||||
if useTestEndpoint {
|
||||
if util.IsOpenShift {
|
||||
// Create test route to get certificates chain.
|
||||
// Note, it is not possible to use SyncRouteToCluster here as it may cause infinite reconcile loop.
|
||||
routeSpec, err := GetRouteSpec(
|
||||
deployContext,
|
||||
routeSpec, err := deploy.GetRouteSpec(
|
||||
ctx,
|
||||
"test",
|
||||
"",
|
||||
"",
|
||||
"test",
|
||||
8080,
|
||||
deployContext.CheCluster.Spec.Server.CheServerRoute,
|
||||
ctx.CheCluster.Spec.Server.CheServerRoute,
|
||||
cheFlavor)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -160,7 +148,7 @@ func GetEndpointTLSCrtChain(deployContext *DeployContext, endpointURL string) ([
|
|||
// Remove controller reference to prevent queueing new reconcile loop
|
||||
routeSpec.SetOwnerReferences(nil)
|
||||
// Create route manually
|
||||
if err := deployContext.ClusterAPI.Client.Create(context.TODO(), routeSpec); err != nil {
|
||||
if err := ctx.ClusterAPI.Client.Create(context.TODO(), routeSpec); err != nil {
|
||||
if !errors.IsAlreadyExists(err) {
|
||||
logrus.Errorf("Failed to create test route 'test': %s", err)
|
||||
return nil, err
|
||||
|
|
@ -169,7 +157,7 @@ func GetEndpointTLSCrtChain(deployContext *DeployContext, endpointURL string) ([
|
|||
|
||||
// Schedule test route cleanup after the job done.
|
||||
defer func() {
|
||||
if err := deployContext.ClusterAPI.Client.Delete(context.TODO(), routeSpec); err != nil {
|
||||
if err := ctx.ClusterAPI.Client.Delete(context.TODO(), routeSpec); err != nil {
|
||||
logrus.Errorf("Failed to delete test route %s: %s", routeSpec.Name, err)
|
||||
}
|
||||
}()
|
||||
|
|
@ -178,7 +166,7 @@ func GetEndpointTLSCrtChain(deployContext *DeployContext, endpointURL string) ([
|
|||
route := &routev1.Route{}
|
||||
for {
|
||||
time.Sleep(time.Duration(1) * time.Second)
|
||||
exists, err := GetNamespacedObject(deployContext, routeSpec.Name, route)
|
||||
exists, err := deploy.GetNamespacedObject(ctx, routeSpec.Name, route)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if exists {
|
||||
|
|
@ -192,17 +180,17 @@ func GetEndpointTLSCrtChain(deployContext *DeployContext, endpointURL string) ([
|
|||
|
||||
// Create test ingress to get certificates chain.
|
||||
// Note, it is not possible to use SyncIngressToCluster here as it may cause infinite reconcile loop.
|
||||
_, ingressSpec := GetIngressSpec(
|
||||
deployContext,
|
||||
_, ingressSpec := deploy.GetIngressSpec(
|
||||
ctx,
|
||||
"test",
|
||||
"",
|
||||
"",
|
||||
"test",
|
||||
8080,
|
||||
deployContext.CheCluster.Spec.Server.CheServerIngress,
|
||||
ctx.CheCluster.Spec.Server.CheServerIngress,
|
||||
cheFlavor)
|
||||
// Create ingress manually
|
||||
if err := deployContext.ClusterAPI.Client.Create(context.TODO(), ingressSpec); err != nil {
|
||||
if err := ctx.ClusterAPI.Client.Create(context.TODO(), ingressSpec); err != nil {
|
||||
if !errors.IsAlreadyExists(err) {
|
||||
logrus.Errorf("Failed to create test ingress 'test': %s", err)
|
||||
return nil, err
|
||||
|
|
@ -211,7 +199,7 @@ func GetEndpointTLSCrtChain(deployContext *DeployContext, endpointURL string) ([
|
|||
|
||||
// Schedule test ingress cleanup after the job done.
|
||||
defer func() {
|
||||
if err := deployContext.ClusterAPI.Client.Delete(context.TODO(), ingressSpec); err != nil {
|
||||
if err := ctx.ClusterAPI.Client.Delete(context.TODO(), ingressSpec); err != nil {
|
||||
logrus.Errorf("Failed to delete test ingress %s: %s", ingressSpec.Name, err)
|
||||
}
|
||||
}()
|
||||
|
|
@ -220,7 +208,7 @@ func GetEndpointTLSCrtChain(deployContext *DeployContext, endpointURL string) ([
|
|||
ingress := &networking.Ingress{}
|
||||
for {
|
||||
time.Sleep(time.Duration(1) * time.Second)
|
||||
exists, err := GetNamespacedObject(deployContext, ingressSpec.Name, ingress)
|
||||
exists, err := deploy.GetNamespacedObject(ctx, ingressSpec.Name, ingress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if exists {
|
||||
|
|
@ -234,14 +222,14 @@ func GetEndpointTLSCrtChain(deployContext *DeployContext, endpointURL string) ([
|
|||
requestURL = endpointURL
|
||||
}
|
||||
|
||||
certificates, err := doRequestForTLSCrtChain(deployContext, requestURL, useTestEndpoint)
|
||||
certificates, err := doRequestForTLSCrtChain(ctx, requestURL, useTestEndpoint)
|
||||
if err != nil {
|
||||
if deployContext.Proxy.HttpProxy != "" && useTestEndpoint {
|
||||
if ctx.Proxy.HttpProxy != "" && useTestEndpoint {
|
||||
// Fetching certificates from the test route without proxy failed. Probably non-proxy connections are blocked.
|
||||
// Retrying with proxy configuration, however it might cause retreiving of wrong certificate in case of TLS interception by proxy.
|
||||
logrus.Warn("Failed to get certificate chain of trust of the OpenShift Ingress bypassing the proxy")
|
||||
|
||||
return doRequestForTLSCrtChain(deployContext, requestURL, false)
|
||||
return doRequestForTLSCrtChain(ctx, requestURL, false)
|
||||
}
|
||||
|
||||
return nil, err
|
||||
|
|
@ -249,13 +237,13 @@ func GetEndpointTLSCrtChain(deployContext *DeployContext, endpointURL string) ([
|
|||
return certificates, nil
|
||||
}
|
||||
|
||||
func doRequestForTLSCrtChain(deployContext *DeployContext, requestURL string, skipProxy bool) ([]*x509.Certificate, error) {
|
||||
func doRequestForTLSCrtChain(ctx *deploy.DeployContext, requestURL string, skipProxy bool) ([]*x509.Certificate, error) {
|
||||
transport := &http.Transport{}
|
||||
// Adding the proxy settings to the Transport object.
|
||||
// However, in case of test route we need to reach cluter directly in order to get the right certificate.
|
||||
if deployContext.Proxy.HttpProxy != "" && !skipProxy {
|
||||
logrus.Infof("Configuring proxy with %s to extract certificate chain from the following URL: %s", deployContext.Proxy.HttpProxy, requestURL)
|
||||
ConfigureProxy(deployContext, transport)
|
||||
if ctx.Proxy.HttpProxy != "" && !skipProxy {
|
||||
logrus.Infof("Configuring proxy with %s to extract certificate chain from the following URL: %s", ctx.Proxy.HttpProxy, requestURL)
|
||||
deploy.ConfigureProxy(ctx, transport)
|
||||
}
|
||||
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
client := &http.Client{
|
||||
|
|
@ -276,8 +264,8 @@ func doRequestForTLSCrtChain(deployContext *DeployContext, requestURL string, sk
|
|||
// Creates a test TLS route/ingress if endpoint url is empty.
|
||||
// There's an easier way which is to read tls secret in default (3.11) or openshift-ingress (4.0) namespace
|
||||
// which however requires extra privileges for operator service account
|
||||
func GetEndpointTLSCrtBytes(deployContext *DeployContext, endpointURL string) (certificates []byte, err error) {
|
||||
peerCertificates, err := GetEndpointTLSCrtChain(deployContext, endpointURL)
|
||||
func GetEndpointTLSCrtBytes(ctx *deploy.DeployContext, endpointURL string) (certificates []byte, err error) {
|
||||
peerCertificates, err := GetEndpointTLSCrtChain(ctx, endpointURL)
|
||||
if err != nil {
|
||||
if util.IsTestMode() {
|
||||
fakeCrt := make([]byte, 5)
|
||||
|
|
@ -299,14 +287,14 @@ func GetEndpointTLSCrtBytes(deployContext *DeployContext, endpointURL string) (c
|
|||
}
|
||||
|
||||
// K8sHandleCheTLSSecrets handles TLS secrets required for Che deployment on Kubernetes infrastructure.
|
||||
func K8sHandleCheTLSSecrets(deployContext *DeployContext) (reconcile.Result, error) {
|
||||
cheTLSSecretName := deployContext.CheCluster.Spec.K8s.TlsSecretName
|
||||
func K8sHandleCheTLSSecrets(ctx *deploy.DeployContext) (reconcile.Result, error) {
|
||||
cheTLSSecretName := ctx.CheCluster.Spec.K8s.TlsSecretName
|
||||
|
||||
cheTLSSecretNamespacedName := types.NamespacedName{Namespace: deployContext.CheCluster.Namespace, Name: cheTLSSecretName}
|
||||
CheTLSSelfSignedCertificateSecretNamespacedName := types.NamespacedName{Namespace: deployContext.CheCluster.Namespace, Name: CheTLSSelfSignedCertificateSecretName}
|
||||
cheTLSSecretNamespacedName := types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: cheTLSSecretName}
|
||||
CheTLSSelfSignedCertificateSecretNamespacedName := types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: deploy.CheTLSSelfSignedCertificateSecretName}
|
||||
|
||||
job := &batchv1.Job{}
|
||||
err := deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: CheTLSJobName, Namespace: deployContext.CheCluster.Namespace}, job)
|
||||
err := ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: CheTLSJobName, Namespace: ctx.CheCluster.Namespace}, job)
|
||||
var jobExists bool
|
||||
if err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
|
|
@ -320,7 +308,7 @@ func K8sHandleCheTLSSecrets(deployContext *DeployContext) (reconcile.Result, err
|
|||
// ===== Check Che server TLS certificate ===== //
|
||||
|
||||
cheTLSSecret := &corev1.Secret{}
|
||||
err = deployContext.ClusterAPI.Client.Get(context.TODO(), cheTLSSecretNamespacedName, cheTLSSecret)
|
||||
err = ctx.ClusterAPI.Client.Get(context.TODO(), cheTLSSecretNamespacedName, cheTLSSecret)
|
||||
if err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
// Error reading secret info
|
||||
|
|
@ -343,58 +331,58 @@ func K8sHandleCheTLSSecrets(deployContext *DeployContext) (reconcile.Result, err
|
|||
|
||||
// Remove Che CA certificate secret if any
|
||||
cheCASelfSignedCertificateSecret := &corev1.Secret{}
|
||||
err = deployContext.ClusterAPI.Client.Get(context.TODO(), CheTLSSelfSignedCertificateSecretNamespacedName, cheCASelfSignedCertificateSecret)
|
||||
err = ctx.ClusterAPI.Client.Get(context.TODO(), CheTLSSelfSignedCertificateSecretNamespacedName, cheCASelfSignedCertificateSecret)
|
||||
if err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
// Error reading secret info
|
||||
logrus.Errorf("Error getting Che self-signed certificate secert \"%s\": %v", CheTLSSelfSignedCertificateSecretName, err)
|
||||
logrus.Errorf("Error getting Che self-signed certificate secert \"%s\": %v", deploy.CheTLSSelfSignedCertificateSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
// Che CA certificate doesn't exists (that's expected at this point), do nothing
|
||||
} else {
|
||||
// Remove Che CA secret because Che TLS secret is missing (they should be generated together).
|
||||
if err = deployContext.ClusterAPI.Client.Delete(context.TODO(), cheCASelfSignedCertificateSecret); err != nil {
|
||||
logrus.Errorf("Error deleting Che self-signed certificate secret \"%s\": %v", CheTLSSelfSignedCertificateSecretName, err)
|
||||
if err = ctx.ClusterAPI.Client.Delete(context.TODO(), cheCASelfSignedCertificateSecret); err != nil {
|
||||
logrus.Errorf("Error deleting Che self-signed certificate secret \"%s\": %v", deploy.CheTLSSelfSignedCertificateSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare permissions for the certificate generation job
|
||||
done, err := SyncServiceAccountToCluster(deployContext, CheTLSJobServiceAccountName)
|
||||
done, err := deploy.SyncServiceAccountToCluster(ctx, CheTLSJobServiceAccountName)
|
||||
if !done {
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
|
||||
done, err = SyncTLSRoleToCluster(deployContext)
|
||||
done, err = SyncTLSRoleToCluster(ctx)
|
||||
if !done {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
done, err = SyncRoleBindingToCluster(deployContext, CheTLSJobRoleBindingName, CheTLSJobServiceAccountName, CheTLSJobRoleName, "Role")
|
||||
done, err = deploy.SyncRoleBindingToCluster(ctx, CheTLSJobRoleBindingName, CheTLSJobServiceAccountName, CheTLSJobRoleName, "Role")
|
||||
if !done {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
domains := deployContext.CheCluster.Spec.K8s.IngressDomain + ",*." + deployContext.CheCluster.Spec.K8s.IngressDomain
|
||||
if deployContext.CheCluster.Spec.Server.CheHost != "" && !strings.Contains(deployContext.CheCluster.Spec.Server.CheHost, deployContext.CheCluster.Spec.K8s.IngressDomain) && deployContext.CheCluster.Spec.Server.CheHostTLSSecret == "" {
|
||||
domains += "," + deployContext.CheCluster.Spec.Server.CheHost
|
||||
domains := ctx.CheCluster.Spec.K8s.IngressDomain + ",*." + ctx.CheCluster.Spec.K8s.IngressDomain
|
||||
if ctx.CheCluster.Spec.Server.CheHost != "" && !strings.Contains(ctx.CheCluster.Spec.Server.CheHost, ctx.CheCluster.Spec.K8s.IngressDomain) && ctx.CheCluster.Spec.Server.CheHostTLSSecret == "" {
|
||||
domains += "," + ctx.CheCluster.Spec.Server.CheHost
|
||||
}
|
||||
|
||||
labels := ""
|
||||
for labelName, labelValue := range GetLabels(deployContext.CheCluster, cheTLSSecretName) {
|
||||
for labelName, labelValue := range deploy.GetLabels(ctx.CheCluster, cheTLSSecretName) {
|
||||
labels += fmt.Sprintf("%s=%s ", labelName, labelValue)
|
||||
}
|
||||
|
||||
cheTLSSecretsCreationJobImage := DefaultCheTLSSecretsCreationJobImage()
|
||||
cheTLSSecretsCreationJobImage := deploy.DefaultCheTLSSecretsCreationJobImage()
|
||||
jobEnvVars := map[string]string{
|
||||
"DOMAIN": domains,
|
||||
"CHE_NAMESPACE": deployContext.CheCluster.Namespace,
|
||||
"CHE_NAMESPACE": ctx.CheCluster.Namespace,
|
||||
"CHE_SERVER_TLS_SECRET_NAME": cheTLSSecretName,
|
||||
"CHE_CA_CERTIFICATE_SECRET_NAME": CheTLSSelfSignedCertificateSecretName,
|
||||
"CHE_CA_CERTIFICATE_SECRET_NAME": deploy.CheTLSSelfSignedCertificateSecretName,
|
||||
"LABELS": labels,
|
||||
}
|
||||
|
||||
_, err = SyncJobToCluster(deployContext, CheTLSJobName, CheTLSJobComponentName, cheTLSSecretsCreationJobImage, CheTLSJobServiceAccountName, jobEnvVars)
|
||||
_, err = deploy.SyncJobToCluster(ctx, CheTLSJobName, CheTLSJobComponentName, cheTLSSecretsCreationJobImage, CheTLSJobServiceAccountName, jobEnvVars)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
|
|
@ -405,11 +393,11 @@ func K8sHandleCheTLSSecrets(deployContext *DeployContext) (reconcile.Result, err
|
|||
if jobExists {
|
||||
// The job object is present
|
||||
if job.Status.Succeeded > 0 {
|
||||
logrus.Infof("Import public part of Eclipse Che self-signed CA certificate from \"%s\" secret into your browser.", CheTLSSelfSignedCertificateSecretName)
|
||||
deleteJob(deployContext, job)
|
||||
logrus.Infof("Import public part of Eclipse Che self-signed CA certificate from \"%s\" secret into your browser.", deploy.CheTLSSelfSignedCertificateSecretName)
|
||||
deleteJob(ctx, job)
|
||||
} else if job.Status.Failed > 0 {
|
||||
// The job failed, but the certificate is present, shouldn't happen
|
||||
deleteJob(deployContext, job)
|
||||
deleteJob(ctx, job)
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
// Job hasn't reported finished status yet, wait more
|
||||
|
|
@ -421,7 +409,7 @@ func K8sHandleCheTLSSecrets(deployContext *DeployContext) (reconcile.Result, err
|
|||
// The secret is invalid because required field(s) missing.
|
||||
logrus.Infof("Che TLS secret \"%s\" is invalid. Recreating...", cheTLSSecretName)
|
||||
// Delete old invalid secret
|
||||
if err = deployContext.ClusterAPI.Client.Delete(context.TODO(), cheTLSSecret); err != nil {
|
||||
if err = ctx.ClusterAPI.Client.Delete(context.TODO(), cheTLSSecret); err != nil {
|
||||
logrus.Errorf("Error deleting Che TLS secret \"%s\": %v", cheTLSSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
|
|
@ -432,11 +420,11 @@ func K8sHandleCheTLSSecrets(deployContext *DeployContext) (reconcile.Result, err
|
|||
// Check owner reference
|
||||
if cheTLSSecret.ObjectMeta.OwnerReferences == nil {
|
||||
// Set owner Che cluster as Che TLS secret owner
|
||||
if err := controllerutil.SetControllerReference(deployContext.CheCluster, cheTLSSecret, deployContext.ClusterAPI.Scheme); err != nil {
|
||||
if err := controllerutil.SetControllerReference(ctx.CheCluster, cheTLSSecret, ctx.ClusterAPI.Scheme); err != nil {
|
||||
logrus.Errorf("Failed to set owner for Che TLS secret \"%s\". Error: %s", cheTLSSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
if err := deployContext.ClusterAPI.Client.Update(context.TODO(), cheTLSSecret); err != nil {
|
||||
if err := ctx.ClusterAPI.Client.Update(context.TODO(), cheTLSSecret); err != nil {
|
||||
logrus.Errorf("Failed to update owner for Che TLS secret \"%s\". Error: %s", cheTLSSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
|
|
@ -445,11 +433,11 @@ func K8sHandleCheTLSSecrets(deployContext *DeployContext) (reconcile.Result, err
|
|||
// ===== Check Che CA certificate ===== //
|
||||
|
||||
cheTLSSelfSignedCertificateSecret := &corev1.Secret{}
|
||||
err = deployContext.ClusterAPI.Client.Get(context.TODO(), CheTLSSelfSignedCertificateSecretNamespacedName, cheTLSSelfSignedCertificateSecret)
|
||||
err = ctx.ClusterAPI.Client.Get(context.TODO(), CheTLSSelfSignedCertificateSecretNamespacedName, cheTLSSelfSignedCertificateSecret)
|
||||
if err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
// Error reading Che self-signed secret info
|
||||
logrus.Errorf("Error getting Che self-signed certificate secert \"%s\": %v", CheTLSSelfSignedCertificateSecretName, err)
|
||||
logrus.Errorf("Error getting Che self-signed certificate secert \"%s\": %v", deploy.CheTLSSelfSignedCertificateSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
// Che CA self-signed cetificate secret doesn't exist.
|
||||
|
|
@ -457,15 +445,15 @@ func K8sHandleCheTLSSecrets(deployContext *DeployContext) (reconcile.Result, err
|
|||
} else {
|
||||
// Che CA self-signed certificate secret exists, check for required data fields
|
||||
if !isCheCASecretValid(cheTLSSelfSignedCertificateSecret) {
|
||||
logrus.Infof("Che self-signed certificate secret \"%s\" is invalid. Recrating...", CheTLSSelfSignedCertificateSecretName)
|
||||
logrus.Infof("Che self-signed certificate secret \"%s\" is invalid. Recrating...", deploy.CheTLSSelfSignedCertificateSecretName)
|
||||
// Che CA self-signed certificate secret is invalid, delete it
|
||||
if err = deployContext.ClusterAPI.Client.Delete(context.TODO(), cheTLSSelfSignedCertificateSecret); err != nil {
|
||||
logrus.Errorf("Error deleting Che self-signed certificate secret \"%s\": %v", CheTLSSelfSignedCertificateSecretName, err)
|
||||
if err = ctx.ClusterAPI.Client.Delete(context.TODO(), cheTLSSelfSignedCertificateSecret); err != nil {
|
||||
logrus.Errorf("Error deleting Che self-signed certificate secret \"%s\": %v", deploy.CheTLSSelfSignedCertificateSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
// Also delete Che TLS as the certificates should be created together
|
||||
// Here it is not mandatory to check Che TLS secret existence as it is handled above
|
||||
if err = deployContext.ClusterAPI.Client.Delete(context.TODO(), cheTLSSecret); err != nil {
|
||||
if err = ctx.ClusterAPI.Client.Delete(context.TODO(), cheTLSSecret); err != nil {
|
||||
logrus.Errorf("Error deleting Che TLS secret \"%s\": %v", cheTLSSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
|
|
@ -476,12 +464,12 @@ func K8sHandleCheTLSSecrets(deployContext *DeployContext) (reconcile.Result, err
|
|||
// Check owner reference
|
||||
if cheTLSSelfSignedCertificateSecret.ObjectMeta.OwnerReferences == nil {
|
||||
// Set owner Che cluster as Che TLS secret owner
|
||||
if err := controllerutil.SetControllerReference(deployContext.CheCluster, cheTLSSelfSignedCertificateSecret, deployContext.ClusterAPI.Scheme); err != nil {
|
||||
logrus.Errorf("Failed to set owner for Che self-signed certificate secret \"%s\". Error: %s", CheTLSSelfSignedCertificateSecretName, err)
|
||||
if err := controllerutil.SetControllerReference(ctx.CheCluster, cheTLSSelfSignedCertificateSecret, ctx.ClusterAPI.Scheme); err != nil {
|
||||
logrus.Errorf("Failed to set owner for Che self-signed certificate secret \"%s\". Error: %s", deploy.CheTLSSelfSignedCertificateSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
if err := deployContext.ClusterAPI.Client.Update(context.TODO(), cheTLSSelfSignedCertificateSecret); err != nil {
|
||||
logrus.Errorf("Failed to update owner for Che self-signed certificate secret \"%s\". Error: %s", CheTLSSelfSignedCertificateSecretName, err)
|
||||
if err := ctx.ClusterAPI.Client.Update(context.TODO(), cheTLSSelfSignedCertificateSecret); err != nil {
|
||||
logrus.Errorf("Failed to update owner for Che self-signed certificate secret \"%s\". Error: %s", deploy.CheTLSSelfSignedCertificateSecretName, err)
|
||||
return reconcile.Result{RequeueAfter: time.Second}, err
|
||||
}
|
||||
}
|
||||
|
|
@ -508,105 +496,31 @@ func isCheCASecretValid(cheCASelfSignedCertificateSecret *corev1.Secret) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func deleteJob(deployContext *DeployContext, job *batchv1.Job) {
|
||||
names := util.K8sclient.GetPodsByComponent(CheTLSJobComponentName, deployContext.CheCluster.Namespace)
|
||||
func deleteJob(ctx *deploy.DeployContext, job *batchv1.Job) {
|
||||
names := util.K8sclient.GetPodsByComponent(CheTLSJobComponentName, ctx.CheCluster.Namespace)
|
||||
for _, podName := range names {
|
||||
pod := &corev1.Pod{}
|
||||
err := deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: podName, Namespace: deployContext.CheCluster.Namespace}, pod)
|
||||
err := ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: podName, Namespace: ctx.CheCluster.Namespace}, pod)
|
||||
if err == nil {
|
||||
// Delete pod (for some reasons pod isn't removed when job is removed)
|
||||
if err = deployContext.ClusterAPI.Client.Delete(context.TODO(), pod); err != nil {
|
||||
if err = ctx.ClusterAPI.Client.Delete(context.TODO(), pod); err != nil {
|
||||
logrus.Errorf("Error deleting pod: '%s', error: %v", podName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := deployContext.ClusterAPI.Client.Delete(context.TODO(), job); err != nil {
|
||||
if err := ctx.ClusterAPI.Client.Delete(context.TODO(), job); err != nil {
|
||||
logrus.Errorf("Error deleting job: '%s', error: %v", CheTLSJobName, err)
|
||||
}
|
||||
}
|
||||
|
||||
// SyncAdditionalCACertsConfigMapToCluster makes sure that additional CA certs config map is up to date if any
|
||||
func SyncAdditionalCACertsConfigMapToCluster(deployContext *DeployContext) (bool, error) {
|
||||
cr := deployContext.CheCluster
|
||||
// Get all source config maps, if any
|
||||
caConfigMaps, err := GetCACertsConfigMaps(deployContext.ClusterAPI.Client, deployContext.CheCluster.GetNamespace())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(cr.Spec.Server.ServerTrustStoreConfigMapName) > 0 {
|
||||
crConfigMap := &corev1.ConfigMap{}
|
||||
err := deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: deployContext.CheCluster.Namespace, Name: cr.Spec.Server.ServerTrustStoreConfigMapName}, crConfigMap)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
caConfigMaps = append(caConfigMaps, *crConfigMap)
|
||||
}
|
||||
|
||||
mergedCAConfigMap := &corev1.ConfigMap{}
|
||||
err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: deployContext.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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
caConfigMapsCachedRevisions[nameRevision[0]] = nameRevision[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(caConfigMapsCurrentRevisions, caConfigMapsCachedRevisions) {
|
||||
// Existing merged config map is up to date, do nothing
|
||||
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
|
||||
}
|
||||
|
||||
// Save source config map revision
|
||||
if revisions != "" {
|
||||
revisions += labelCommaSign
|
||||
}
|
||||
revisions += cm.ObjectMeta.Name + labelEqualSign + cm.ObjectMeta.ResourceVersion
|
||||
}
|
||||
|
||||
mergedCAConfigMapSpec := GetConfigMapSpec(deployContext, CheAllCACertsConfigMapName, data, DefaultCheFlavor(cr))
|
||||
mergedCAConfigMapSpec.ObjectMeta.Labels[KubernetesPartOfLabelKey] = CheEclipseOrg
|
||||
mergedCAConfigMapSpec.ObjectMeta.Annotations[CheMergedCAConfigMapRevisionsAnnotationKey] = revisions
|
||||
return SyncConfigMapSpecToCluster(deployContext, mergedCAConfigMapSpec)
|
||||
}
|
||||
|
||||
// GetCACertsConfigMaps 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) {
|
||||
CACertsConfigMapList := &corev1.ConfigMapList{}
|
||||
|
||||
caBundleLabelSelectorRequirement, _ := labels.NewRequirement(KubernetesComponentLabelKey, selection.Equals, []string{CheCACertsConfigMapLabelValue})
|
||||
cheComponetLabelSelectorRequirement, _ := labels.NewRequirement(KubernetesPartOfLabelKey, selection.Equals, []string{CheEclipseOrg})
|
||||
caBundleLabelSelectorRequirement, _ := labels.NewRequirement(deploy.KubernetesComponentLabelKey, selection.Equals, []string{CheCACertsConfigMapLabelValue})
|
||||
cheComponetLabelSelectorRequirement, _ := labels.NewRequirement(deploy.KubernetesPartOfLabelKey, selection.Equals, []string{deploy.CheEclipseOrg})
|
||||
listOptions := &k8sclient.ListOptions{
|
||||
LabelSelector: labels.NewSelector().Add(*cheComponetLabelSelectorRequirement).Add(*caBundleLabelSelectorRequirement),
|
||||
Namespace: namespace,
|
||||
|
|
@ -619,12 +533,52 @@ func GetCACertsConfigMaps(client k8sclient.Client, namespace string) ([]corev1.C
|
|||
}
|
||||
|
||||
// GetAdditionalCACertsConfigMapVersion returns revision of merged additional CA certs config map
|
||||
func GetAdditionalCACertsConfigMapVersion(deployContext *DeployContext) string {
|
||||
func GetAdditionalCACertsConfigMapVersion(ctx *deploy.DeployContext) string {
|
||||
trustStoreConfigMap := &corev1.ConfigMap{}
|
||||
exists, _ := GetNamespacedObject(deployContext, CheAllCACertsConfigMapName, trustStoreConfigMap)
|
||||
exists, _ := deploy.GetNamespacedObject(ctx, CheAllCACertsConfigMapName, trustStoreConfigMap)
|
||||
if exists {
|
||||
return trustStoreConfigMap.ResourceVersion
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// CreateTLSSecretFromEndpoint creates TLS secret with given name which contains certificates obtained from the given url.
|
||||
// If the url is empty string, then cluster default certificate will be obtained.
|
||||
// Does nothing if secret with given name already exists.
|
||||
func CreateTLSSecretFromEndpoint(ctx *deploy.DeployContext, url string, name string) (err error) {
|
||||
secret := &corev1.Secret{}
|
||||
if err := ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: ctx.CheCluster.Namespace}, secret); err != nil && errors.IsNotFound(err) {
|
||||
crtBytes, err := GetEndpointTLSCrtBytes(ctx, url)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to extract certificate for secret %s. Failed to create a secret with a self signed crt: %s", name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = deploy.SyncSecretToCluster(ctx, name, ctx.CheCluster.Namespace, map[string][]byte{"ca.crt": crtBytes})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func SyncTLSRoleToCluster(ctx *deploy.DeployContext) (bool, error) {
|
||||
tlsPolicyRule := []rbac.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{
|
||||
"",
|
||||
},
|
||||
Resources: []string{
|
||||
"secrets",
|
||||
},
|
||||
Verbs: []string{
|
||||
"create",
|
||||
"get",
|
||||
"patch",
|
||||
},
|
||||
},
|
||||
}
|
||||
return deploy.SyncRoleToCluster(ctx, CheTLSJobRoleName, tlsPolicyRule)
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019-2021 Red Hat, Inc.
|
||||
// This program and the accompanying materials are made
|
||||
// available under the terms of the Eclipse Public License 2.0
|
||||
// which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0
|
||||
//
|
||||
// Contributors:
|
||||
// Red Hat, Inc. - initial API and implementation
|
||||
//
|
||||
|
||||
package deploy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
orgv1 "github.com/eclipse-che/che-operator/api/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
)
|
||||
|
||||
func TestSyncAdditionalCACertsConfigMapToCluster(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{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "cert2",
|
||||
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"},
|
||||
}
|
||||
|
||||
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
|
||||
corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
|
||||
cli := fake.NewFakeClientWithScheme(scheme.Scheme, cert1)
|
||||
deployContext := &DeployContext{
|
||||
CheCluster: &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "eclipse-che",
|
||||
Name: "eclipse-che",
|
||||
},
|
||||
},
|
||||
ClusterAPI: ClusterAPI{
|
||||
Client: cli,
|
||||
NonCachingClient: cli,
|
||||
Scheme: scheme.Scheme,
|
||||
},
|
||||
}
|
||||
|
||||
// check ca-cert-merged
|
||||
done, err := SyncAdditionalCACertsConfigMapToCluster(deployContext)
|
||||
if !done || err != nil {
|
||||
t.Fatalf("Failed to sync config map: %v", err)
|
||||
}
|
||||
|
||||
cacertMerged := &corev1.ConfigMap{}
|
||||
err = cli.Get(context.TODO(), types.NamespacedName{Name: CheAllCACertsConfigMapName, Namespace: "eclipse-che"}, cacertMerged)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get config map: %v", err)
|
||||
}
|
||||
if cacertMerged.ObjectMeta.Annotations["che.eclipse.org/included-configmaps"] != "cert1-1" {
|
||||
t.Fatalf("Failed to sync config map")
|
||||
}
|
||||
|
||||
// let's create another configmap
|
||||
err = cli.Create(context.TODO(), cert2)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create config map: %v", err)
|
||||
}
|
||||
|
||||
// check ca-cert-merged
|
||||
_, err = SyncAdditionalCACertsConfigMapToCluster(deployContext)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to sync config map: %v", err)
|
||||
}
|
||||
|
||||
// sync twice to be sure update done correctly
|
||||
done, err = SyncAdditionalCACertsConfigMapToCluster(deployContext)
|
||||
if !done || err != nil {
|
||||
t.Fatalf("Failed to sync config map: %v", err)
|
||||
}
|
||||
|
||||
err = cli.Get(context.TODO(), types.NamespacedName{Name: CheAllCACertsConfigMapName, Namespace: "eclipse-che"}, cacertMerged)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get config map: %v", err)
|
||||
}
|
||||
if cacertMerged.ObjectMeta.Annotations["che.eclipse.org/included-configmaps"] != "cert1-1.cert2-1" {
|
||||
t.Fatalf("Failed to sync config map")
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue