Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add cacert:export command to get self-signed Che CA certificate #669

Merged
merged 4 commits into from
Apr 27, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ USAGE
* [`chectl dashboard:open`](#chectl-dashboardopen)
* [`chectl devfile:generate`](#chectl-devfilegenerate)
* [`chectl help [COMMAND]`](#chectl-help-command)
* [`chectl server:certificate`](#chectl-servercertificate)
* [`chectl server:debug`](#chectl-serverdebug)
* [`chectl server:delete`](#chectl-serverdelete)
* [`chectl server:logs`](#chectl-serverlogs)
Expand Down Expand Up @@ -189,6 +190,40 @@ OPTIONS

_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v2.2.3/src/commands/help.ts)_

## `chectl server:certificate`

Retrieves Eclipse Che self-signed certificate

```
USAGE
$ chectl server:certificate

OPTIONS
-d, --destination=destination
[default: ~] Destination where to store Che certificate.
If the destination is a file (might not exist), then the certificate will be saved there in PEM
format.
If the destination is a directory, then cheCA.crt file will be created there with Che
certificate in PEM format.
If this option is ommited, then Che certificate will be stored in user's home directory as
cheCA.crt

-h, --help
show CLI help

-n, --chenamespace=chenamespace
[default: che] Kubernetes namespace where Eclipse Che server is supposed to be deployed

-p, --platform=minikube|minishift|k8s|openshift|microk8s|docker-desktop|crc
Type of Kubernetes platform. Valid values are "minikube", "minishift", "k8s (for kubernetes)", "openshift", "crc
(for CodeReady Containers)", "microk8s".

--make-path
Creates path specified in "destination" parameter if it doesn't exist.
```

_See code: [src/commands/server/certificate.ts](https://github.com/che-incubator/chectl/blob/v0.0.2/src/commands/server/certificate.ts)_

## `chectl server:debug`

Enable local debug of Eclipse Che server
Expand Down Expand Up @@ -322,6 +357,7 @@ OPTIONS
--catalog-source-yaml=catalog-source-yaml
Path to a yaml file that describes custom catalog source for installation Eclipse Che operator.
Catalog source will be applied to the namespace with Che operator.
Also you need define 'olm-channel' name and 'package-manifest-name'.
This parameter is used only when the installer is the 'olm'.

--che-operator-cr-patch-yaml=che-operator-cr-patch-yaml
Expand Down
105 changes: 105 additions & 0 deletions src/commands/server/certificate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*********************************************************************
* Copyright (c) 2020 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
**********************************************************************/

import { Command, flags } from '@oclif/command'
import { boolean, string } from '@oclif/parser/lib/flags'
import * as fs from 'fs'
import * as Listr from 'listr'
import * as os from 'os'
import * as path from 'path'

import { cheNamespace } from '../../common-flags'
import { CheTasks } from '../../tasks/che'
import { ApiTasks } from '../../tasks/platforms/api'
import { PlatformTasks } from '../../tasks/platforms/platform'

const DEFAULT_CA_CERT_FILE_NAME = 'cheCA.crt'

export default class Certificate extends Command {
static description = 'Retrieves Eclipse Che self-signed certificate'

static flags = {
help: flags.help({ char: 'h' }),
chenamespace: cheNamespace,
platform: string({
char: 'p',
description: 'Type of Kubernetes platform. Valid values are \"minikube\", \"minishift\", \"k8s (for kubernetes)\", \"openshift\", \"crc (for CodeReady Containers)\", \"microk8s\".',
options: ['minikube', 'minishift', 'k8s', 'openshift', 'microk8s', 'docker-desktop', 'crc'],
}),
destination: string({
char: 'd',
description: `Destination where to store Che certificate.
If the destination is a file (might not exist), then the certificate will be saved there in PEM format.
If the destination is a directory, then ${DEFAULT_CA_CERT_FILE_NAME} file will be created there with Che certificate in PEM format.
If this option is ommited, then Che certificate will be stored in user's home directory as ${DEFAULT_CA_CERT_FILE_NAME}`,
env: 'CHE_CA_CERT_LOCATION',
default: '~'
}),
'make-path': boolean({
mmorhun marked this conversation as resolved.
Show resolved Hide resolved
description: 'Creates path specified in "destination" parameter if it doesn\'t exist.',
default: false
}),
}

async run() {
const { flags } = this.parse(Certificate)
const ctx: any = {}
const platformTasks = new PlatformTasks()
const cheTasks = new CheTasks(flags)
const apiTasks = new ApiTasks()
const tasks = new Listr([], { renderer: 'silent' })

const targetFile = this.prepareTarget(flags.destination, flags['make-path'])
ctx.cheCaCertFile = targetFile

tasks.add(platformTasks.preflightCheckTasks(flags, this))
tasks.add(apiTasks.testApiTasks(flags, this))
tasks.add(cheTasks.verifyCheNamespaceExistsTask(flags, this))
tasks.add(cheTasks.retrieveEclipseCheCaCert(flags))

try {
await tasks.run(ctx)
this.log(`Eclipse Che self-signed CA certificate is exported to ${targetFile}`)
} catch (error) {
this.error(error)
}
}

/**
* Handles certificate target location and returns string which points to the target file.
*/
private prepareTarget(destinaton: string, makePath = false): string {
if (destinaton === '~') {
return path.join(os.homedir(), DEFAULT_CA_CERT_FILE_NAME)
}

if (fs.existsSync(destinaton)) {
return fs.lstatSync(destinaton).isDirectory() ? path.join(destinaton, DEFAULT_CA_CERT_FILE_NAME) : destinaton
}

const baseDirectory = path.dirname(destinaton)
if (fs.existsSync(baseDirectory)) {
return destinaton
}

if (makePath) {
if (destinaton.endsWith('/')) {
fs.mkdirSync(destinaton, { recursive: true })
return path.join(destinaton, DEFAULT_CA_CERT_FILE_NAME)
} else {
fs.mkdirSync(baseDirectory, { recursive: true })
return destinaton
}
} else {
throw new Error(`Base directory "${baseDirectory}" doesn't exist.`)
}
}

}
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const CA_CERT_GENERATION_JOB_IMAGE = 'quay.io/eclipse/che-cert-manager-ca

export const CERT_MANAGER_NAMESPACE_NAME = 'cert-manager'
export const CHE_TLS_SECRET_NAME = 'che-tls'
export const CHE_ROOT_CA_SECRET_NAME = 'self-signed-certificate'

export const operatorCheCluster = 'eclipse-che'
export const CHE_CLUSTER_CR_NAME = 'eclipse-che'
Expand Down
41 changes: 41 additions & 0 deletions src/tasks/che.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
import { Command } from '@oclif/command'
import * as fs from 'fs'
import * as Listr from 'listr'

import { CheHelper } from '../api/che'
import { KubeHelper } from '../api/kube'
import { OpenShiftHelper } from '../api/openshift'
import { CHE_ROOT_CA_SECRET_NAME } from '../constants'

import { KubeTasks } from './kube'

Expand Down Expand Up @@ -604,6 +606,45 @@ export class CheTasks {
]
}

/**
* Saves self-signed Che CA certificate into file. 'self-signed-certificate' secret should exist.
*/
retrieveEclipseCheCaCert(flags: any): ReadonlyArray<Listr.ListrTask> {
return [
{
title: 'Retrieving self-signed Eclipse Che CA certificate',
task: async (ctx: any, task: any) => {
const cheCaSecret = await this.kube.getSecret(CHE_ROOT_CA_SECRET_NAME, flags.chenamespace)
if (!cheCaSecret) {
throw new Error(`Secret "${CHE_ROOT_CA_SECRET_NAME}" not found.`)
}
if (cheCaSecret.data && cheCaSecret.data['ca.crt']) {
ctx.cheCaCert = Buffer.from(cheCaSecret.data['ca.crt'], 'base64').toString('ascii')
} else {
throw new Error(`Secret "${CHE_ROOT_CA_SECRET_NAME}" has invalid format.`)
}

task.title = `${task.title}... done`
}
},
{
title: 'Saving self-signed Eclipse Che CA certificate',
task: async (ctx: any, task: any) => {
if (!ctx.cheCaCert) {
throw new Error('Che CA certificate is not present in the context.')
}
if (!ctx.cheCaCertFile) {
throw new Error('Target file for Che CA certificate is not present in the context.')
}

fs.writeFileSync(ctx.cheCaCertFile, ctx.cheCaCert)

task.title = `Eclipse Che self-signed CA certificate is saved at ${ctx.cheCaCertFile}`
}
}
]
}

checkEclipseCheStatus(): ReadonlyArray<Listr.ListrTask> {
return [
{
Expand Down