From af07bc735b42c2f2dfb4f9a2feb84827aa8ee19e Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 10 Aug 2016 13:10:00 -0400 Subject: [PATCH] move approval work to worker --- master.js | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ serve.js | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 master.js create mode 100644 serve.js diff --git a/master.js b/master.js new file mode 100644 index 0000000..d26fe37 --- /dev/null +++ b/master.js @@ -0,0 +1,63 @@ +'use strict'; + +module.exports.create = function (opts) { + if (!opts.letsencrypt) { opts.letsencrypt = require('letsencrypt').create({ server: opts.server }); } + if ('function' === typeof opts.approve) { + throw new Error("You must provide opts.approve(options, certs, callback) to approve certificates"); + } + + function log(debug) { + if (!debug) { + return; + } + + var args = Array.prototype.slice.call(arguments); + args.shift(); + args.unshift("[le/lib/core.js]"); + console.log.apply(console, args); + } + + opts._pending = {}; + opts._le = opts.letsencrypt; + opts.addWorker = function (worker) { + + worker.on('online', function () { + log(opts.debug, 'worker is up'); + }); + + worker.on('message', function (msg) { + log(opts.debug, 'Message from worker ' + worker.pid, msg, msg && msg.type); + if ('LE_REQUEST' !== (msg && msg.type)) { + return; + } + + opts.approveDomains(msg.options, msg.certs, function (err, results) { + if (err) { + log(opts.debug, 'Approval got ERROR', err.stack || err); + worker.send({ type: 'LE_RESPONSE', error: err }); + return; + } + + var promise; + + if (results.certs) { + promise = opts._le.renew(results.options, results.certs); + } + else { + promise = opts._le.register(results.options); + } + + promise.then(function (certs) { + log(opts.debug, 'Approval got certs', certs); + // certs = { subject, domains, issuedAt, expiresAt, privkey, cert, chain }; + worker.send({ type: 'LE_RESPONSE', certs: certs }); + }, function (err) { + log(opts.debug, 'Approval got ERROR', err.stack || err); + worker.send({ type: 'LE_RESPONSE', error: err }); + }); + }); + }); + }; + + return opts; +}; diff --git a/serve.js b/serve.js new file mode 100644 index 0000000..6f2e716 --- /dev/null +++ b/serve.js @@ -0,0 +1,71 @@ +'use strict'; + +var cluster = require('cluster'); +var master; +var numCores = 2; // // Math.max(2, require('os').cpus().length) +var i; + +if (cluster.isMaster) { + master = require('./master').create({ + debug: true + + + + , server: 'staging' + + + + , approveDomains: function (options, certs, cb) { + // Depending on your setup it may be more efficient + // for you to implement the approveDomains function + // in your master or in your workers. + // + // Since we implement it in the worker (below) in this example + // we'll give it an immediate approval here in the master + cb(null, { options: options, certs: certs }); + } + }); + + + + for (i = 0; i < numCores; i += 1) { + master.addWorker(cluster.fork()); + } +} +else { + require('./worker').create({ + debug: true + + // We want both to renew well before the expiration date + // and also to stagger the renewals, just a touch + // here we specify to renew between 10 and 15 days + , notBefore: 15 * 24 * 60 * 60 * 1000 + , notAfter: 10 * 24 * 60 * 60 * 1000 // optional + + , approveDomains: function (opts, certs, cb) { + // opts = { domains, email, agreeTos, tosUrl } + // certs = { subject, altnames, expiresAt, issuedAt } + + + + // We might want to do a check to make sure that all of the domains + // specified in altnames are still approved to be renewed and have + // the correct dns entries, but generally speaking it's probably okay + // for renewals to be automatic + if (certs) { + // modify opts.domains to overwrite certs.altnames in renewal + cb(null, { options: opts, certs: certs }); + return; + } + + + + + // This is where we would check our database to make sure that + // this user (specified by email address) has agreed to the terms + // and do some check that they have access to this domain + opts.agreeTos = true; + cb(null, { options: opts }); + } + }); +}