stonix-wallet
Version:
A minimalistic wallet GUI for c-lightning
182 lines (141 loc) • 6.42 kB
JavaScript
;
var _nodeForge = _interopRequireDefault(require("node-forge"));
var _path = _interopRequireDefault(require("path"));
var _https = _interopRequireDefault(require("https"));
var _http = _interopRequireDefault(require("http"));
var _net = _interopRequireDefault(require("net"));
var _isIp = _interopRequireDefault(require("is-ip"));
var _fs = _interopRequireDefault(require("fs"));
var _mkdirp = _interopRequireDefault(require("mkdirp"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
var defaultDir = _path["default"].join(require('os').homedir(), '.stonix-wallet', 'tls');
module.exports = function (app) {
var name = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : app.settings.host;
var dir = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : defaultDir;
var leEmail = arguments.length > 3 ? arguments[3] : undefined;
var tlsOpt = leEmail ? letsencrypt(name, dir, leEmail) : selfsigned(name, dir),
redir = function redir(req, res) {
return res.writeHead(301, {
Location: "https://".concat(req.headers.host || name, "/")
}), res.end();
},
server = createMultiServer(tlsOpt, app, redir);
tlsOpt.cert && app.get('/cert.pem', function (req, res) {
return res.type('pem').send(tlsOpt.cert);
}); // @TODO allow downloading letsencrypt's cert
return new Promise(function (resolve) {
return server.listen(app.settings.port, app.settings.host, function (_) {
return resolve("https://".concat(app.settings.host, ":").concat(server.address().port));
});
});
}; // Self-signed certificate (no CA)
var selfsigned = function selfsigned(name, dir) {
if (_fs["default"].existsSync(_path["default"].join(dir, 'key.pem'))) {
var keyPem = _fs["default"].readFileSync(_path["default"].join(dir, 'key.pem')),
certPem = _fs["default"].readFileSync(_path["default"].join(dir, 'cert.pem')),
cert = _nodeForge["default"].pki.certificateFromPem(certPem),
certDer = _nodeForge["default"].asn1.toDer(_nodeForge["default"].pki.certificateToAsn1(cert)).getBytes(),
fprint = _nodeForge["default"].md.sha1.create().update(certDer).digest().toHex().match(/../g).join(':');
console.log("Loaded TLS certificate with fingerprint ".concat(fprint, " from ").concat(dir));
return {
key: keyPem,
cert: certPem
};
}
var extensions = [].concat(defaultExt, [{
name: 'subjectAltName',
altNames: [(0, _isIp["default"])(name) ? {
type: 7,
ip: name
} : {
type: 2,
value: name
}]
}]);
var pems = require('selfsigned').generate([{
name: 'commonName',
value: name
}], {
extensions: extensions,
keySize: 2048,
algorithm: 'sha256'
});
!_fs["default"].existsSync(dir) && _mkdirp["default"].sync(dir);
_fs["default"].writeFileSync(_path["default"].join(dir, 'key.pem'), pems["private"]);
_fs["default"].writeFileSync(_path["default"].join(dir, 'cert.pem'), pems.cert);
console.log("Created TLS certificate with fingerprint ".concat(pems.fingerprint, " in ").concat(dir));
return {
key: pems["private"],
cert: pems.cert
};
};
var defaultExt = [{
name: 'basicConstraints',
cA: true
}, {
name: 'keyUsage',
keyCertSign: true,
digitalSignature: true,
nonRepudiation: true,
keyEncipherment: true,
dataEncipherment: true
}]; // Automatic CA-signed TLS certificate registration via LetsEncrypt
var letsencrypt = function letsencrypt(name, dir, email) {
console.log("Setting up LetsEncrypt CA-signed TLS cert for ".concat(name, " with email ").concat(email, " in ").concat(dir));
var gl = require('greenlock').create({
version: 'draft-12',
server: 'https://acme-v02.api.letsencrypt.org/directory',
configDir: _path["default"].join(dir, 'letsencrypt'),
store: require('greenlock-store-fs'),
approveDomains: [name],
email: email,
agreeTos: true,
debug: !!process.env.LE_DEBUG
});
if (!process.env.LE_NOVERIFY) {
console.log('Starting LetsEncrypt verification server');
var noCon = function noCon(req, res) {
return res.writeHead(204), res.end();
}; // 204 No Content
_http["default"].createServer(gl.middleware(noCon)).listen(process.env.LE_PORT || 80).on('error', function (err) {
console.error("ERROR: ".concat(err.code, " while starting vertification server on ").concat(err.address, ":").concat(err.port));
console.error(err.stack || err);
if (err.errno == 'EACCES') {
console.error("\nYou don't have prmission to bind on ".concat(err.address, ":").concat(err.port, "."));
console.error('See https://github.com/shesek/stonix-wallet/blob/master/doc/tls.md#letsencrypt-integration for advice.');
} else if (err.errno == 'EADDRINUSE') {
console.error("\n".concat(err.address, ":").concat(err.port, " is already being used by some other program. Stop it and try again."));
}
process.exit(1);
});
}
return gl.tlsOptions;
}; // Create a server capable of handling both TLS and plain HTTP requests
// This is done to redirect users accessing the TLS server without using 'https://'
// Protocol detection/delegation based on https://stackoverflow.com/a/42019773/865693
var createMultiServer = function createMultiServer(tlsOpt, tlsHandler, plainHandler) {
var server = _net["default"].createServer(function (socket) {
socket.once('error', function (err) {
console.error('socket error:', err.stack || err);
socket.destroy();
});
socket.once('data', function (buff) {
socket.pause(); // Determine if this is a TLS or plain HTTP request
var _byte = buff[0],
proto = _byte === 22 ? 'https' : 32 < _byte && _byte < 127 ? 'http' : null;
if (!proto) {
console.error(new Error('Cannot detect HTTP/TLS request').stack);
return socket.destroy();
} // Push the buffer back onto the front of the data stream
socket.unshift(buff); // Delegate the socket to the appropriate handler
socket.server = server[proto];
server[proto].emit('connection', socket);
process.nextTick(function (_) {
return socket.resume();
});
});
});
server.http = _http["default"].createServer(plainHandler);
server.https = _https["default"].createServer(tlsOpt, tlsHandler);
return server;
};