130 lines
16 KiB
JavaScript
130 lines
16 KiB
JavaScript
![]() |
"use strict";
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
exports.uninstall = exports.ensureCACertReadable = exports.withCertificateAuthorityCredentials = void 0;
|
||
|
const tslib_1 = require("tslib");
|
||
|
const fs_1 = require("fs");
|
||
|
const debug_1 = tslib_1.__importDefault(require("debug"));
|
||
|
const constants_1 = require("./constants");
|
||
|
const platforms_1 = tslib_1.__importDefault(require("./platforms"));
|
||
|
const utils_1 = require("./utils");
|
||
|
const certificates_1 = require("./certificates");
|
||
|
const debug = debug_1.default('devcert:certificate-authority');
|
||
|
/**
|
||
|
* Install the once-per-machine trusted root CA. We'll use this CA to sign
|
||
|
* per-app certs.
|
||
|
*/
|
||
|
function installCertificateAuthority(options = {}) {
|
||
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
||
|
debug(`Uninstalling existing certificates, which will be void once any existing CA is gone`);
|
||
|
uninstall();
|
||
|
constants_1.ensureConfigDirs();
|
||
|
debug(`Making a temp working directory for files to copied in`);
|
||
|
let rootKeyPath = utils_1.mktmp();
|
||
|
debug(`Generating the OpenSSL configuration needed to setup the certificate authority`);
|
||
|
seedConfigFiles();
|
||
|
debug(`Generating a private key`);
|
||
|
certificates_1.generateKey(rootKeyPath);
|
||
|
debug(`Generating a CA certificate`);
|
||
|
utils_1.openssl(['req', '-new', '-x509', '-config', constants_1.caSelfSignConfig, '-key', rootKeyPath, '-out', constants_1.rootCACertPath, '-days', '825']);
|
||
|
debug('Saving certificate authority credentials');
|
||
|
yield saveCertificateAuthorityCredentials(rootKeyPath);
|
||
|
debug(`Adding the root certificate authority to trust stores`);
|
||
|
yield platforms_1.default.addToTrustStores(constants_1.rootCACertPath, options);
|
||
|
});
|
||
|
}
|
||
|
exports.default = installCertificateAuthority;
|
||
|
/**
|
||
|
* Initializes the files OpenSSL needs to sign certificates as a certificate
|
||
|
* authority, as well as our CA setup version
|
||
|
*/
|
||
|
function seedConfigFiles() {
|
||
|
// This is v2 of the devcert certificate authority setup
|
||
|
fs_1.writeFileSync(constants_1.caVersionFile, '2');
|
||
|
// OpenSSL CA files
|
||
|
fs_1.writeFileSync(constants_1.opensslDatabaseFilePath, '');
|
||
|
fs_1.writeFileSync(constants_1.opensslSerialFilePath, '01');
|
||
|
}
|
||
|
function withCertificateAuthorityCredentials(cb) {
|
||
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
||
|
debug(`Retrieving devcert's certificate authority credentials`);
|
||
|
let tmpCAKeyPath = utils_1.mktmp();
|
||
|
let caKey = yield platforms_1.default.readProtectedFile(constants_1.rootCAKeyPath);
|
||
|
fs_1.writeFileSync(tmpCAKeyPath, caKey);
|
||
|
yield cb({ caKeyPath: tmpCAKeyPath, caCertPath: constants_1.rootCACertPath });
|
||
|
fs_1.unlinkSync(tmpCAKeyPath);
|
||
|
});
|
||
|
}
|
||
|
exports.withCertificateAuthorityCredentials = withCertificateAuthorityCredentials;
|
||
|
function saveCertificateAuthorityCredentials(keypath) {
|
||
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
||
|
debug(`Saving devcert's certificate authority credentials`);
|
||
|
let key = fs_1.readFileSync(keypath, 'utf-8');
|
||
|
yield platforms_1.default.writeProtectedFile(constants_1.rootCAKeyPath, key);
|
||
|
});
|
||
|
}
|
||
|
function certErrors() {
|
||
|
try {
|
||
|
utils_1.openssl(['x509', '-in', constants_1.rootCACertPath, '-noout']);
|
||
|
return '';
|
||
|
}
|
||
|
catch (e) {
|
||
|
return e.toString();
|
||
|
}
|
||
|
}
|
||
|
// This function helps to migrate from v1.0.x to >= v1.1.0.
|
||
|
/**
|
||
|
* Smoothly migrate the certificate storage from v1.0.x to >= v1.1.0.
|
||
|
* In v1.1.0 there are new options for retrieving the CA cert directly,
|
||
|
* to help third-party Node apps trust the root CA.
|
||
|
*
|
||
|
* If a v1.0.x cert already exists, then devcert has written it with
|
||
|
* platform.writeProtectedFile(), so an unprivileged readFile cannot access it.
|
||
|
* Pre-detect and remedy this; it should only happen once per installation.
|
||
|
*/
|
||
|
function ensureCACertReadable(options = {}) {
|
||
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
||
|
if (!certErrors()) {
|
||
|
return;
|
||
|
}
|
||
|
/**
|
||
|
* on windows, writeProtectedFile left the cert encrypted on *nix, the cert
|
||
|
* has no read permissions either way, openssl will fail and that means we
|
||
|
* have to fix it
|
||
|
*/
|
||
|
try {
|
||
|
const caFileContents = yield platforms_1.default.readProtectedFile(constants_1.rootCACertPath);
|
||
|
platforms_1.default.deleteProtectedFiles(constants_1.rootCACertPath);
|
||
|
fs_1.writeFileSync(constants_1.rootCACertPath, caFileContents);
|
||
|
}
|
||
|
catch (e) {
|
||
|
return installCertificateAuthority(options);
|
||
|
}
|
||
|
// double check that we have a live one
|
||
|
const remainingErrors = certErrors();
|
||
|
if (remainingErrors) {
|
||
|
return installCertificateAuthority(options);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
exports.ensureCACertReadable = ensureCACertReadable;
|
||
|
/**
|
||
|
* Remove as much of the devcert files and state as we can. This is necessary
|
||
|
* when generating a new root certificate, and should be available to API
|
||
|
* consumers as well.
|
||
|
*
|
||
|
* Not all of it will be removable. If certutil is not installed, we'll leave
|
||
|
* Firefox alone. We try to remove files with maximum permissions, and if that
|
||
|
* fails, we'll silently fail.
|
||
|
*
|
||
|
* It's also possible that the command to untrust will not work, and we'll
|
||
|
* silently fail that as well; with no existing certificates anymore, the
|
||
|
* security exposure there is minimal.
|
||
|
*/
|
||
|
function uninstall() {
|
||
|
platforms_1.default.removeFromTrustStores(constants_1.rootCACertPath);
|
||
|
platforms_1.default.deleteProtectedFiles(constants_1.domainsDir);
|
||
|
platforms_1.default.deleteProtectedFiles(constants_1.rootCADir);
|
||
|
platforms_1.default.deleteProtectedFiles(constants_1.getLegacyConfigDir());
|
||
|
}
|
||
|
exports.uninstall = uninstall;
|
||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2VydGlmaWNhdGUtYXV0aG9yaXR5LmpzIiwic291cmNlUm9vdCI6Ii4vIiwic291cmNlcyI6WyJjZXJ0aWZpY2F0ZS1hdXRob3JpdHkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7OztBQUFBLDJCQUlZO0FBQ1osMERBQWdDO0FBRWhDLDJDQVdxQjtBQUNyQixvRUFBMEM7QUFDMUMsbUNBQXlDO0FBQ3pDLGlEQUE2QztBQUc3QyxNQUFNLEtBQUssR0FBRyxlQUFXLENBQUMsK0JBQStCLENBQUMsQ0FBQztBQUUzRDs7O0dBR0c7QUFDSCxTQUE4QiwyQkFBMkIsQ0FBQyxVQUFtQixFQUFFOztRQUM3RSxLQUFLLENBQUMscUZBQXFGLENBQUMsQ0FBQztRQUM3RixTQUFTLEVBQUUsQ0FBQztRQUNaLDRCQUFnQixFQUFFLENBQUM7UUFFbkIsS0FBSyxDQUFDLHdEQUF3RCxDQUFDLENBQUM7UUFDaEUsSUFBSSxXQUFXLEdBQUcsYUFBSyxFQUFFLENBQUM7UUFFMUIsS0FBSyxDQUFDLGdGQUFnRixDQUFDLENBQUM7UUFDeEYsZUFBZSxFQUFFLENBQUM7UUFFbEIsS0FBSyxDQUFDLDBCQUEwQixDQUFDLENBQUM7UUFDbEMsMEJBQVcsQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUV6QixLQUFLLENBQUMsNkJBQTZCLENBQUMsQ0FBQztRQUNyQyxlQUFPLENBQUMsQ0FBQyxLQUFLLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsNEJBQWdCLEVBQUUsTUFBTSxFQUFFLFdBQVcsRUFBRSxNQUFNLEVBQUUsMEJBQWMsRUFBRSxPQUFPLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQztRQUU1SCxLQUFLLENBQUMsMENBQTBDLENBQUMsQ0FBQztRQUNsRCxNQUFNLG1DQUFtQyxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBRXZELEtBQUssQ0FBQyx1REFBdUQsQ0FBQyxDQUFDO1FBQy9ELE1BQU0sbUJBQWUsQ0FBQyxnQkFBZ0IsQ0FBQywwQkFBYyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ2xFLENBQUM7Q0FBQTtBQXRCRCw4Q0FzQkM7QUFFRDs7O0dBR0c7QUFDSCxTQUFTLGVBQWU7SUFDdEIsd0RBQXdEO0lBQ3hELGtCQUFTLENBQUMseUJBQWEsRUFBRSxHQUFHLENBQUMsQ0FBQztJQUM5QixtQkFBbUI7SUFDbkIsa0JBQVMsQ0FBQyxtQ0FBdUIsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUN2QyxrQkFBUyxDQUFDLGlDQUFxQixFQUFFLElBQUksQ0FBQyxDQUFDO0FBQ3pDLENBQUM7QUFFRCxTQUFzQixtQ0FBbUMsQ0FBQyxFQUFrRzs7UUFDMUosS0FBSyxDQUFDLHdEQUF3RCxDQUFDLENBQUM7UUFDaEUsSUFBSSxZQUFZLEdBQUcsYUFBSyxFQUFFLENBQUM7UUFDM0IsSUFBSSxLQUFLLEdBQUcsTUFBTSxtQkFBZSxDQUFDLGlCQUFpQixDQUFDLHlCQUFhLENBQUMsQ0FBQztRQUNuRSxrQkFBUyxDQUFDLFlBQVksRUFBRSxLQUFLLENBQUMsQ0FBQztRQUMvQixNQUFNLEVBQUUsQ0FBQyxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsVUFBVSxFQUFFLDBCQUFjLEVBQUUsQ0FBQyxDQUFDO1FBQ2xFLGVBQUUsQ0FBQyxZQUFZLENBQUMsQ0FBQztJQUNuQixDQUFDO0NBQUE7QUFQRCxrRkFPQztBQUVELFNBQWUsbUNBQW1DLENBQUMsT0FBZTs7UUFDaEUsS0FBSyxDQUFDLG9EQUFvRCxDQUFDLENBQUM7UUFDNUQsSUFBSSxHQUFHLEdBQUcsaUJBQVEsQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDckMsTUFBTSxtQkFBZSxDQUFDLGtCQUFrQixDQUFDLHlCQUFhLEVBQUUsR0FBRyxDQUFDLENBQUM7SUFDL0QsQ0FBQztDQUFBO0FBR0QsU0FBUyxVQUFVO0lBQ2pCLElBQUk7UUFDRixlQUFPLENBQUMsQ0FBQyxNQUFNLEVBQUUsS0FBSyxFQUFFLDBCQUFjLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUNuRCxPQUFPLEVBQUUsQ0FBQztLQUNYO0lBQUMsT0FBTyxDQUFDLEVBQUU7UUFDVixPQUFPLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQztLQUNyQjtBQUNILENBQUM7QUFFRCwyREFBMkQ7QUFDM0Q7Ozs7Ozs7O0dBUUc7QUFDSCxTQUFzQixvQkFBb0IsQ0FBQyxVQUFtQixFQUFFOztRQUM5RCxJQUFJLENBQUMsVUFBVSxFQUFFLEVBQUU7WUFDakIsT0FBTztTQUNSO1FBQ0Q7Ozs7V0FJRztRQUNILElBQUk7WUFDRixNQUFNLGNBQWMsR0FBRyxNQUFNLG1CQUFlLENBQUMsaUJBQWlCLENBQUMsMEJBQWMsQ0FBQyxDQUFDO1lBQy9FLG1CQUFlLENBQUMsb0JBQW9CLENBQUMsMEJBQWMsQ0FBQyxDQUFDO1lBQ3JELGtCQUFTLENBQUMsMEJBQWMsRUFBRSxjQUFjLENBQUMsQ0FBQztTQUMzQztRQUFDLE9BQU8sQ0FBQyxFQUFFO1lBQ1YsT0FBTywyQkFBMkIsQ0FBQyxPQUFPLENBQUMsQ0FBQztTQUM3QztRQUVELHVDQUF1QztRQUN2QyxNQUFNLGVBQWUsR0FBRyxVQUFVLEVBQUUsQ0FBQztRQUNyQyxJQUFJLGVBQWUsRUFBRTtZQUNuQixPQUFPLDJCQUEyQixDQUFDLE9BQU8sQ0FBQyxDQUFDO1NBQzdDO0lBQ0gsQ0FBQztDQUFBO0FBdEJELG9EQXNCQztBQUVEOzs7Ozs7Ozs7Ozs7R0FZRztBQUNILFNBQWdCLFNBQVM7SUFDdkIsbUJBQWUsQ0FBQyxxQkFBcUIsQ0FBQywwQkFBYyxDQUFDLENBQUM7SUFDdEQsbUJBQWUsQ0FBQyxvQkFBb0IsQ0FBQyxzQkFBVSxDQUFDLENBQUM7SUFDakQsbUJBQWUsQ0FBQyxvQkFBb0IsQ0FBQyxxQkFBUyxDQUFDLENBQUM7SUFDaEQsbUJBQWUsQ0FBQyxvQkFBb0IsQ0FBQyw4QkFBa0IsRUFBRSxDQUFDLENBQUM7QUFDN0QsQ0FBQztBQUxELDhCQUtDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgdW5saW5rU3luYyBhcyBybSxcbiAgcmVhZEZpbGVTeW5jIGFzIHJlYWRGaWxlLFxuICB3cml0ZUZpbGVTeW5jIGFzIHdyaXRlRmlsZVxufSBmcm9tICdmcyc7XG5pbXBvcnQgY3JlYXRlRGVidWcgZnJvbSAnZGVidWcnO1xuXG5pbXBvcnQge1xuICBkb21haW5zRGlyLFxuICByb290Q0FEaXIsXG4gIGVuc3VyZUNvbmZpZ0RpcnMsXG4gIGdldExlZ2FjeUNvbmZpZ0RpcixcbiAgcm9vdENBS2V5UGF0aCxcbiAgcm9vdENBQ2VydFBhdGgsXG4gIGNhU2VsZlNpZ25Db25maWcsXG4gIG9wZW5zc2xTZXJpYWxGaWxlUGF0aCxcbiAgb3BlbnNzbERhdGFiYXNlRmlsZVBhdGgsXG4gIGNhVmVyc2lvbkZpbGVcbn0gZnJvbSAnLi9jb25zdGFudHMnO1xuaW1wb3J0IGN1cnJlbnRQbGF0Zm9ybSBmcm9tICcuL3BsYXRmb
|