From ff9d41a31fbffc9525f910cd03f5088b4d04f8c2 Mon Sep 17 00:00:00 2001 From: Mykola Morhun Date: Wed, 6 May 2020 17:02:48 +0300 Subject: [PATCH 1/4] Retrieve autogenerated Keycloak admin user credentials after Che deployment finish Signed-off-by: Mykola Morhun --- src/api/che.ts | 31 ++++++++++++++++++- src/commands/server/start.ts | 3 +- .../component-installers/cert-manager.ts | 3 +- src/tasks/installers/common-tasks.ts | 18 +++++++++++ src/util.ts | 4 +++ 5 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/api/che.ts b/src/api/che.ts index e62fa834a..73dca442c 100644 --- a/src/api/che.ts +++ b/src/api/che.ts @@ -22,6 +22,7 @@ import * as path from 'path' import { OpenShiftHelper } from '../api/openshift' import { CHE_ROOT_CA_SECRET_NAME, DEFAULT_CA_CERT_FILE_NAME } from '../constants' +import { base64Decode } from '../util' import { Devfile } from './devfile' import { KubeHelper } from './kube' @@ -119,7 +120,7 @@ export class CheHelper { } if (cheCaSecret.data && cheCaSecret.data['ca.crt']) { - return Buffer.from(cheCaSecret.data['ca.crt'], 'base64').toString('ascii') + return base64Decode(cheCaSecret.data['ca.crt']) } throw new Error(`Secret "${CHE_ROOT_CA_SECRET_NAME}" has invalid format: "ca.crt" key not found in data.`) @@ -139,6 +140,34 @@ export class CheHelper { return destinaton } + /** + * Retreives Keycloak admin user credentials. + * Works only with installers which use Che CR (operator, olm). + * Returns credentials as an array of two values: [login, password] + * In case of an error an array with undefined values will be returned. + */ + async retrieveKeycloakAdminCredentials(cheNamespace: string): Promise { + let adminUsername + let adminPassword + + const cheCluster = await this.kube.getCheCluster('eclipse-che', cheNamespace) + const keycloakCredentialsSecretName = cheCluster.spec.auth.identityProviderSecret + if (keycloakCredentialsSecretName) { + // Keycloak credentials are sotored in secret + const keycloakCredentialsSecret = await this.kube.getSecret(keycloakCredentialsSecretName, cheNamespace) + if (keycloakCredentialsSecret && keycloakCredentialsSecret.data) { + adminUsername = base64Decode(keycloakCredentialsSecret.data.user) + adminPassword = base64Decode(keycloakCredentialsSecret.data.password) + } + } else { + // Keycloak credentials are sotored in Che custom resource + adminUsername = cheCluster.spec.auth.identityProviderAdminUserName + adminPassword = cheCluster.spec.auth.identityProviderPassword + } + + return [adminUsername, adminPassword] + } + async cheK8sURL(namespace = ''): Promise { const ingress_names = ['che', 'che-ingress'] for (const ingress_name of ingress_names) { diff --git a/src/commands/server/start.ts b/src/commands/server/start.ts index 9b4f16059..6611ad512 100644 --- a/src/commands/server/start.ts +++ b/src/commands/server/start.ts @@ -20,7 +20,7 @@ import * as path from 'path' import { cheDeployment, cheNamespace, listrRenderer, skipKubeHealthzCheck as skipK8sHealthCheck } from '../../common-flags' import { DEFAULT_CHE_IMAGE, DEFAULT_CHE_OPERATOR_IMAGE, DOCS_LINK_INSTALL_TLS_WITH_SELF_SIGNED_CERT } from '../../constants' import { CheTasks } from '../../tasks/che' -import { retrieveCheCaCertificateTask } from '../../tasks/installers/common-tasks' +import { getRetrieveKeycloakCredentialsTask, retrieveCheCaCertificateTask } from '../../tasks/installers/common-tasks' import { InstallerTasks } from '../../tasks/installers/installer' import { ApiTasks } from '../../tasks/platforms/api' import { PlatformTasks } from '../../tasks/platforms/platform' @@ -351,6 +351,7 @@ export default class Start extends Command { title: '✅ Post installation checklist', task: () => new Listr(cheTasks.waitDeployedChe(flags, this)) }, + getRetrieveKeycloakCredentialsTask(flags), retrieveCheCaCertificateTask(flags), { title: 'Show important messages', diff --git a/src/tasks/component-installers/cert-manager.ts b/src/tasks/component-installers/cert-manager.ts index 03b121871..710ba1d04 100644 --- a/src/tasks/component-installers/cert-manager.ts +++ b/src/tasks/component-installers/cert-manager.ts @@ -14,6 +14,7 @@ import * as path from 'path' import { CheHelper } from '../../api/che' import { KubeHelper } from '../../api/kube' import { CA_CERT_GENERATION_JOB_IMAGE, CERT_MANAGER_NAMESPACE_NAME, CHE_TLS_SECRET_NAME } from '../../constants' +import { base64Decode } from '../../util' import { getMessageImportCaCertIntoBrowser } from '../installers/common-tasks' export const CERT_MANAGER_CA_SECRET_NAME = 'ca' @@ -167,7 +168,7 @@ export class CertManagerTasks { task: async (ctx: any, task: any) => { const cheSecret = await this.kubeHelper.getSecret(CHE_TLS_SECRET_NAME, flags.chenamespace) if (cheSecret && cheSecret.data) { - const cheCaCrt = Buffer.from(cheSecret.data['ca.crt'], 'base64').toString('ascii') + const cheCaCrt = base64Decode(cheSecret.data['ca.crt']) const cheCaCertPath = await this.cheHelper.saveCheCaCert(cheCaCrt) ctx.highlightedMessages.push(getMessageImportCaCertIntoBrowser(cheCaCertPath)) diff --git a/src/tasks/installers/common-tasks.ts b/src/tasks/installers/common-tasks.ts index 11f8fc212..f2f2cf968 100644 --- a/src/tasks/installers/common-tasks.ts +++ b/src/tasks/installers/common-tasks.ts @@ -154,3 +154,21 @@ export function getMessageImportCaCertIntoBrowser(caCertFileLocation: string): s `Documentaton how to add a CA certificate into a browser: ${DOCS_LINK_IMPORT_CA_CERT_INTO_BROWSER}` return message } + +export function getRetrieveKeycloakCredentialsTask(flags: any): ListrTask { + return { + title: 'Retrieving Keycloak admin credentials', + enabled: () => flags.multiuser && (flags.installer !== 'operator' || flags.installer !== 'olm'), + task: async (ctx: any, task: any) => { + const che = new CheHelper(flags) + const [login, password] = await che.retrieveKeycloakAdminCredentials(flags.chenamespace) + if (login && password) { + ctx.highlightedMessages.push(`Autogenerated Keycloak credentials are: "${login}:${password}".`) + + task.title = `${task.title }... ${login}:${password}` + } else { + task.title = `${task.title }... Failed.` + } + } + } +} diff --git a/src/util.ts b/src/util.ts index 2c64404ad..3b7e8fa46 100644 --- a/src/util.ts +++ b/src/util.ts @@ -60,3 +60,7 @@ export function generatePassword(passwodLength: number, charactersSet = '') { } return generatedPassword } + +export function base64Decode(arg: string): string { + return Buffer.from(arg, 'base64').toString('ascii') +} From e92aa8d445754fe655d9850f4b94e75d2bbec5c5 Mon Sep 17 00:00:00 2001 From: Mykola Morhun Date: Thu, 7 May 2020 18:06:54 +0300 Subject: [PATCH 2/4] Uodate yarn.lock to use newer operator CR Signed-off-by: Mykola Morhun --- yarn.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8732b7dee..21678351e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1521,11 +1521,11 @@ ecc-jsbn@~0.1.1: "eclipse-che-operator@git://github.com/eclipse/che-operator#master": version "0.0.0" - resolved "git://github.com/eclipse/che-operator#b0a62e0f4de376a7aa4e260ce1b1774bc32d2784" + resolved "git://github.com/eclipse/che-operator#0c671d8a117a333ee36756e5d3a4a75171d64b14" "eclipse-che@git://github.com/eclipse/che#master": version "0.0.0" - resolved "git://github.com/eclipse/che#6333d8951a949628e0c80bf61406efed6064ceca" + resolved "git://github.com/eclipse/che#0fd09abd02140545c49b28bce186512afeb04336" editorconfig@^0.15.0: version "0.15.3" From 5963a0febf83e9e3f459116baa4622295bf7cd4c Mon Sep 17 00:00:00 2001 From: Mykola Morhun Date: Thu, 7 May 2020 18:07:21 +0300 Subject: [PATCH 3/4] Fixes according to review Signed-off-by: Mykola Morhun --- src/api/che.ts | 8 ++++---- src/tasks/installers/common-tasks.ts | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/api/che.ts b/src/api/che.ts index 73dca442c..13436fd25 100644 --- a/src/api/che.ts +++ b/src/api/che.ts @@ -21,7 +21,7 @@ import * as os from 'os' import * as path from 'path' import { OpenShiftHelper } from '../api/openshift' -import { CHE_ROOT_CA_SECRET_NAME, DEFAULT_CA_CERT_FILE_NAME } from '../constants' +import { CHE_CLUSTER_CR_NAME, CHE_ROOT_CA_SECRET_NAME, DEFAULT_CA_CERT_FILE_NAME } from '../constants' import { base64Decode } from '../util' import { Devfile } from './devfile' @@ -150,17 +150,17 @@ export class CheHelper { let adminUsername let adminPassword - const cheCluster = await this.kube.getCheCluster('eclipse-che', cheNamespace) + const cheCluster = await this.kube.getCheCluster(CHE_CLUSTER_CR_NAME, cheNamespace) const keycloakCredentialsSecretName = cheCluster.spec.auth.identityProviderSecret if (keycloakCredentialsSecretName) { - // Keycloak credentials are sotored in secret + // Keycloak credentials are stored in secret const keycloakCredentialsSecret = await this.kube.getSecret(keycloakCredentialsSecretName, cheNamespace) if (keycloakCredentialsSecret && keycloakCredentialsSecret.data) { adminUsername = base64Decode(keycloakCredentialsSecret.data.user) adminPassword = base64Decode(keycloakCredentialsSecret.data.password) } } else { - // Keycloak credentials are sotored in Che custom resource + // Keycloak credentials are stored in Che custom resource adminUsername = cheCluster.spec.auth.identityProviderAdminUserName adminPassword = cheCluster.spec.auth.identityProviderPassword } diff --git a/src/tasks/installers/common-tasks.ts b/src/tasks/installers/common-tasks.ts index f2f2cf968..325126222 100644 --- a/src/tasks/installers/common-tasks.ts +++ b/src/tasks/installers/common-tasks.ts @@ -78,6 +78,7 @@ export function createEclipeCheCluster(flags: any, kube: KubeHelper): ListrTask const yamlFilePath = flags['che-operator-cr-yaml'] === '' ? ctx.resourcesPath + 'crds/org_v1_che_cr.yaml' : flags['che-operator-cr-yaml'] const cr = await kube.createCheClusterFromFile(yamlFilePath, flags, flags['che-operator-cr-yaml'] === '') + ctx.cr = cr ctx.isKeycloakReady = ctx.isKeycloakReady || cr.spec.auth.externalIdentityProvider ctx.isPostgresReady = ctx.isPostgresReady || cr.spec.database.externalDb ctx.isDevfileRegistryReady = ctx.isDevfileRegistryReady || cr.spec.server.externalDevfileRegistry @@ -158,7 +159,7 @@ export function getMessageImportCaCertIntoBrowser(caCertFileLocation: string): s export function getRetrieveKeycloakCredentialsTask(flags: any): ListrTask { return { title: 'Retrieving Keycloak admin credentials', - enabled: () => flags.multiuser && (flags.installer !== 'operator' || flags.installer !== 'olm'), + enabled: (ctx: any) => !ctx.cr.spec.auth.externalIdentityProvider && flags.multiuser && (flags.installer !== 'operator' || flags.installer !== 'olm'), task: async (ctx: any, task: any) => { const che = new CheHelper(flags) const [login, password] = await che.retrieveKeycloakAdminCredentials(flags.chenamespace) From 389137168ed66c01d25d1bda2198fa9fadeb31bc Mon Sep 17 00:00:00 2001 From: Mykola Morhun Date: Tue, 12 May 2020 17:25:55 +0300 Subject: [PATCH 4/4] Fix typo Signed-off-by: Mykola Morhun --- src/tasks/installers/common-tasks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/installers/common-tasks.ts b/src/tasks/installers/common-tasks.ts index 325126222..542a31973 100644 --- a/src/tasks/installers/common-tasks.ts +++ b/src/tasks/installers/common-tasks.ts @@ -152,7 +152,7 @@ export function getMessageImportCaCertIntoBrowser(caCertFileLocation: string): s const yellow = '\x1b[33m' const noColor = '\x1b[0m' const message = `❗${yellow}[MANUAL ACTION REQUIRED]${noColor} Please add Che self-signed CA certificate into your browser: ${caCertFileLocation}.\n` + - `Documentaton how to add a CA certificate into a browser: ${DOCS_LINK_IMPORT_CA_CERT_INTO_BROWSER}` + `Documentation how to add a CA certificate into a browser: ${DOCS_LINK_IMPORT_CA_CERT_INTO_BROWSER}` return message }