From 63787372bd54e5914c9f67235b941fd575d98bd4 Mon Sep 17 00:00:00 2001 From: Eugene Ivantsov Date: Fri, 19 Apr 2019 14:40:49 +0300 Subject: [PATCH] Get crt of OpenShift baseURL for Keycloak (#14) * Get crt of OpenShift baseURL for Keycloak * Don't fails when rolling out router deployment --- e2e/run-okd-tests.sh | 4 +-- pkg/controller/che/che_controller.go | 24 ++++++------- pkg/controller/che/create.go | 23 +++++++++++- pkg/controller/che/k8s_helpers.go | 31 +++++++++------- pkg/deploy/deployment_keycloak.go | 53 +++++++++++++++++++++++----- pkg/util/util.go | 2 +- 6 files changed, 100 insertions(+), 37 deletions(-) diff --git a/e2e/run-okd-tests.sh b/e2e/run-okd-tests.sh index 495b9e89f..a1f371305 100755 --- a/e2e/run-okd-tests.sh +++ b/e2e/run-okd-tests.sh @@ -54,8 +54,8 @@ echo "[INFO] Update OpenShift router tls secret" ./oc project default ./oc secrets new router-certs tls.crt=ca.crt tls.key=key.pem -o json --type='kubernetes.io/tls' --confirm | ./oc replace -f - echo "[INFO] Initiate a new router deployment" -sleep 10 -./oc rollout latest dc/router -n=default +sleep 20 +./oc rollout latest dc/router -n=default || true echo "[INFO] Compile tests binary" docker run -t \ diff --git a/pkg/controller/che/che_controller.go b/pkg/controller/che/che_controller.go index 7f34165ba..de80e751c 100644 --- a/pkg/controller/che/che_controller.go +++ b/pkg/controller/che/che_controller.go @@ -215,25 +215,25 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e return reconcile.Result{}, err } } - // create a secret with router tls cert when on OpenShift infra and router is configured with a self signed certificate selfSignedCert := instance.Spec.Server.SelfSignedCert if isOpenShift && selfSignedCert { - secret := &corev1.Secret{} - if err := r.client.Get(context.TODO(), types.NamespacedName{Name: "self-signed-certificate", Namespace: instance.Namespace}, secret); - err != nil && errors.IsNotFound(err) { - crt, err := r.GetRouterTlsCrt(instance) - if err != nil { - logrus.Errorf("Default router tls secret not found. Self signed cert isn't added") + if err := r.CreateTLSSecret(instance, "", "self-signed-certificate"); err != nil { + return reconcile.Result{}, err + } + } + // create a secret with OpenShift API crt to be added to keystore that RH SSO will consume + if isOpenShift { + baseURL, err := util.GetClusterPublicHostname() + 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 { + if err := r.CreateTLSSecret(instance, baseURL, "openshift-api-crt"); err != nil { return reconcile.Result{}, err - } else { - secret := deploy.NewSecret(instance, "self-signed-certificate", crt) - if err := r.CreateNewSecret(instance, secret); err != nil { - return reconcile.Result{}, err - } } } } + if !tests { deployment := &appsv1.Deployment{} name := "che" diff --git a/pkg/controller/che/create.go b/pkg/controller/che/create.go index e3a71977e..ebdbc7fc3 100644 --- a/pkg/controller/che/create.go +++ b/pkg/controller/che/create.go @@ -318,6 +318,28 @@ func (r *ReconcileChe) CreateIdentityProviderItems(instance *orgv1.CheCluster, r return nil } +func (r *ReconcileChe) CreateTLSSecret(instance *orgv1.CheCluster, url string, name string) (err error) { + // create a secret with either router tls cert (or OpenShift API crt) when on OpenShift infra + // and router is configured with a self signed certificate + // this secret is used by CRW server to reach RH SSO TLS endpoint + secret := &corev1.Secret{} + if err := r.client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: instance.Namespace}, secret); + err != nil && errors.IsNotFound(err) { + crt, err := r.GetEndpointTlsCrt(instance, url) + if err != nil { + logrus.Errorf("Failed to extract crt. Failed to create a secret with a self signed crt: %s", err) + return err + } else { + secret := deploy.NewSecret(instance, name, crt) + if err := r.CreateNewSecret(instance, secret); err != nil { + return err + } + } + } + + return nil +} + func (r *ReconcileChe) GenerateAndSaveFields(instance *orgv1.CheCluster, request reconcile.Request) (err error) { chePostgresPassword := util.GetValue(instance.Spec.Database.ChePostgresPassword, util.GeneratePasswd(12)) @@ -445,7 +467,6 @@ func (r *ReconcileChe) GenerateAndSaveFields(instance *orgv1.CheCluster, request if len(instance.Spec.Auth.KeycloakClientId) < 1 { instance.Spec.Auth.KeycloakClientId = keycloakClientId - if err := r.UpdateCheCRSpec(instance, "Keycloak client ID", keycloakClientId); err != nil { return err } diff --git a/pkg/controller/che/k8s_helpers.go b/pkg/controller/che/k8s_helpers.go index 085fe4d2c..722d5dbd1 100644 --- a/pkg/controller/che/k8s_helpers.go +++ b/pkg/controller/che/k8s_helpers.go @@ -19,6 +19,7 @@ import ( orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1" "github.com/eclipse/che-operator/pkg/deploy" "github.com/eclipse/che-operator/pkg/util" + routev1 "github.com/openshift/api/route/v1" "github.com/sirupsen/logrus" "io" appsv1 "k8s.io/api/apps/v1" @@ -251,17 +252,21 @@ func (cl *k8s) GetDeploymentPod(name string, ns string) (podName string, err err return podName, nil } -// GetRouterTlsCrt creates a test TLS route and gets it to extract certificate chain +// GetEndpointTlsCrt creates a test TLS route and gets it to extract certificate chain // 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 (r *ReconcileChe) GetRouterTlsCrt(instance *orgv1.CheCluster) (certificate []byte, err error) { - testRoute := deploy.NewTlsRoute(instance, "test", "test") - logrus.Infof("Creating a test route %s to extract routes crt", testRoute.Name) - if err := r.CreateNewRoute(instance, testRoute); err != nil { - logrus.Errorf("Failed to create test route %s: %s", testRoute.Name, err) - return nil, err +func (r *ReconcileChe) GetEndpointTlsCrt(instance *orgv1.CheCluster, url string) (certificate []byte, err error) { + testRoute := &routev1.Route{} + if len(url) < 1 { + testRoute = deploy.NewTlsRoute(instance, "test", "test") + logrus.Infof("Creating a test route %s to extract routes crt", testRoute.Name) + if err := r.CreateNewRoute(instance, testRoute); err != nil { + logrus.Errorf("Failed to create test route %s: %s", testRoute.Name, err) + return nil, err + } + url = "https://" + testRoute.Spec.Host + } - url := "https://" + testRoute.Spec.Host http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} client := &http.Client{} req, err := http.NewRequest("GET", url, nil) @@ -283,10 +288,12 @@ func (r *ReconcileChe) GetRouterTlsCrt(instance *orgv1.CheCluster) (certificate }) certificate = append(certificate, crt...) } - logrus.Infof("Deleting a test route %s to extract routes crt", testRoute.Name) - if err := r.client.Delete(context.TODO(), testRoute); err != nil { - logrus.Errorf("Failed to delete test route %s: %s", testRoute.Name, err) - } + if len(url) < 1 { + logrus.Infof("Deleting a test route %s to extract routes crt", testRoute.Name) + if err := r.client.Delete(context.TODO(), testRoute); err != nil { + logrus.Errorf("Failed to delete test route %s: %s", testRoute.Name, err) + } + } return certificate, nil } diff --git a/pkg/deploy/deployment_keycloak.go b/pkg/deploy/deployment_keycloak.go index eb58d1047..39ecf1127 100644 --- a/pkg/deploy/deployment_keycloak.go +++ b/pkg/deploy/deployment_keycloak.go @@ -32,17 +32,28 @@ func NewKeycloakDeployment(cr *orgv1.CheCluster, keycloakPostgresPassword string // writable dir in the upstream Keycloak image jbossDir = "/scripts" } - // add crt to Java trust store so that Keycloak can connect to k8s API - addCertToTrustStoreCommand := "if [ ! -z \"${CHE_SELF__SIGNED__CERT}\" ]; then echo \"${CHE_SELF__SIGNED__CERT}\" > " + jbossDir + "/openshift.crt && " + - "keytool -importcert -alias HOSTDOMAIN0" + - " -keystore " + jbossDir +"/openshift.jks" + - " -file " + jbossDir + "/openshift.crt -storepass " + trustpass + " -noprompt; fi" + - " && keytool -importcert -alias HOSTDOMAIN" + - " -keystore " + jbossDir +"/openshift.jks" + - " -file /var/run/secrets/kubernetes.io/serviceaccount/ca.crt -storepass " + trustpass + " -noprompt" + - " && keytool -importkeystore -srckeystore $JAVA_HOME/jre/lib/security/cacerts" + + + // add various certificates to Java trust store so that Keycloak can connect to OpenShift API + // certificate that OpenShift router uses (for 4.0 only) + addRouterCrt := "if [ ! -z \"${CHE_SELF__SIGNED__CERT}\" ]; then echo \"${CHE_SELF__SIGNED__CERT}\" > " + jbossDir + "/openshift.crt && " + + "keytool -importcert -alias ROUTERCRT" + + " -keystore " + jbossDir + "/openshift.jks" + + " -file " + jbossDir + "/openshift.crt -storepass " + trustpass + " -noprompt; fi" + // certificate retrieved from http call to OpenShift API endpoint + addOpenShiftAPICrt := "if [ ! -z \"${OPENSHIFT_SELF__SIGNED__CERT}\" ]; then echo \"${OPENSHIFT_SELF__SIGNED__CERT}\" > " + jbossDir + "/openshift.crt && " + + "keytool -importcert -alias OPENSHIFTAPI" + + " -keystore " + jbossDir + "/openshift.jks" + + " -file " + jbossDir + "/openshift.crt -storepass " + trustpass + " -noprompt; fi" + // certificate mounted into container /var/run/secrets + addMountedCrt := " keytool -importcert -alias MOUNTEDCRT" + + " -keystore " + jbossDir + "/openshift.jks" + + " -file /var/run/secrets/kubernetes.io/serviceaccount/ca.crt -storepass " + trustpass + " -noprompt" + importJavaCacerts := "keytool -importkeystore -srckeystore $JAVA_HOME/jre/lib/security/cacerts" + " -destkeystore " + jbossDir + "/openshift.jks" + " -srcstorepass changeit -deststorepass " + trustpass + + addCertToTrustStoreCommand := addRouterCrt + " && " + addOpenShiftAPICrt + " && " + addMountedCrt + " && " + importJavaCacerts + startCommand := "sed -i 's/WILDCARD/ANY/g' /opt/eap/bin/launch/keycloak-spi.sh && /opt/eap/bin/openshift-launch.sh -b 0.0.0.0" // upstream Keycloak has a bit different mechanism of adding jks changeConfigCommand := "echo -e \"embed-server --server-config=standalone.xml --std-out=echo \n" + @@ -101,6 +112,18 @@ func NewKeycloakDeployment(cr *orgv1.CheCluster, keycloakPostgresPassword string }, }, }, + { + Name: "OPENSHIFT_SELF__SIGNED__CERT", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + Key: "ca.crt", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "openshift-api-crt", + }, + Optional: &optionalEnv, + }, + }, + }, } if cheFlavor == "codeready" { keycloakEnv = []corev1.EnvVar{ @@ -168,6 +191,18 @@ func NewKeycloakDeployment(cr *orgv1.CheCluster, keycloakPostgresPassword string }, }, }, + { + Name: "OPENSHIFT_SELF__SIGNED__CERT", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + Key: "ca.crt", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "openshift-api-crt", + }, + Optional: &optionalEnv, + }, + }, + }, } } command := addCertToTrustStoreCommand + " && " + changeConfigCommand + " && /opt/jboss/docker-entrypoint.sh -b 0.0.0.0" diff --git a/pkg/util/util.go b/pkg/util/util.go index 6c3ee953f..815484663 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -110,7 +110,7 @@ func GetClusterPublicHostname() (hostname string, err error) { client := &http.Client{} kubeApi := os.Getenv("KUBERNETES_PORT_443_TCP_ADDR") url := "https://" + kubeApi + "/.well-known/oauth-authorization-server" - req, err := http.NewRequest("GET", url, nil) + req, err := http.NewRequest(http.MethodGet, url, nil) resp, err := client.Do(req) if err != nil { logrus.Errorf("An error occurred when getting API public hostname: %s", err)