From 8463211472ff852fe39c1ca05b24d71a278d8fa8 Mon Sep 17 00:00:00 2001 From: James Hush Date: Fri, 14 Jun 2019 17:04:11 -0700 Subject: [PATCH 1/8] feat(subject_alt_names): Allow multiple subject_alt_names --- .../domain-certificate-signing-requests.conf | 3 +-- .../domain-certificates.conf | 3 +-- src/constants.ts | 19 ++++++++++++++++--- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/openssl-configurations/domain-certificate-signing-requests.conf b/openssl-configurations/domain-certificate-signing-requests.conf index 4196ba2..b9e3196 100644 --- a/openssl-configurations/domain-certificate-signing-requests.conf +++ b/openssl-configurations/domain-certificate-signing-requests.conf @@ -21,5 +21,4 @@ subjectAltName = @subject_alt_names subjectKeyIdentifier = hash [ subject_alt_names ] -DNS.1 = *.<%= domain %> -DNS.2 = <%= domain %> \ No newline at end of file +<%= altNames %> diff --git a/openssl-configurations/domain-certificates.conf b/openssl-configurations/domain-certificates.conf index 48eb5d6..bc998ef 100644 --- a/openssl-configurations/domain-certificates.conf +++ b/openssl-configurations/domain-certificates.conf @@ -35,5 +35,4 @@ extendedKeyUsage = serverAuth subjectAltName = @subject_alt_names [ subject_alt_names ] -DNS.1 = *.<%= domain %> -DNS.2 = <%= domain %> \ No newline at end of file +<%= altNames %> diff --git a/src/constants.ts b/src/constants.ts index 70b74ff..2f8d60b 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -23,21 +23,34 @@ export const opensslSerialFilePath = configPath('certificate-authority', 'serial export const opensslDatabaseFilePath = configPath('certificate-authority', 'index.txt'); export const caSelfSignConfig = path.join(__dirname, '../openssl-configurations/certificate-authority-self-signing.conf'); -export function withDomainSigningRequestConfig(domain: string, cb: (filepath: string) => void) { +function generateAltNames(domains: string[]) { + return domains + .map((domain, index) => `DNS.${index + 1} = ${domain}`) + .join("\r\n"); +} + +export function withDomainSigningRequestConfig(domains: string | string[], cb: (filepath: string) => void) { + const domainList = typeof domains === "string" ? [domains] : domains; + const altNames = generateAltNames(domainList); + let tmpFile = mktmp(); let source = readFile(path.join(__dirname, '../openssl-configurations/domain-certificate-signing-requests.conf'), 'utf-8'); let template = makeTemplate(source); - let result = template({ domain }); + let result = template({ domain, altNames }); writeFile(tmpFile, eol.auto(result)); cb(tmpFile); rm(tmpFile); } -export function withDomainCertificateConfig(domain: string, cb: (filepath: string) => void) { +export function withDomainCertificateConfig(domains: string | string[], cb: (filepath: string) => void) { + const domainList = typeof domains === "string" ? [domains] : domains; + const altNames = generateAltNames(domainList); + let tmpFile = mktmp(); let source = readFile(path.join(__dirname, '../openssl-configurations/domain-certificates.conf'), 'utf-8'); let template = makeTemplate(source); let result = template({ + altNames, domain, serialFile: opensslSerialFilePath, databaseFile: opensslDatabaseFilePath, From 1e7527c8faa5bd166ee09651048581cf25d8529e Mon Sep 17 00:00:00 2001 From: James Hush Date: Fri, 14 Jun 2019 17:06:29 -0700 Subject: [PATCH 2/8] Change alt names --- .../domain-certificate-signing-requests.conf | 2 +- openssl-configurations/domain-certificates.conf | 2 +- src/constants.ts | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openssl-configurations/domain-certificate-signing-requests.conf b/openssl-configurations/domain-certificate-signing-requests.conf index b9e3196..2290920 100644 --- a/openssl-configurations/domain-certificate-signing-requests.conf +++ b/openssl-configurations/domain-certificate-signing-requests.conf @@ -21,4 +21,4 @@ subjectAltName = @subject_alt_names subjectKeyIdentifier = hash [ subject_alt_names ] -<%= altNames %> +<%= subjectAltNames %> diff --git a/openssl-configurations/domain-certificates.conf b/openssl-configurations/domain-certificates.conf index bc998ef..0def6a3 100644 --- a/openssl-configurations/domain-certificates.conf +++ b/openssl-configurations/domain-certificates.conf @@ -35,4 +35,4 @@ extendedKeyUsage = serverAuth subjectAltName = @subject_alt_names [ subject_alt_names ] -<%= altNames %> +<%= subjectAltNames %> diff --git a/src/constants.ts b/src/constants.ts index 2f8d60b..b64f2f6 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -23,7 +23,7 @@ export const opensslSerialFilePath = configPath('certificate-authority', 'serial export const opensslDatabaseFilePath = configPath('certificate-authority', 'index.txt'); export const caSelfSignConfig = path.join(__dirname, '../openssl-configurations/certificate-authority-self-signing.conf'); -function generateAltNames(domains: string[]) { +function generatesubjectAltNames(domains: string[]) { return domains .map((domain, index) => `DNS.${index + 1} = ${domain}`) .join("\r\n"); @@ -31,12 +31,12 @@ function generateAltNames(domains: string[]) { export function withDomainSigningRequestConfig(domains: string | string[], cb: (filepath: string) => void) { const domainList = typeof domains === "string" ? [domains] : domains; - const altNames = generateAltNames(domainList); + const subjectAltNames = generatesubjectAltNames(domainList); let tmpFile = mktmp(); let source = readFile(path.join(__dirname, '../openssl-configurations/domain-certificate-signing-requests.conf'), 'utf-8'); let template = makeTemplate(source); - let result = template({ domain, altNames }); + let result = template({ domain, subjectAltNames }); writeFile(tmpFile, eol.auto(result)); cb(tmpFile); rm(tmpFile); @@ -44,13 +44,13 @@ export function withDomainSigningRequestConfig(domains: string | string[], cb: ( export function withDomainCertificateConfig(domains: string | string[], cb: (filepath: string) => void) { const domainList = typeof domains === "string" ? [domains] : domains; - const altNames = generateAltNames(domainList); + const subjectAltNames = generatesubjectAltNames(domainList); let tmpFile = mktmp(); let source = readFile(path.join(__dirname, '../openssl-configurations/domain-certificates.conf'), 'utf-8'); let template = makeTemplate(source); let result = template({ - altNames, + subjectAltNames, domain, serialFile: opensslSerialFilePath, databaseFile: opensslDatabaseFilePath, From db54c8af8226e5a65553af6eaf81c6f3e5807eb9 Mon Sep 17 00:00:00 2001 From: James Hush Date: Fri, 14 Jun 2019 17:10:21 -0700 Subject: [PATCH 3/8] Add flatMap --- src/constants.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/constants.ts b/src/constants.ts index b64f2f6..1b4ace1 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -25,7 +25,10 @@ export const caSelfSignConfig = path.join(__dirname, '../openssl-configurations/ function generatesubjectAltNames(domains: string[]) { return domains - .map((domain, index) => `DNS.${index + 1} = ${domain}`) + .flatMap(domain => { + return [domain, `*.${domain}`] + } + .map(domain => `DNS.${index + 1} = ${domain}`) .join("\r\n"); } From f8c5bae0de7f4f4407b026738bd2ba86d1ef069f Mon Sep 17 00:00:00 2001 From: James Hush Date: Fri, 14 Jun 2019 17:24:41 -0700 Subject: [PATCH 4/8] Fix typescript errors, add flatMap support in typescript --- src/constants.ts | 9 ++++----- tsconfig.json | 3 +++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index 1b4ace1..91361ac 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -25,15 +25,14 @@ export const caSelfSignConfig = path.join(__dirname, '../openssl-configurations/ function generatesubjectAltNames(domains: string[]) { return domains - .flatMap(domain => { - return [domain, `*.${domain}`] - } - .map(domain => `DNS.${index + 1} = ${domain}`) + .flatMap(domain => [domain, `*.${domain}`]) + .map((domain, index) => `DNS.${index + 1} = ${domain}`) .join("\r\n"); } export function withDomainSigningRequestConfig(domains: string | string[], cb: (filepath: string) => void) { const domainList = typeof domains === "string" ? [domains] : domains; + const domain = domainList[0]; const subjectAltNames = generatesubjectAltNames(domainList); let tmpFile = mktmp(); @@ -47,6 +46,7 @@ export function withDomainSigningRequestConfig(domains: string | string[], cb: ( export function withDomainCertificateConfig(domains: string | string[], cb: (filepath: string) => void) { const domainList = typeof domains === "string" ? [domains] : domains; + const domain = domainList[0]; const subjectAltNames = generatesubjectAltNames(domainList); let tmpFile = mktmp(); @@ -54,7 +54,6 @@ export function withDomainCertificateConfig(domains: string | string[], cb: (fil let template = makeTemplate(source); let result = template({ subjectAltNames, - domain, serialFile: opensslSerialFilePath, databaseFile: opensslDatabaseFilePath, domainDir: pathForDomain(domain) diff --git a/tsconfig.json b/tsconfig.json index 2710aa5..32cf451 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,9 @@ "compileOnSave": false, "compilerOptions": { "declaration": true, + "lib": [ + "esnext" + ], "module": "commonjs", "moduleResolution": "node", "target": "ES2016", From c06e68fcb827a816b9ba260a61b14d7c593d9d25 Mon Sep 17 00:00:00 2001 From: James Hush Date: Fri, 14 Jun 2019 17:35:51 -0700 Subject: [PATCH 5/8] Switch public api --- src/certificates.ts | 4 ++-- src/index.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/certificates.ts b/src/certificates.ts index 78fe1c3..0a6a2f9 100644 --- a/src/certificates.ts +++ b/src/certificates.ts @@ -15,7 +15,7 @@ const debug = createDebug('devcert:certificates'); * individual domain certificates are signed by the devcert root CA (which was * added to the OS/browser trust stores), they are trusted. */ -export default async function generateDomainCertificate(domain: string): Promise { +export default async function generateDomainCertificate(domain: string | string[]): Promise { mkdirp(pathForDomain(domain)); debug(`Generating private key for ${ domain }`); @@ -43,4 +43,4 @@ export function generateKey(filename: string): void { debug(`generateKey: ${ filename }`); openssl(`genrsa -out "${ filename }" 2048`); chmod(filename, 400); -} \ No newline at end of file +} diff --git a/src/index.ts b/src/index.ts index 92893ce..2455e79 100644 --- a/src/index.ts +++ b/src/index.ts @@ -35,7 +35,7 @@ export interface Options { * are Buffers with the contents of the certificate private key and certificate * file, respectively */ -export async function certificateFor(domain: string, options: Options = {}) { +export async function certificateFor(domain: string | string[], options: Options = {}) { debug(`Certificate requested for ${ domain }. Skipping certutil install: ${ Boolean(options.skipCertutilInstall) }. Skipping hosts file: ${ Boolean(options.skipHostsFile) }`); if (options.ui) { @@ -84,4 +84,4 @@ export function configuredDomains() { export function removeDomain(domain: string) { return rimraf.sync(pathForDomain(domain)); -} \ No newline at end of file +} From a50d7b959a6eb25294fd26615d2c2b61501587cb Mon Sep 17 00:00:00 2001 From: James Hush Date: Thu, 20 Jun 2019 11:29:47 -0700 Subject: [PATCH 6/8] Fix the domain vs domains change --- package.json | 2 +- src/constants.ts | 2 +- src/index.ts | 17 ++++++++++------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 3ca28cc..6e7226e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "devcert", - "version": "1.0.0", + "version": "1.1.0", "description": "Generate trusted local SSL/TLS certificates for local SSL development", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/constants.ts b/src/constants.ts index 91361ac..5ac3e83 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -16,7 +16,7 @@ export const configDir = applicationConfigPath('devcert'); export const configPath: (...pathSegments: string[]) => string = path.join.bind(path, configDir); export const domainsDir = configPath('domains'); -export const pathForDomain: (domain: string, ...pathSegments: string[]) => string = path.join.bind(path, domainsDir) +export const pathForDomain: (domain: string | string[], ...pathSegments: string[]) => string = path.join.bind(path, domainsDir) export const caVersionFile = configPath('devcert-ca-version'); export const opensslSerialFilePath = configPath('certificate-authority', 'serial'); diff --git a/src/index.ts b/src/index.ts index 2455e79..d643b70 100644 --- a/src/index.ts +++ b/src/index.ts @@ -36,7 +36,8 @@ export interface Options { * file, respectively */ export async function certificateFor(domain: string | string[], options: Options = {}) { - debug(`Certificate requested for ${ domain }. Skipping certutil install: ${ Boolean(options.skipCertutilInstall) }. Skipping hosts file: ${ Boolean(options.skipHostsFile) }`); + const domains = Array.isArray(domain) ? domain : [domain]; + debug(`Certificate requested for ${ domains }. Skipping certutil install: ${ Boolean(options.skipCertutilInstall) }. Skipping hosts file: ${ Boolean(options.skipHostsFile) }`); if (options.ui) { Object.assign(UI, options.ui); @@ -50,21 +51,23 @@ export async function certificateFor(domain: string | string[], options: Options throw new Error('OpenSSL not found: OpenSSL is required to generate SSL certificates - make sure it is installed and available in your PATH'); } - let domainKeyPath = pathForDomain(domain, `private-key.key`); - let domainCertPath = pathForDomain(domain, `certificate.crt`); + let domainKeyPath = pathForDomain(domains, `private-key.key`); + let domainCertPath = pathForDomain(domains, `certificate.crt`); if (!exists(rootCAKeyPath)) { debug('Root CA is not installed yet, so it must be our first run. Installing root CA ...'); await installCertificateAuthority(options); } - if (!exists(pathForDomain(domain, `certificate.crt`))) { - debug(`Can't find certificate file for ${ domain }, so it must be the first request for ${ domain }. Generating and caching ...`); - await generateDomainCertificate(domain); + if (!exists(pathForDomain(domains, `certificate.crt`))) { + debug(`Can't find certificate file for ${ domains }, so it must be the first request for ${ domains }. Generating and caching ...`); + await generateDomainCertificate(domains); } if (!options.skipHostsFile) { - await currentPlatform.addDomainToHostFileIfMissing(domain); + domains.forEach(async (domain) => { + await currentPlatform.addDomainToHostFileIfMissing(domain); + }) } debug(`Returning domain certificate`); From 1c77d6cffbbe63ac77c35b5a925b5e3a65040e6e Mon Sep 17 00:00:00 2001 From: James Hush Date: Thu, 20 Jun 2019 11:43:00 -0700 Subject: [PATCH 7/8] Fix typescript stuff --- package.json | 6 +++--- yarn.lock | 14 ++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 6e7226e..b7c80d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "devcert", - "version": "1.1.0", + "version": "1.0.0", "description": "Generate trusted local SSL/TLS certificates for local SSL development", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -27,7 +27,7 @@ "homepage": "https://github.com/davewasmer/devcert#readme", "devDependencies": { "standard-version": "^4.3.0", - "typescript": "^2.7.0" + "typescript": "^2.9.2" }, "dependencies": { "@types/configstore": "^2.1.1", @@ -52,7 +52,7 @@ "rimraf": "^2.6.2", "sudo-prompt": "^8.2.0", "tmp": "^0.0.33", - "tslib": "^1.8.1" + "tslib": "^1.10.0" }, "optionalDependencies": {} } diff --git a/yarn.lock b/yarn.lock index 2f4d478..75f07b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1230,17 +1230,19 @@ trim-off-newlines@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" -tslib@^1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.8.1.tgz#6946af2d1d651a7b1863b531d6e5afa41aa44eac" +tslib@^1.10.0: + version "1.10.0" + resolved "https://system1.jfrog.io/system1/api/npm/npm-virtual/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + integrity sha1-w8GflZc/sKYpc/sJ2Q2WHuQ+XIo= typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" -typescript@^2.7.0: - version "2.7.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.7.2.tgz#2d615a1ef4aee4f574425cdff7026edf81919836" +typescript@^2.9.2: + version "2.9.2" + resolved "https://system1.jfrog.io/system1/api/npm/npm-virtual/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" + integrity sha1-HL9h0F1rliaSROtqO85L2RTg8Aw= uglify-js@^2.6: version "2.8.18" From 08038ba30be46fdbb852194438bf8f55157e0eb0 Mon Sep 17 00:00:00 2001 From: James Hush Date: Thu, 20 Jun 2019 15:48:22 -0700 Subject: [PATCH 8/8] Try this? --- src/certificates.ts | 3 ++- src/index.ts | 18 +++++++++++------- yarn.lock | 8 ++++---- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/certificates.ts b/src/certificates.ts index 0a6a2f9..563f38f 100644 --- a/src/certificates.ts +++ b/src/certificates.ts @@ -15,7 +15,8 @@ const debug = createDebug('devcert:certificates'); * individual domain certificates are signed by the devcert root CA (which was * added to the OS/browser trust stores), they are trusted. */ -export default async function generateDomainCertificate(domain: string | string[]): Promise { +export default async function generateDomainCertificate(domains: string | string[]): Promise { + const domain = Array.isArray(domains) ? domains[0] : domains; mkdirp(pathForDomain(domain)); debug(`Generating private key for ${ domain }`); diff --git a/src/index.ts b/src/index.ts index d643b70..8f7500b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -35,8 +35,8 @@ export interface Options { * are Buffers with the contents of the certificate private key and certificate * file, respectively */ -export async function certificateFor(domain: string | string[], options: Options = {}) { - const domains = Array.isArray(domain) ? domain : [domain]; +export async function certificateFor(domains: string | string[], options: Options = {}) { + const domain = Array.isArray(domains) ? domains[0] : domains; debug(`Certificate requested for ${ domains }. Skipping certutil install: ${ Boolean(options.skipCertutilInstall) }. Skipping hosts file: ${ Boolean(options.skipHostsFile) }`); if (options.ui) { @@ -51,23 +51,27 @@ export async function certificateFor(domain: string | string[], options: Options throw new Error('OpenSSL not found: OpenSSL is required to generate SSL certificates - make sure it is installed and available in your PATH'); } - let domainKeyPath = pathForDomain(domains, `private-key.key`); - let domainCertPath = pathForDomain(domains, `certificate.crt`); + let domainKeyPath = pathForDomain(domain, `private-key.key`); + let domainCertPath = pathForDomain(domain, `certificate.crt`); if (!exists(rootCAKeyPath)) { debug('Root CA is not installed yet, so it must be our first run. Installing root CA ...'); await installCertificateAuthority(options); } - if (!exists(pathForDomain(domains, `certificate.crt`))) { + if (!exists(pathForDomain(domain, `certificate.crt`))) { debug(`Can't find certificate file for ${ domains }, so it must be the first request for ${ domains }. Generating and caching ...`); await generateDomainCertificate(domains); } if (!options.skipHostsFile) { - domains.forEach(async (domain) => { + if (Array.isArray(domains)) { + domains.forEach(async (domain) => { + await currentPlatform.addDomainToHostFileIfMissing(domain); + }) + } else { await currentPlatform.addDomainToHostFileIfMissing(domain); - }) + } } debug(`Returning domain certificate`); diff --git a/yarn.lock b/yarn.lock index 75f07b2..92fdf79 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1232,8 +1232,8 @@ trim-off-newlines@^1.0.0: tslib@^1.10.0: version "1.10.0" - resolved "https://system1.jfrog.io/system1/api/npm/npm-virtual/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" - integrity sha1-w8GflZc/sKYpc/sJ2Q2WHuQ+XIo= + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== typedarray@^0.0.6: version "0.0.6" @@ -1241,8 +1241,8 @@ typedarray@^0.0.6: typescript@^2.9.2: version "2.9.2" - resolved "https://system1.jfrog.io/system1/api/npm/npm-virtual/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" - integrity sha1-HL9h0F1rliaSROtqO85L2RTg8Aw= + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" + integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w== uglify-js@^2.6: version "2.8.18"