From 55227712ddd858485b3af3863a65769e4c90a868 Mon Sep 17 00:00:00 2001 From: Johannes Grumboeck Date: Fri, 14 Oct 2022 00:03:46 +0200 Subject: [PATCH 01/10] fix(backend): fix certificate validation Signed-off-by: Johannes Grumboeck --- .changeset/forty-bags-trade.md | 5 +++++ .../src/service/lib/hostFactory.ts | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 .changeset/forty-bags-trade.md diff --git a/.changeset/forty-bags-trade.md b/.changeset/forty-bags-trade.md new file mode 100644 index 0000000000..4908134498 --- /dev/null +++ b/.changeset/forty-bags-trade.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +Generated development HTTPS backend certificate is now checked for expiration date instead of file age. diff --git a/packages/backend-common/src/service/lib/hostFactory.ts b/packages/backend-common/src/service/lib/hostFactory.ts index 572d8483b1..943942d255 100644 --- a/packages/backend-common/src/service/lib/hostFactory.ts +++ b/packages/backend-common/src/service/lib/hostFactory.ts @@ -22,7 +22,7 @@ import * as https from 'https'; import { Logger } from 'winston'; import { HttpsSettings } from './config'; -const ALMOST_MONTH_IN_MS = 25 * 24 * 60 * 60 * 1000; +const FIVE_DAYS_IN_MS = 5 * 24 * 60 * 60 * 1000; const IP_HOSTNAME_REGEX = /:|^\d+\.\d+\.\d+\.\d+$/; @@ -95,15 +95,21 @@ async function getGeneratedCertificate(hostname: string, logger?: Logger) { } let cert = undefined; + let remainingMs = 0; if (await fs.pathExists(certPath)) { - const stat = await fs.stat(certPath); - const ageMs = Date.now() - stat.ctimeMs; - if (stat.isFile() && ageMs < ALMOST_MONTH_IN_MS) { - cert = await fs.readFile(certPath); + cert = await fs.readFile(certPath); + try { + const forge = require('node-forge') + const crt = forge.pki.certificateFromPem(cert) + const crtTimestamp = Date.parse(crt.validity.notAfter); + remainingMs = crtTimestamp - Date.now(); + } catch (error) { + logger.warn(`Unable to parse self-signed certificate. ${error}`); + remainingMs = 0 } } - if (cert) { + if (remainingMs > FIVE_DAYS_IN_MS) { logger?.info('Using existing self-signed certificate'); return { key: cert, From 88ac2dd22066e0cb3419055649b1f90cfc0756e1 Mon Sep 17 00:00:00 2001 From: Johannes Grumboeck Date: Fri, 14 Oct 2022 00:09:27 +0200 Subject: [PATCH 02/10] fix(backend): Object is possibly 'undefined' Signed-off-by: Johannes Grumboeck --- packages/backend-common/src/service/lib/hostFactory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend-common/src/service/lib/hostFactory.ts b/packages/backend-common/src/service/lib/hostFactory.ts index 943942d255..917458896e 100644 --- a/packages/backend-common/src/service/lib/hostFactory.ts +++ b/packages/backend-common/src/service/lib/hostFactory.ts @@ -104,7 +104,7 @@ async function getGeneratedCertificate(hostname: string, logger?: Logger) { const crtTimestamp = Date.parse(crt.validity.notAfter); remainingMs = crtTimestamp - Date.now(); } catch (error) { - logger.warn(`Unable to parse self-signed certificate. ${error}`); + logger?.warn(`Unable to parse self-signed certificate. ${error}`); remainingMs = 0 } } From 7e739e6009f630ebb612d18f92925b339c482b98 Mon Sep 17 00:00:00 2001 From: Johannes Grumboeck Date: Fri, 14 Oct 2022 00:28:47 +0200 Subject: [PATCH 03/10] fix(backend): Type is not assignable Signed-off-by: Johannes Grumboeck --- packages/backend-common/src/service/lib/hostFactory.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/backend-common/src/service/lib/hostFactory.ts b/packages/backend-common/src/service/lib/hostFactory.ts index 917458896e..a4bd178de7 100644 --- a/packages/backend-common/src/service/lib/hostFactory.ts +++ b/packages/backend-common/src/service/lib/hostFactory.ts @@ -107,9 +107,13 @@ async function getGeneratedCertificate(hostname: string, logger?: Logger) { logger?.warn(`Unable to parse self-signed certificate. ${error}`); remainingMs = 0 } + if (remainingMs < FIVE_DAYS_IN_MS) { + // Reset certificate if expiration is nearly over + cert = undefined + } } - if (remainingMs > FIVE_DAYS_IN_MS) { + if (cert) { logger?.info('Using existing self-signed certificate'); return { key: cert, From 35f292c9f7532e8a145e10141d73fdd02cb26f0e Mon Sep 17 00:00:00 2001 From: Johannes Grumboeck Date: Fri, 14 Oct 2022 00:54:28 +0200 Subject: [PATCH 04/10] fix(backend): run prettier and yarn add node-forge Signed-off-by: Johannes Grumboeck --- packages/backend-common/package.json | 1 + packages/backend-common/src/service/lib/hostFactory.ts | 8 ++++---- yarn.lock | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 2a7e307774..b9cda38b51 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -75,6 +75,7 @@ "morgan": "^1.10.0", "node-abort-controller": "^3.0.1", "node-fetch": "^2.6.7", + "node-forge": "^1.3.1", "raw-body": "^2.4.1", "request": "^2.88.2", "selfsigned": "^2.0.0", diff --git a/packages/backend-common/src/service/lib/hostFactory.ts b/packages/backend-common/src/service/lib/hostFactory.ts index a4bd178de7..16bfc6adf6 100644 --- a/packages/backend-common/src/service/lib/hostFactory.ts +++ b/packages/backend-common/src/service/lib/hostFactory.ts @@ -99,17 +99,17 @@ async function getGeneratedCertificate(hostname: string, logger?: Logger) { if (await fs.pathExists(certPath)) { cert = await fs.readFile(certPath); try { - const forge = require('node-forge') - const crt = forge.pki.certificateFromPem(cert) + const forge = require('node-forge'); + const crt = forge.pki.certificateFromPem(cert); const crtTimestamp = Date.parse(crt.validity.notAfter); remainingMs = crtTimestamp - Date.now(); } catch (error) { logger?.warn(`Unable to parse self-signed certificate. ${error}`); - remainingMs = 0 + remainingMs = 0; } if (remainingMs < FIVE_DAYS_IN_MS) { // Reset certificate if expiration is nearly over - cert = undefined + cert = undefined; } } diff --git a/yarn.lock b/yarn.lock index e9f94623ae..378e432b08 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3201,6 +3201,7 @@ __metadata: mysql2: ^2.2.5 node-abort-controller: ^3.0.1 node-fetch: ^2.6.7 + node-forge: ^1.3.1 raw-body: ^2.4.1 recursive-readdir: ^2.2.2 request: ^2.88.2 From a17a7a6cc1d8d2d97b1087efa9feabb97260ef2f Mon Sep 17 00:00:00 2001 From: Johannes Grumboeck Date: Sun, 23 Oct 2022 21:49:51 +0200 Subject: [PATCH 05/10] chore: use import for node-forge instead of require Signed-off-by: Johannes Grumboeck --- packages/backend-common/package.json | 1 + packages/backend-common/src/service/lib/hostFactory.ts | 6 +++--- yarn.lock | 10 ++++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index b9cda38b51..6791d7f322 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -49,6 +49,7 @@ "@types/dockerode": "^3.3.0", "@types/express": "^4.17.6", "@types/luxon": "^3.0.0", + "@types/node-forge": "^1.3.0", "@types/webpack-env": "^1.15.2", "archiver": "^5.0.2", "aws-sdk": "^2.840.0", diff --git a/packages/backend-common/src/service/lib/hostFactory.ts b/packages/backend-common/src/service/lib/hostFactory.ts index 16bfc6adf6..ecd214814d 100644 --- a/packages/backend-common/src/service/lib/hostFactory.ts +++ b/packages/backend-common/src/service/lib/hostFactory.ts @@ -21,6 +21,7 @@ import * as http from 'http'; import * as https from 'https'; import { Logger } from 'winston'; import { HttpsSettings } from './config'; +import * as forge from 'node-forge'; const FIVE_DAYS_IN_MS = 5 * 24 * 60 * 60 * 1000; @@ -99,9 +100,8 @@ async function getGeneratedCertificate(hostname: string, logger?: Logger) { if (await fs.pathExists(certPath)) { cert = await fs.readFile(certPath); try { - const forge = require('node-forge'); - const crt = forge.pki.certificateFromPem(cert); - const crtTimestamp = Date.parse(crt.validity.notAfter); + const crt = forge.pki.certificateFromPem(cert.toString()); + const crtTimestamp = Date.parse(crt.validity.notAfter.toString()); remainingMs = crtTimestamp - Date.now(); } catch (error) { logger?.warn(`Unable to parse self-signed certificate. ${error}`); diff --git a/yarn.lock b/yarn.lock index 378e432b08..06939594f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3164,6 +3164,7 @@ __metadata: "@types/minimist": ^1.2.0 "@types/mock-fs": ^4.13.0 "@types/morgan": ^1.9.0 + "@types/node-forge": ^1.3.0 "@types/recursive-readdir": ^2.2.0 "@types/stoppable": ^1.1.0 "@types/supertest": ^2.0.8 @@ -14014,6 +14015,15 @@ __metadata: languageName: node linkType: hard +"@types/node-forge@npm:^1.3.0": + version: 1.3.0 + resolution: "@types/node-forge@npm:1.3.0" + dependencies: + "@types/node": "*" + checksum: f811885f997fbeebb0df2db8249b9b288ad5f5573beaecf6d323b019bb9a4c12f1476a8974c934f62329862f29986c116263a50aef0008364cc3f9867672a5a0 + languageName: node + linkType: hard + "@types/node@npm:*, @types/node@npm:>=12.12.47, @types/node@npm:>=13.7.0": version: 17.0.25 resolution: "@types/node@npm:17.0.25" From 374e65c6798cc1f619773d4ef842a0afee686fae Mon Sep 17 00:00:00 2001 From: Johannes Grumboeck Date: Sun, 23 Oct 2022 22:14:17 +0200 Subject: [PATCH 06/10] feat: own function for certificate expiration Signed-off-by: Johannes Grumboeck --- .../src/service/lib/hostFactory.ts | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/backend-common/src/service/lib/hostFactory.ts b/packages/backend-common/src/service/lib/hostFactory.ts index ecd214814d..b725b5e648 100644 --- a/packages/backend-common/src/service/lib/hostFactory.ts +++ b/packages/backend-common/src/service/lib/hostFactory.ts @@ -83,6 +83,17 @@ export async function createHttpsServer( return https.createServer(credentials, app) as http.Server; } +function getCertificateExpiration(cert: string, logger?: Logger) { + try { + const crt = forge.pki.certificateFromPem(cert); + const crtTimestamp = Date.parse(crt.validity.notAfter.toString()); + return crtTimestamp - Date.now(); + } catch (error) { + logger?.warn(`Unable to parse self-signed certificate. ${error}`); + return 0; + } +} + async function getGeneratedCertificate(hostname: string, logger?: Logger) { const hasModules = await fs.pathExists('node_modules'); let certPath; @@ -95,25 +106,14 @@ async function getGeneratedCertificate(hostname: string, logger?: Logger) { certPath = resolvePath('.dev-cert.pem'); } - let cert = undefined; + let cert = undefined let remainingMs = 0; if (await fs.pathExists(certPath)) { cert = await fs.readFile(certPath); - try { - const crt = forge.pki.certificateFromPem(cert.toString()); - const crtTimestamp = Date.parse(crt.validity.notAfter.toString()); - remainingMs = crtTimestamp - Date.now(); - } catch (error) { - logger?.warn(`Unable to parse self-signed certificate. ${error}`); - remainingMs = 0; - } - if (remainingMs < FIVE_DAYS_IN_MS) { - // Reset certificate if expiration is nearly over - cert = undefined; - } + remainingMs = getCertificateExpiration(cert.toString(), logger); } - if (cert) { + if (cert && remainingMs > FIVE_DAYS_IN_MS) { logger?.info('Using existing self-signed certificate'); return { key: cert, From 296e3ac84a4b1711690afb19886b05b982f9d997 Mon Sep 17 00:00:00 2001 From: Johannes Grumboeck Date: Sun, 23 Oct 2022 22:20:34 +0200 Subject: [PATCH 07/10] fix: @types/node-forge needs to be dev-dependency Signed-off-by: Johannes Grumboeck --- packages/backend-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 6791d7f322..8def408fca 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -49,7 +49,6 @@ "@types/dockerode": "^3.3.0", "@types/express": "^4.17.6", "@types/luxon": "^3.0.0", - "@types/node-forge": "^1.3.0", "@types/webpack-env": "^1.15.2", "archiver": "^5.0.2", "aws-sdk": "^2.840.0", @@ -107,6 +106,7 @@ "@types/minimist": "^1.2.0", "@types/mock-fs": "^4.13.0", "@types/morgan": "^1.9.0", + "@types/node-forge": "^1.3.0", "@types/recursive-readdir": "^2.2.0", "@types/stoppable": "^1.1.0", "@types/supertest": "^2.0.8", From 6e16a040ae358030e4bb004022bd2a9b96ce3436 Mon Sep 17 00:00:00 2001 From: Johannes Grumboeck Date: Mon, 24 Oct 2022 20:11:45 +0200 Subject: [PATCH 08/10] fix: run prettier Signed-off-by: Johannes Grumboeck --- packages/backend-common/src/service/lib/hostFactory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend-common/src/service/lib/hostFactory.ts b/packages/backend-common/src/service/lib/hostFactory.ts index b725b5e648..616120e868 100644 --- a/packages/backend-common/src/service/lib/hostFactory.ts +++ b/packages/backend-common/src/service/lib/hostFactory.ts @@ -106,7 +106,7 @@ async function getGeneratedCertificate(hostname: string, logger?: Logger) { certPath = resolvePath('.dev-cert.pem'); } - let cert = undefined + let cert = undefined; let remainingMs = 0; if (await fs.pathExists(certPath)) { cert = await fs.readFile(certPath); From 89e86f467f6a100c09dcbec5d4e0b2a77a5f0214 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Tue, 25 Oct 2022 10:03:16 +0200 Subject: [PATCH 09/10] chore: refactor https expiration logic Signed-off-by: Johan Haals --- .../src/service/lib/hostFactory.ts | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/backend-common/src/service/lib/hostFactory.ts b/packages/backend-common/src/service/lib/hostFactory.ts index 616120e868..089bf610ed 100644 --- a/packages/backend-common/src/service/lib/hostFactory.ts +++ b/packages/backend-common/src/service/lib/hostFactory.ts @@ -21,7 +21,7 @@ import * as http from 'http'; import * as https from 'https'; import { Logger } from 'winston'; import { HttpsSettings } from './config'; -import * as forge from 'node-forge'; +import forge from 'node-forge'; const FIVE_DAYS_IN_MS = 5 * 24 * 60 * 60 * 1000; @@ -106,19 +106,16 @@ async function getGeneratedCertificate(hostname: string, logger?: Logger) { certPath = resolvePath('.dev-cert.pem'); } - let cert = undefined; - let remainingMs = 0; if (await fs.pathExists(certPath)) { - cert = await fs.readFile(certPath); - remainingMs = getCertificateExpiration(cert.toString(), logger); - } - - if (cert && remainingMs > FIVE_DAYS_IN_MS) { - logger?.info('Using existing self-signed certificate'); - return { - key: cert, - cert: cert, - }; + const cert = await fs.readFile(certPath); + const remainingMs = getCertificateExpiration(cert.toString(), logger); + if (remainingMs > FIVE_DAYS_IN_MS) { + logger?.info('Using existing self-signed certificate'); + return { + key: cert, + cert, + }; + } } logger?.info('Generating new self-signed certificate'); From 3758f4a7c2f7f58f22dea19439c85aec7e764509 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Tue, 25 Oct 2022 14:40:40 +0200 Subject: [PATCH 10/10] chore: simplify date parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Johan Haals --- packages/backend-common/src/service/lib/hostFactory.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/backend-common/src/service/lib/hostFactory.ts b/packages/backend-common/src/service/lib/hostFactory.ts index 089bf610ed..57334062ea 100644 --- a/packages/backend-common/src/service/lib/hostFactory.ts +++ b/packages/backend-common/src/service/lib/hostFactory.ts @@ -86,8 +86,7 @@ export async function createHttpsServer( function getCertificateExpiration(cert: string, logger?: Logger) { try { const crt = forge.pki.certificateFromPem(cert); - const crtTimestamp = Date.parse(crt.validity.notAfter.toString()); - return crtTimestamp - Date.now(); + return crt.validity.notAfter.getTime() - Date.now(); } catch (error) { logger?.warn(`Unable to parse self-signed certificate. ${error}`); return 0;