UNPKG

4.2 kBPlain TextView Raw
1import path from 'path';
2import {
3 unlinkSync as rm,
4 readFileSync as readFile,
5 writeFileSync as writeFile,
6 existsSync as exists
7} from 'fs';
8import { sync as rimraf } from 'rimraf';
9import createDebug from 'debug';
10
11import {
12 rootCAKeyPath,
13 rootCACertPath,
14 caSelfSignConfig,
15 opensslSerialFilePath,
16 opensslDatabaseFilePath,
17 isWindows,
18 isLinux,
19 caVersionFile
20} from './constants';
21import currentPlatform from './platforms';
22import { openssl, mktmp } from './utils';
23import { generateKey } from './certificates';
24import { Options } from './index';
25
26const debug = createDebug('devcert:certificate-authority');
27
28/**
29 * Install the once-per-machine trusted root CA. We'll use this CA to sign
30 * per-app certs.
31 */
32export default async function installCertificateAuthority(options: Options = {}): Promise<void> {
33 debug(`Checking if older devcert install is present`);
34 scrubOldInsecureVersions();
35
36 debug(`Generating a root certificate authority`);
37 let rootKeyPath = mktmp();
38 let rootCertPath = mktmp();
39
40 debug(`Generating the OpenSSL configuration needed to setup the certificate authority`);
41 seedConfigFiles();
42
43 debug(`Generating a private key`);
44 generateKey(rootKeyPath);
45
46 debug(`Generating a CA certificate`);
47 openssl(`req -new -x509 -config "${ caSelfSignConfig }" -key "${ rootKeyPath }" -out "${ rootCertPath }"`);
48
49 debug('Saving certificate authority credentials');
50 await saveCertificateAuthorityCredentials(rootKeyPath, rootCertPath);
51
52 debug(`Adding the root certificate authority to trust stores`);
53 await currentPlatform.addToTrustStores(rootCertPath, options);
54}
55
56/**
57 * Older versions of devcert left the root certificate keys unguarded and
58 * accessible by userland processes. Here, we check for evidence of this older
59 * version, and if found, we delete the root certificate keys to remove the
60 * attack vector.
61 */
62function scrubOldInsecureVersions() {
63 // Use the old verion's logic for determining config directory
64 let configDir: string;
65 if (isWindows && process.env.LOCALAPPDATA) {
66 configDir = path.join(process.env.LOCALAPPDATA, 'devcert', 'config');
67 } else {
68 let uid = process.getuid && process.getuid();
69 let userHome = (isLinux && uid === 0) ? path.resolve('/usr/local/share') : require('os').homedir();
70 configDir = path.join(userHome, '.config', 'devcert');
71 }
72
73 // Delete the root certificate keys, as well as the generated app certificates
74 debug(`Checking ${ configDir } for legacy files ...`);
75 [
76 path.join(configDir, 'openssl.conf'),
77 path.join(configDir, 'devcert-ca-root.key'),
78 path.join(configDir, 'devcert-ca-root.crt'),
79 path.join(configDir, 'devcert-ca-version'),
80 path.join(configDir, 'certs')
81 ].forEach((filepath) => {
82 if (exists(filepath)) {
83 debug(`Removing legacy file: ${ filepath }`)
84 rimraf(filepath);
85 }
86 });
87}
88
89/**
90 * Initializes the files OpenSSL needs to sign certificates as a certificate
91 * authority, as well as our CA setup version
92 */
93function seedConfigFiles() {
94 // This is v2 of the devcert certificate authority setup
95 writeFile(caVersionFile, '2');
96 // OpenSSL CA files
97 writeFile(opensslDatabaseFilePath, '');
98 writeFile(opensslSerialFilePath, '01');
99}
100
101export async function withCertificateAuthorityCredentials(cb: ({ caKeyPath, caCertPath }: { caKeyPath: string, caCertPath: string }) => Promise<void> | void) {
102 debug(`Retrieving devcert's certificate authority credentials`);
103 let tmpCAKeyPath = mktmp();
104 let tmpCACertPath = mktmp();
105 let caKey = await currentPlatform.readProtectedFile(rootCAKeyPath);
106 let caCert = await currentPlatform.readProtectedFile(rootCACertPath);
107 writeFile(tmpCAKeyPath, caKey);
108 writeFile(tmpCACertPath, caCert);
109 await cb({ caKeyPath: tmpCAKeyPath, caCertPath: tmpCACertPath });
110 rm(tmpCAKeyPath);
111 rm(tmpCACertPath);
112}
113
114async function saveCertificateAuthorityCredentials(keypath: string, certpath: string) {
115 debug(`Saving devcert's certificate authority credentials`);
116 let key = readFile(keypath, 'utf-8');
117 let cert = readFile(certpath, 'utf-8');
118 await currentPlatform.writeProtectedFile(rootCAKeyPath, key);
119 await currentPlatform.writeProtectedFile(rootCACertPath, cert);
120}