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: Make cacert export save only CA certificates #859

Merged
merged 4 commits into from
Sep 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"listr-verbose-renderer": "^0.6.0",
"lodash": "^4.17.20",
"mkdirp": "^1.0.4",
"node-forge": "^0.10.0",
"node-notifier": "^8.0.0",
"stream-buffers": "^3.0.2",
"tslib": "^1"
Expand All @@ -51,6 +52,7 @@
"@types/js-yaml": "^3.12.5",
"@types/listr": "^0.14.2",
"@types/node": "^12",
"@types/node-forge": "^0.9.5",
"chai": "^4.2.0",
"cpx": "^1.5.0",
"globby": "^11",
Expand Down
30 changes: 30 additions & 0 deletions src/api/che.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import * as commandExists from 'command-exists'
import * as fs from 'fs-extra'
import * as https from 'https'
import * as yaml from 'js-yaml'
import * as nodeforge from 'node-forge'
import * as os from 'os'
import * as path from 'path'

Expand Down Expand Up @@ -120,6 +121,35 @@ export class CheHelper {
* If secret doesn't exist, undefined is returned.
*/
async retrieveCheCaCert(cheNamespace: string): Promise<string | undefined> {
const cheCaSecretContent = await this.getCheSelfSignedSecretContent(cheNamespace)
if (!cheCaSecretContent) {
return
}

const pemBeginHeader = '-----BEGIN CERTIFICATE-----'
const pemEndHeader = '-----END CERTIFICATE-----'
const certRegExp = new RegExp(`(^${pemBeginHeader}$(?:(?!${pemBeginHeader}).)*^${pemEndHeader}$)`, 'mgs')
const certsPem = cheCaSecretContent.match(certRegExp)

const caCertsPem: string[] = []
if (certsPem) {
for (const certPem of certsPem) {
const cert = nodeforge.pki.certificateFromPem(certPem)
const basicConstraintsExt = cert.getExtension('basicConstraints')
if (basicConstraintsExt && (basicConstraintsExt as any).cA) {
Copy link
Collaborator

@tolusha tolusha Sep 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cA is it correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Otherwise it wouldn't work. (I also wondered why they named the field such way).

caCertsPem.push(certPem)
}
}
}

return caCertsPem.join('\n')
}

/**
* Retrieves content of Che self-signed-certificate secret or undefined if the secret doesn't exist.
* Note, it contains certificate chain in pem format.
*/
private async getCheSelfSignedSecretContent(cheNamespace: string): Promise<string | undefined> {
const cheCaSecret = await this.kube.getSecret(CHE_ROOT_CA_SECRET_NAME, cheNamespace)
if (!cheCaSecret) {
return
Expand Down
20 changes: 9 additions & 11 deletions src/commands/cacert/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,13 @@
import { Command, flags } from '@oclif/command'
import { 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 { CheHelper } from '../../api/che'
import { KubeHelper } from '../../api/kube'
import { cheNamespace, skipKubeHealthzCheck } from '../../common-flags'
import { DEFAULT_CA_CERT_FILE_NAME } from '../../constants'
import { CheTasks } from '../../tasks/che'
import { ApiTasks } from '../../tasks/platforms/api'

export default class Export extends Command {
static description = 'Retrieves Eclipse Che self-signed certificate'
Expand All @@ -41,23 +39,23 @@ export default class Export extends Command {

async run() {
const { flags } = this.parse(Export)
const ctx: any = {}
const kube = new KubeHelper(flags)
const cheHelper = new CheHelper(flags)
const cheTasks = new CheTasks(flags)
const apiTasks = new ApiTasks()
const tasks = new Listr([], { renderer: 'silent' })

tasks.add(apiTasks.testApiTasks(flags, this))
tasks.add(cheTasks.verifyCheNamespaceExistsTask(flags, this))
if (!await kube.hasReadPermissionsForNamespace(flags.chenamespace)) {
throw new Error(`E_PERM_DENIED - Permission denied: no read access to '${flags.chenamespace}' namespace`)
}
if (!await cheHelper.cheNamespaceExist(flags.chenamespace)) {
throw new Error(`E_BAD_NS - Namespace ${flags.chenamespace} does not exist. Please specify it with --chenamespace flag`)
mmorhun marked this conversation as resolved.
Show resolved Hide resolved
}

try {
await tasks.run(ctx)
const cheCaCert = await cheHelper.retrieveCheCaCert(flags.chenamespace)
if (cheCaCert) {
const targetFile = await cheHelper.saveCheCaCert(cheCaCert, this.getTargetFile(flags.destination))
this.log(`Eclipse Che self-signed CA certificate is exported to ${targetFile}`)
} else {
this.log('Seems commonly trusted certificate is used.')
this.log('Self signed certificate secret not found. Is commonly trusted certificate used?')
}
} catch (error) {
this.error(error)
Expand Down
2 changes: 1 addition & 1 deletion src/tasks/che.ts
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ export class CheTasks {
title: `Verify if namespace '${flags.chenamespace}' exists`,
task: async () => {
if (!await this.che.cheNamespaceExist(flags.chenamespace)) {
command.error(`E_BAD_NS - Namespace does not exist.\nThe Kubernetes Namespace "${flags.chenamespace}" doesn't exist. The configuration cannot be injected.\nFix with: verify the namespace where workspace is running (kubectl get --all-namespaces deployment | grep workspace)`, { code: 'EBADNS' })
command.error(`E_BAD_NS - Namespace does not exist.\nThe Kubernetes Namespace "${flags.chenamespace}" doesn't exist.`, { code: 'EBADNS' })
}
}
}]
Expand Down
12 changes: 12 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,13 @@
dependencies:
"@types/node" "*"

"@types/node-forge@^0.9.5":
version "0.9.5"
resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-0.9.5.tgz#648231d79da197216290429020698d4e767365a0"
integrity sha512-rrN3xfA/oZIzwOnO3d2wRQz7UdeVkmMMPjWUCfpPTPuKFVb3D6G10LuiVHYYmvrivBBLMx4m0P/FICoDbNZUMA==
dependencies:
"@types/node" "*"

"@types/node-notifier@^8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@types/node-notifier/-/node-notifier-8.0.0.tgz#51100d67155ed1500a8aaa633987109f59a0637d"
Expand Down Expand Up @@ -4256,6 +4263,11 @@ nock@^11.7.0:
mkdirp "^0.5.0"
propagate "^2.0.0"

node-forge@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==

node-forge@^0.8.5:
version "0.8.5"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.8.5.tgz#57906f07614dc72762c84cef442f427c0e1b86ee"
Expand Down