UNPKG

stonix-wallet

Version:

A minimalistic wallet GUI for c-lightning

182 lines (141 loc) 6.42 kB
"use strict"; 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; };