UNPKG

14.7 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const tslib_1 = require("tslib");
4const path_1 = tslib_1.__importDefault(require("path"));
5const fs_1 = require("fs");
6const rimraf_1 = require("rimraf");
7const debug_1 = tslib_1.__importDefault(require("debug"));
8const constants_1 = require("./constants");
9const platforms_1 = tslib_1.__importDefault(require("./platforms"));
10const utils_1 = require("./utils");
11const certificates_1 = require("./certificates");
12const debug = debug_1.default('devcert:certificate-authority');
13/**
14 * Install the once-per-machine trusted root CA. We'll use this CA to sign
15 * per-app certs.
16 */
17function installCertificateAuthority(options = {}) {
18 return tslib_1.__awaiter(this, void 0, void 0, function* () {
19 debug(`Checking if older devcert install is present`);
20 scrubOldInsecureVersions();
21 debug(`Generating a root certificate authority`);
22 let rootKeyPath = utils_1.mktmp();
23 let rootCertPath = utils_1.mktmp();
24 debug(`Generating the OpenSSL configuration needed to setup the certificate authority`);
25 seedConfigFiles();
26 debug(`Generating a private key`);
27 certificates_1.generateKey(rootKeyPath);
28 debug(`Generating a CA certificate`);
29 utils_1.openssl(`req -new -x509 -config "${constants_1.caSelfSignConfig}" -key "${rootKeyPath}" -out "${rootCertPath}"`);
30 debug('Saving certificate authority credentials');
31 yield saveCertificateAuthorityCredentials(rootKeyPath, rootCertPath);
32 debug(`Adding the root certificate authority to trust stores`);
33 yield platforms_1.default.addToTrustStores(rootCertPath, options);
34 });
35}
36exports.default = installCertificateAuthority;
37/**
38 * Older versions of devcert left the root certificate keys unguarded and
39 * accessible by userland processes. Here, we check for evidence of this older
40 * version, and if found, we delete the root certificate keys to remove the
41 * attack vector.
42 */
43function scrubOldInsecureVersions() {
44 // Use the old verion's logic for determining config directory
45 let configDir;
46 if (constants_1.isWindows && process.env.LOCALAPPDATA) {
47 configDir = path_1.default.join(process.env.LOCALAPPDATA, 'devcert', 'config');
48 }
49 else {
50 let uid = process.getuid && process.getuid();
51 let userHome = (constants_1.isLinux && uid === 0) ? path_1.default.resolve('/usr/local/share') : require('os').homedir();
52 configDir = path_1.default.join(userHome, '.config', 'devcert');
53 }
54 // Delete the root certificate keys, as well as the generated app certificates
55 debug(`Checking ${configDir} for legacy files ...`);
56 [
57 path_1.default.join(configDir, 'openssl.conf'),
58 path_1.default.join(configDir, 'devcert-ca-root.key'),
59 path_1.default.join(configDir, 'devcert-ca-root.crt'),
60 path_1.default.join(configDir, 'devcert-ca-version'),
61 path_1.default.join(configDir, 'certs')
62 ].forEach((filepath) => {
63 if (fs_1.existsSync(filepath)) {
64 debug(`Removing legacy file: ${filepath}`);
65 rimraf_1.sync(filepath);
66 }
67 });
68}
69/**
70 * Initializes the files OpenSSL needs to sign certificates as a certificate
71 * authority, as well as our CA setup version
72 */
73function seedConfigFiles() {
74 // This is v2 of the devcert certificate authority setup
75 fs_1.writeFileSync(constants_1.caVersionFile, '2');
76 // OpenSSL CA files
77 fs_1.writeFileSync(constants_1.opensslDatabaseFilePath, '');
78 fs_1.writeFileSync(constants_1.opensslSerialFilePath, '01');
79}
80function withCertificateAuthorityCredentials(cb) {
81 return tslib_1.__awaiter(this, void 0, void 0, function* () {
82 debug(`Retrieving devcert's certificate authority credentials`);
83 let tmpCAKeyPath = utils_1.mktmp();
84 let tmpCACertPath = utils_1.mktmp();
85 let caKey = yield platforms_1.default.readProtectedFile(constants_1.rootCAKeyPath);
86 let caCert = yield platforms_1.default.readProtectedFile(constants_1.rootCACertPath);
87 fs_1.writeFileSync(tmpCAKeyPath, caKey);
88 fs_1.writeFileSync(tmpCACertPath, caCert);
89 yield cb({ caKeyPath: tmpCAKeyPath, caCertPath: tmpCACertPath });
90 fs_1.unlinkSync(tmpCAKeyPath);
91 fs_1.unlinkSync(tmpCACertPath);
92 });
93}
94exports.withCertificateAuthorityCredentials = withCertificateAuthorityCredentials;
95function saveCertificateAuthorityCredentials(keypath, certpath) {
96 return tslib_1.__awaiter(this, void 0, void 0, function* () {
97 debug(`Saving devcert's certificate authority credentials`);
98 let key = fs_1.readFileSync(keypath, 'utf-8');
99 let cert = fs_1.readFileSync(certpath, 'utf-8');
100 yield platforms_1.default.writeProtectedFile(constants_1.rootCAKeyPath, key);
101 yield platforms_1.default.writeProtectedFile(constants_1.rootCACertPath, cert);
102 });
103}
104//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2VydGlmaWNhdGUtYXV0aG9yaXR5LmpzIiwic291cmNlUm9vdCI6Ii9Vc2Vycy9kYXcvb3NzL2RldmNlcnQvIiwic291cmNlcyI6WyJjZXJ0aWZpY2F0ZS1hdXRob3JpdHkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsd0RBQXdCO0FBQ3hCLDJCQUtZO0FBQ1osbUNBQXdDO0FBQ3hDLDBEQUFnQztBQUVoQywyQ0FTcUI7QUFDckIsb0VBQTBDO0FBQzFDLG1DQUF5QztBQUN6QyxpREFBNkM7QUFHN0MsTUFBTSxLQUFLLEdBQUcsZUFBVyxDQUFDLCtCQUErQixDQUFDLENBQUM7QUFFM0Q7OztHQUdHO0FBQ0gscUNBQTBELFVBQW1CLEVBQUU7O1FBQzdFLEtBQUssQ0FBQyw4Q0FBOEMsQ0FBQyxDQUFDO1FBQ3RELHdCQUF3QixFQUFFLENBQUM7UUFFM0IsS0FBSyxDQUFDLHlDQUF5QyxDQUFDLENBQUM7UUFDakQsSUFBSSxXQUFXLEdBQUcsYUFBSyxFQUFFLENBQUM7UUFDMUIsSUFBSSxZQUFZLEdBQUcsYUFBSyxFQUFFLENBQUM7UUFFM0IsS0FBSyxDQUFDLGdGQUFnRixDQUFDLENBQUM7UUFDeEYsZUFBZSxFQUFFLENBQUM7UUFFbEIsS0FBSyxDQUFDLDBCQUEwQixDQUFDLENBQUM7UUFDbEMsMEJBQVcsQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUV6QixLQUFLLENBQUMsNkJBQTZCLENBQUMsQ0FBQztRQUNyQyxlQUFPLENBQUMsMkJBQTRCLDRCQUFpQixXQUFZLFdBQVksV0FBWSxZQUFhLEdBQUcsQ0FBQyxDQUFDO1FBRTNHLEtBQUssQ0FBQywwQ0FBMEMsQ0FBQyxDQUFDO1FBQ2xELE1BQU0sbUNBQW1DLENBQUMsV0FBVyxFQUFFLFlBQVksQ0FBQyxDQUFDO1FBRXJFLEtBQUssQ0FBQyx1REFBdUQsQ0FBQyxDQUFDO1FBQy9ELE1BQU0sbUJBQWUsQ0FBQyxnQkFBZ0IsQ0FBQyxZQUFZLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDaEUsQ0FBQztD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S0FBSyxDQUFDLG9EQUFvRCxDQUFDLENBQUM7UUFDNUQsSUFBSSxHQUFHLEdBQUcsaUJBQVEsQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDckMsSUFBSSxJQUFJLEdBQUcsaUJBQVEsQ0FBQyxRQUFRLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDdkMsTUFBTSxtQkFBZSxDQUFDLGtCQUFrQixDQUFDLHlCQUFhLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDN0QsTUFBTSxtQkFBZSxDQUFDLGtCQUFrQixDQUFDLDBCQUFjLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDakUsQ0FBQztDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQge1xuICB1bmxpbmtTeW5jIGFzIHJtLFxuICByZWFkRmlsZVN5bmMgYXMgcmVhZEZpbGUsXG4gIHdyaXRlRmlsZVN5bmMgYXMgd3JpdGVGaWxlLFxuICBleGlzdHNTeW5jIGFzIGV4aXN0c1xufSBmcm9tICdmcyc7XG5pbXBvcnQgeyBzeW5jIGFzIHJpbXJhZiB9IGZyb20gJ3JpbXJhZic7XG5pbXBvcnQgY3JlYXRlRGVidWcgZnJvbSAnZGVidWcnO1xuXG5pbXBvcnQge1xuICByb290Q0FLZXlQYXRoLFxuICByb290Q0FDZXJ0UGF0aCxcbiAgY2FTZWxmU2lnbkNvbmZpZyxcbiAgb3BlbnNzbFNlcmlhbEZpbGVQYXRoLFxuICBvcGVuc3NsRGF0YWJhc2VGaWxlUGF0aCxcbiAgaXNXaW5kb3dzLFxuICBpc0xpbnV4LFxuICBjYVZlcnNpb25GaWxlXG59IGZyb20gJy4vY29uc3RhbnRzJztcbmltcG9ydCBjdXJyZW50UGxhdGZvcm0gZnJvbSAnLi9wbGF0Zm9ybXMnO1xuaW1wb3J0IHsgb3BlbnNzbCwgbWt0bXAgfSBmcm9tICcuL3V0aWxzJztcbmltcG9ydCB7IGdlbmVyYXRlS2V5IH0gZnJvbSAnLi9jZXJ0aWZpY2F0ZXMnO1xuaW1wb3J0IHsgT3B0aW9ucyB9IGZyb20gJy4vaW5kZXgnO1xuXG5jb25zdCBkZWJ1ZyA9IGNyZWF0ZURlYnVnKCdkZXZjZXJ0OmNlcnRpZmljYXRlLWF1dGhvcml0eScpO1xuXG4vKipcbiAqIEluc3RhbGwgdGhlIG9uY2UtcGVyLW1hY2hpbmUgdHJ1c3RlZCByb290IENBLiBXZSdsbCB1c2UgdGhpcyBDQSB0byBzaWduXG4gKiBwZXItYXBwIGNlcnRzLlxuICovXG5leHBvcnQgZGVmYXVsdCBhc3luYyBmdW5jdGlvbiBpbnN0YWxsQ2VydGlmaWNhdGVBdXRob3JpdHkob3B0aW9uczogT3B0aW9ucyA9IHt9KTogUHJvbWlzZTx2b2lkPiB7XG4gIGRlYnVnKGBDaGVja2luZyBpZiBvbGRlciBkZXZjZXJ0IGluc3RhbGwgaXMgcHJlc2VudGApO1xuICBzY3J1Yk9sZEluc2VjdXJlVmVyc2lvbnMoKTtcblxuICBkZWJ1ZyhgR2VuZXJhdGluZyBhIHJvb3QgY2VydGlmaWNhdGUgYXV0aG9yaXR5YCk7XG4gIGxldCByb290S2V5UGF0aCA9IG1rdG1wKCk7XG4gIGxldCByb290Q2VydFBhdGggPSBta3RtcCgpO1xuXG4gIGRlYnVnKGBHZW5lcmF0aW5nIHRoZSBPcGVuU1NMIGNvbmZpZ3VyYXRpb24gbmVlZGVkIHRvIHNldHVwIHRoZSBjZXJ0aWZpY2F0ZSBhdXRob3JpdHlgKTtcbiAgc2VlZENvbmZpZ0ZpbGVzKCk7XG5cbiAgZGVidWcoYEdlbmVyYXRpbmcgYSBwcml2YXRlIGtleWApO1xuICBnZW5lcmF0ZUtleShyb290S2V5UGF0aCk7XG5cbiAgZGVidWcoYEdlbmVyYXRpbmcgYSBDQSBjZXJ0aWZpY2F0ZWApO1xuICBvcGVuc3NsKGByZXEgLW5ldyAteDUwOSAtY29uZmlnIFwiJHsgY2FTZWxmU2lnbkNvbmZpZyB9XCIgLWtleSBcIiR7IHJvb3RLZXlQYXRoIH1cIiAtb3V0IFwiJHsgcm9vdENlcnRQYXRoIH1cImApO1xuXG4gIGRlYnVnKCdTYXZpbmcgY2VydGlmaWNhdGUgYXV0aG9yaXR5IGNyZWRlbnRpYWxzJyk7XG4gIGF3YWl0IHNhdmVDZXJ0aWZpY2F0ZUF1dGhvcml0eUNyZWRlbnRpYWxzKHJvb3RLZXlQYXRoLCByb290Q2VydFBhdGgpO1xuXG4gIGRlYnVnKGBBZGRpbmcgdGhlIHJvb3QgY2VydGlmaWNhdGUgYXV0aG9yaXR5IHRvIHRydXN0IHN0b3Jlc2ApO1xuICBhd2FpdCBjdXJyZW50UGxhdGZvcm0uYWRkVG9UcnVzdFN0b3Jlcyhyb290Q2VydFBhdGgsIG9wdGlvbnMpO1xufVxuXG4vKipcbiAqIE9sZGVyIHZlcnNpb25zIG9mIGRldmNlcnQgbGVmdCB0aGUgcm9vdCBjZXJ0aWZpY2F0ZSBrZXlzIHVuZ3VhcmRlZCBhbmRcbiAqIGFjY2Vzc2libGUgYnkgdXNlcmxhbmQgcHJvY2Vzc2VzLiBIZXJlLCB3ZSBjaGVjayBmb3IgZXZpZGVuY2Ugb2YgdGhpcyBvbGRlclxuICogdmVyc2lvbiwgYW5kIGlmIGZvdW5kLCB3ZSBkZWxldGUgdGhlIHJvb3QgY2VydGlmaWNhdGUga2V5cyB0byByZW1vdmUgdGhlXG4gKiBhdHRhY2sgdmVjdG9yLlxuICovXG5mdW5jdGlvbiBzY3J1Yk9sZEluc2VjdXJlVmVyc2lvbnMoKSB7XG4gIC8vIFVzZSB0aGUgb2xkIHZlcmlvbidzIGxvZ2ljIGZvciBkZXRlcm1pbmluZyBjb25maWcgZGlyZWN0b3J5XG4gIGxldCBjb25maWdEaXI6IHN0cmluZztcbiAgaWYgKGlzV2luZG93cyAmJiBwcm9jZXNzLmVudi5MT0NBTEFQUERBVEEpIHtcbiAgICBjb25maWdEaXIgPSBwYXRoLmpvaW4ocHJvY2Vzcy5lbnYuTE9DQUxBUFBEQVRBLCAnZGV2Y2VydCcsICdjb25maWcnKTtcbiAgfSBlbHNlIHtcbiAgICBsZXQgdWlkID0gcHJvY2Vzcy5nZXR1aWQgJiYgcHJvY2Vzcy5nZXR1aWQoKTtcbiAgICBsZXQgdXNlckhvbWUgPSAoaXNMaW51eCAmJiB1aWQgPT09IDApID8gcGF0aC5yZXNvbHZlKCcvdXNyL2xvY2FsL3NoYXJlJykgOiByZXF1aXJlKCdvcycpLmhvbWVkaXIoKTtcbiAgICBjb25maWdEaXIgPSBwYXRoLmpvaW4odXNlckhvbWUsICcuY29uZmlnJywgJ2RldmNlcnQnKTtcbiAgfVxuXG4gIC8vIERlbGV0ZSB0aGUgcm9vdCBjZXJ0aWZpY2F0ZSBrZXlzLCBhcyB3ZWxsIGFzIHRoZSBnZW5lcmF0ZWQgYXBwIGNlcnRpZmljYXRlc1xuICBkZWJ1ZyhgQ2hlY2tpbmcgJHsgY29uZmlnRGlyIH0gZm9yIGxlZ2FjeSBmaWxlcyAuLi5gKTtcbiAgW1xuICAgIHBhdGguam9pbihjb25maWdEaXIsICdvcGVuc3NsLmNvbmYnKSxcbiAgICBwYXRoLmpvaW4oY29uZmlnRGlyLCAnZGV2Y2VydC1jYS1yb290LmtleScpLFxuICAgIHBhdGguam9pbihjb25maWdEaXIsICdkZXZjZXJ0LWNhLXJvb3QuY3J0JyksXG4gICAgcGF0aC5qb2luKGNvbmZpZ0RpciwgJ2RldmNlcnQtY2EtdmVyc2lvbicpLFxuICAgIHBhdGguam9pbihjb25maWdEaXIsICdjZXJ0cycpXG4gIF0uZm9yRWFjaCgoZmlsZXBhdGgpID0+IHtcbiAgICBpZiAoZXhpc3RzKGZpbGVwYXRoKSkge1xuICAgICAgZGVidWcoYFJlbW92aW5nIGxlZ2FjeSBmaWxlOiAkeyBmaWxlcGF0aCB9YClcbiAgICAgIHJpbXJhZihmaWxlcGF0aCk7XG4gICAgfVxuICB9KTtcbn1cblxuLyoqXG4gKiBJbml0aWFsaXplcyB0aGUgZmlsZXMgT3BlblNTTCBuZWVkcyB0byBzaWduIGNlcnRpZmljYXRlcyBhcyBhIGNlcnRpZmljYXRlXG4gKiBhdXRob3JpdHksIGFzIHdlbGwgYXMgb3VyIENBIHNldHVwIHZlcnNpb25cbiAqL1xuZnVuY3Rpb24gc2VlZENvbmZpZ0ZpbGVzKCkge1xuICAvLyBUaGlzIGlzIHYyIG9mIHRoZSBkZXZjZXJ0IGNlcnRpZmljYXRlIGF1dGhvcml0eSBzZXR1cFxuICB3cml0ZUZpbGUoY2FWZXJzaW9uRmlsZSwgJzInKTtcbiAgLy8gT3BlblNTTCBDQSBmaWxlc1xuICB3cml0ZUZpbGUob3BlbnNzbERhdGFiYXNlRmlsZVBhdGgsICcnKTtcbiAgd3JpdGVGaWxlKG9wZW5zc2xTZXJpYWxGaWxlUGF0aCwgJzAxJyk7XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiB3aXRoQ2VydGlmaWNhdGVBdXRob3JpdHlDcmVkZW50aWFscyhjYjogKHsgY2FLZXlQYXRoLCBjYUNlcnRQYXRoIH06IHsgY2FLZXlQYXRoOiBzdHJpbmcsIGNhQ2VydFBhdGg6IHN0cmluZyB9KSA9PiBQcm9taXNlPHZvaWQ+IHwgdm9pZCkge1xuICBkZWJ1ZyhgUmV0cmlldmluZyBkZXZjZXJ0J3MgY2VydGlmaWNhdGUgYXV0aG9yaXR5IGNyZWRlbnRpYWxzYCk7XG4gIGxldCB0bXBDQUtleVBhdGggPSBta3RtcCgpO1xuICBsZXQgdG1wQ0FDZXJ0UGF0aCA9IG1rdG1wKCk7XG4gIGxldCBjYUtleSA9IGF3YWl0IGN1cnJlbnRQbGF0Zm9ybS5yZWFkUHJvdGVjdGVkRmlsZShyb290Q0FLZXlQYXRoKTtcbiAgbGV0IGNhQ2VydCA9IGF3YWl0IGN1cnJlbnRQbGF0Zm9ybS5yZWFkUHJvdGVjdGVkRmlsZShyb290Q0FDZXJ0UGF0aCk7XG4gIHdyaXRlRmlsZSh0bXBDQUtleVBhdGgsIGNhS2V5KTtcbiAgd3JpdGVGaWxlKHRtcENBQ2VydFBhdGgsIGNhQ2VydCk7XG4gIGF3YWl0IGNiKHsgY2FLZXlQYXRoOiB0bXBDQUtleVBhdGgsIGNhQ2VydFBhdGg6IHRtcENBQ2VydFBhdGggfSk7XG4gIHJtKHRtcENBS2V5UGF0aCk7XG4gIHJtKHRtcENBQ2VydFBhdGgpO1xufVxuXG5hc3luYyBmdW5jdGlvbiBzYXZlQ2VydGlmaWNhdGVBdXRob3JpdHlDcmVkZW50aWFscyhrZXlwYXRoOiBzdHJpbmcsIGNlcnRwYXRoOiBzdHJpbmcpIHtcbiAgZGVidWcoYFNhdmluZyBkZXZjZXJ0J3MgY2VydGlmaWNhdGUgYXV0aG9yaXR5IGNyZWRlbnRpYWxzYCk7XG4gIGxldCBrZXkgPSByZWFkRmlsZShrZXlwYXRoLCAndXRmLTgnKTtcbiAgbGV0IGNlcnQgPSByZWFkRmlsZShjZXJ0cGF0aCwgJ3V0Zi04Jyk7XG4gIGF3YWl0IGN1cnJlbnRQbGF0Zm9ybS53cml0ZVByb3RlY3RlZEZpbGUocm9vdENBS2V5UGF0aCwga2V5KTtcbiAgYXdhaXQgY3VycmVudFBsYXRmb3JtLndyaXRlUHJvdGVjdGVkRmlsZShyb290Q0FDZXJ0UGF0aCwgY2VydCk7XG59XG4iXX0=
\No newline at end of file