navy
Version:
Quick and powerful development environments using Docker and Docker Compose
213 lines (163 loc) • 6.08 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createCert = createCert;
exports.generateRootCa = generateRootCa;
exports.getCertsPath = getCertsPath;
exports.removeCert = removeCert;
var _path = _interopRequireDefault(require("path"));
var _config = require("../config");
var _errors = require("../errors");
var _chalk = _interopRequireDefault(require("chalk"));
var _fs = _interopRequireDefault(require("fs"));
var _nodeForge = require("node-forge");
var _navy = require("../navy");
const debug = require('debug')('navy:https');
function getCertsPath(create = false) {
const certsPath = _path.default.join((0, _config.getConfigDir)(), 'tls-certs');
if (!_fs.default.existsSync(certsPath)) {
if (create) {
debug(`Create ${certsPath} dir`); // $FlowIgnore
_fs.default.mkdirSync(certsPath, {
recursive: true
});
} else {
return '';
}
}
return certsPath;
}
async function removeCert(opts) {
const certsPath = getCertsPath();
const navy = await (0, _navy.getNavy)(opts.navy);
const serviceUrl = await navy.url(opts.disable);
const baseName = serviceUrl.split('//')[1];
const extensions = ['crt', 'key'];
for (const ext of extensions) {
const file = `${certsPath}/${baseName}.${ext}`;
if (_fs.default.existsSync(file)) {
try {
await _fs.default.unlinkSync(file);
debug(`File ${file} removed.`);
} catch (err) {
throw new _errors.NavyError(err);
}
}
}
}
async function generateRootCa() {
const tlsRootCaDir = (0, _config.getConfig)().tlsRootCaDir || _config.DEFAULT_TLS_ROOT_CA_DIR;
if (!_fs.default.existsSync(tlsRootCaDir)) {
debug(`Creating ${tlsRootCaDir} Root CA dir`);
try {
// $FlowIgnore
_fs.default.mkdirSync(tlsRootCaDir, {
recursive: true
});
} catch (err) {
throw new _errors.NavyError(err);
}
}
if (_fs.default.existsSync(`${tlsRootCaDir}/ca.crt`) && _fs.default.existsSync(`${tlsRootCaDir}/ca.key`)) {
debug(`Root CA already exists, skipping generation`);
return;
}
debug('Generating Root CA');
debug('Generating 2048-bit key-pair...');
const keys = _nodeForge.pki.rsa.generateKeyPair(2048);
debug('Creating self-signed certificate...');
const cert = _nodeForge.pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = '01';
cert.validity.notBefore = new Date();
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 5);
const attrs = [{
name: 'commonName',
value: 'navy-dev-ca.local'
}, {
name: 'organizationName',
value: 'navy-dev'
}];
cert.setSubject(attrs);
cert.setIssuer(attrs);
cert.setExtensions([{
name: 'basicConstraints',
cA: true
}, {
name: 'subjectKeyIdentifier'
}, {
name: 'authorityKeyIdentifier'
}]);
try {
// self-sign certificate
cert.sign(keys.privateKey, _nodeForge.md.sha256.create()); // PEM-format keys and cert
const pem = {
privateKey: _nodeForge.pki.privateKeyToPem(keys.privateKey),
publicKey: _nodeForge.pki.publicKeyToPem(keys.publicKey),
certificate: _nodeForge.pki.certificateToPem(cert)
};
_fs.default.writeFileSync(tlsRootCaDir + '/ca.key', pem.privateKey, {
mode: 0o400
});
_fs.default.writeFileSync(tlsRootCaDir + '/ca.pub.key', pem.publicKey, {
mode: 0o640
});
_fs.default.writeFileSync(tlsRootCaDir + '/ca.crt', pem.certificate, {
mode: 0o640
});
console.log(_chalk.default.green(`✅ CA Certificate created at ${tlsRootCaDir}/ca.crt`));
console.log(_chalk.default.yellow(`⚠️ Importing a self-signed CA into a browser/truststore/keychain is not advisable ⚠️`));
} catch (e) {
throw new _errors.NavyError(e);
}
}
async function createCert(opts) {
const tlsRootCaDir = (0, _config.getConfig)().tlsRootCaDir || _config.DEFAULT_TLS_ROOT_CA_DIR;
const certName = opts.hostName || opts.serviceUrl.split('//')[1];
const certsPath = getCertsPath(true);
if (_fs.default.existsSync(`${certsPath}/${certName}.crt`)) {
debug(`Certificate for ${certName} already exists, skipping generation`);
return;
}
await generateRootCa();
const caCertString = _fs.default.readFileSync(`${tlsRootCaDir}/ca.crt`, 'utf8');
const caKeyString = _fs.default.readFileSync(`${tlsRootCaDir}/ca.key`, 'utf8');
debug(`Generating cert for ${certName} in ${certsPath}`);
const privateCAKey = _nodeForge.pki.privateKeyFromPem(caKeyString);
const keys = _nodeForge.pki.rsa.generateKeyPair(2048);
const cert = _nodeForge.pki.createCertificate();
const caCert = _nodeForge.pki.certificateFromPem(caCertString);
cert.publicKey = keys.publicKey;
cert.serialNumber = Math.floor(Math.random() * (99 - 2) + 2).toString();
cert.validity.notBefore = new Date();
cert.validity.notBefore.setDate(cert.validity.notBefore.getDate() - 1);
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 2);
const attrs = [{
name: 'commonName',
value: certName
}, {
name: 'organizationName',
value: 'navy-dev'
}];
try {
cert.setSubject(attrs);
cert.setIssuer(caCert.subject.attributes);
cert.setExtensions([{
name: 'extKeyUsage',
serverAuth: true
}]);
cert.sign(privateCAKey, _nodeForge.md.sha256.create()); // PEM-format keys and cert
const pem = {
privateKey: _nodeForge.pki.privateKeyToPem(keys.privateKey),
certificate: _nodeForge.pki.certificateToPem(cert) // publicKey: pki.publicKeyToPem(keys.publicKey),
};
_fs.default.writeFileSync(`${certsPath}/${certName}.key`, pem.privateKey);
_fs.default.writeFileSync(`${certsPath}/${certName}.crt`, pem.certificate); // fs.writeFileSync(`${certsPath}/${certName}.pub.key`, pem.publicKey)
} catch (e) {
throw new _errors.NavyError(e);
}
}