Compare commits

...

No commits in common. "master" and "greenlock" have entirely different histories.

11 changed files with 394 additions and 527 deletions

View File

@ -1,7 +0,0 @@
{
"bracketSpacing": true,
"printWidth": 120,
"tabWidth": 4,
"trailingComma": "none",
"useTabs": false
}

150
README.md
View File

@ -1,68 +1,47 @@
# Replaced: Use Greenlock Express v3 <!-- BANNER_TPL_BEGIN -->
See https://git.rootprojects.org/root/greenlock-express.js About Daplie: We're taking back the Internet!
--------------
```js Down with Google, Apple, and Facebook!
"use strict";
var pkg = require("./package.json"); We're re-decentralizing the web and making it read-write again - one home cloud system at a time.
require("greenlock-express")
.init(function getConfig() {
// Greenlock Config
return { Tired of serving the Empire? Come join the Rebel Alliance:
package: { name: pkg.name, version: pkg.version },
maintainerEmail: pkg.author,
// put cluster on full throttle! <a href="mailto:jobs@daplie.com">jobs@daplie.com</a> | [Invest in Daplie on Wefunder](https://daplie.com/invest/) | [Pre-order Cloud](https://daplie.com/preorder/), The World's First Home Server for Everyone
cloudnative: true
webscale: true
cluster: true
};
})
.serve(httpsWorker);
function httpsWorker(glx) { <!-- BANNER_TPL_END -->
// Serves on 80 and 443
// Get's SSL certificates magically!
glx.serveApp(function(req, res) { [![Join the chat at https://gitter.im/Daplie/letsencrypt-express](https://badges.gitter.im/Daplie/letsencrypt-express.svg)](https://gitter.im/Daplie/letsencrypt-express?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
res.end("Hello, Encrypted World!");
});
}
```
# OLD STUFF BELOW | [greenlock (lib)](https://git.daplie.com/Daplie/node-greenlock)
| [greenlock-cli](https://git.daplie.com/Daplie/greenlock-cli)
# (Preserved for historical reference) | [greenlock-express](https://git.daplie.com/Daplie/greenlock-express)
| A [Root](https://therootcompany.com) Project
| [greenlock (lib)](https://git.coolaj86.com/coolaj86/greenlock.js)
| [greenlock-cli](https://git.coolaj86.com/coolaj86/greenlock-cli.js)
| [greenlock-express](https://git.coolaj86.com/coolaj86/greenlock-express.js)
| **greenlock-cluster** | **greenlock-cluster**
| [greenlock-koa](https://git.coolaj86.com/coolaj86/greenlock-koa.js) | [greenlock-koa](https://git.daplie.com/Daplie/greenlock-koa)
| [greenlock-hapi](https://git.coolaj86.com/coolaj86/greenlock-hapi.js) | [greenlock-hapi](https://git.daplie.com/Daplie/greenlock-hapi)
| |
# greenlock-cluster greenlock-cluster (letsencrypt-cluster)
===================
(previously letsencrypt-cluster)
Use automatic letsencrypt with node on multiple cores or even multiple machines. Use automatic letsencrypt with node on multiple cores or even multiple machines.
- Take advantage of multi-core computing * Take advantage of multi-core computing
- Process certificates in master * Process certificates in master
- Serve https from multiple workers * Serve https from multiple workers
- Can work with any clustering strategy [#1](https://github.com/Daplie/letsencrypt-cluster/issues/1) * Can work with any clustering strategy [#1](https://github.com/Daplie/letsencrypt-cluster/issues/1)
# Install Install
=======
```bash ```bash
npm install --save greenlock-cluster@2.x npm install --save greenlock-cluster@2.x
``` ```
# Usage Usage
=====
In a cluster environment you have some main file that boots your app In a cluster environment you have some main file that boots your app
and then conditionally loads certain code based on whether that fork and then conditionally loads certain code based on whether that fork
@ -72,34 +51,35 @@ In such a file you might want to define some of the options that need
to be shared between both the master and the worker, like this: to be shared between both the master and the worker, like this:
`boot.js`: `boot.js`:
```javascript ```javascript
"use strict"; 'use strict';
var cluster = require("cluster"); var cluster = require('cluster');
var path = require("path"); var path = require('path');
var os = require("os"); var os = require('os');
var main; var main;
var sharedOptions = { var sharedOptions = {
webrootPath: path.join(os.tmpdir(), "acme-challenge"), // /tmp/acme-challenge webrootPath: path.join(os.tmpdir(), 'acme-challenge') // /tmp/acme-challenge
// used by le-challenge-fs, the default plugin // used by le-challenge-fs, the default plugin
renewWithin: 14 * 24 * 60 * 60 * 1000, // 10 days before expiration , renewWithin: 10 * 24 * 60 * 60 * 1000 // 10 days before expiration
debug: true , debug: true
}; };
if (cluster.isMaster) { if (cluster.isMaster) {
main = require("./master"); main = require('./master');
} else { }
main = require("./worker"); else {
main = require('./worker');
} }
main.init(sharedOptions); main.init(sharedOptions);
``` ```
## Master Master
------
We think it makes the most sense to load greenlock in master. We think it makes the most sense to load greenlock in master.
This can prevent race conditions (see [node-letsencrypt#45](https://github.com/Daplie/node-letsencrypt/issues/45)) This can prevent race conditions (see [node-letsencrypt#45](https://github.com/Daplie/node-letsencrypt/issues/45))
@ -112,7 +92,6 @@ The master takes **the same arguments** as `node-greenlock` (`challenge`, `store
plus a few extra (`approveDomains`... okay, just one extra): plus a few extra (`approveDomains`... okay, just one extra):
`master.js`: `master.js`:
```javascript ```javascript
'use strict'; 'use strict';
@ -123,9 +102,7 @@ module.exports.init = function (sharedOpts) {
var leMaster = require('greenlock-cluster/master').create({ var leMaster = require('greenlock-cluster/master').create({
debug: sharedOpts.debug debug: sharedOpts.debug
// You MUST change this to 'https://acme-v02.api.letsencrypt.org/directory' in production , server: 'staging' // CHANGE TO PRODUCTION
server: 'https://acme-staging-v02.api.letsencrypt.org/directory'
, version: 'draft-11' // Let's Encrypt v2
, renewWithin: sharedOpts.renewWithin , renewWithin: sharedOpts.renewWithin
@ -155,43 +132,43 @@ All options are passed directly to `node-greenlock`
(in other works, `leMaster` is a `greenlock` instance), (in other works, `leMaster` is a `greenlock` instance),
but a few are only actually used by `greenlock-cluster`. but a few are only actually used by `greenlock-cluster`.
- `leOptions.approveDomains(options, certs, cb)` is special for `greenlock-cluster`, but will probably be included in `node-greenlock` in the future (no API change). * `leOptions.approveDomains(options, certs, cb)` is special for `greenlock-cluster`, but will probably be included in `node-greenlock` in the future (no API change).
- `leMaster.addWorker(worker)` is added by `greenlock-cluster` and **must be called** for each new worker. * `leMaster.addWorker(worker)` is added by `greenlock-cluster` and **must be called** for each new worker.
## Worker Worker
------
The worker takes _similar_ arguments to `node-greenlock`, The worker takes *similar* arguments to `node-greenlock`,
but only ones that are useful for determining certificate but only ones that are useful for determining certificate
renewal and for `le.challenge.get`. renewal and for `le.challenge.get`.
If you want to a non-default `le.challenge` If you want to a non-default `le.challenge`
`worker.js`: `worker.js`:
```javascript ```javascript
"use strict"; 'use strict';
module.exports.init = function (sharedOpts) { module.exports.init = function (sharedOpts) {
var leWorker = require("greenlock-cluster/worker").create({ var leWorker = require('greenlock-cluster/worker').create({
debug: sharedOpts.debug, debug: sharedOpts.debug
renewWithin: sharedOpts.renewWithin, , renewWithin: sharedOpts.renewWithin
webrootPath: sharedOpts.webrootPath, , webrootPath: sharedOpts.webrootPath
// , challenge: require('le-challenge-fs').create({ webrootPath: '...', ... }) // , challenge: require('le-challenge-fs').create({ webrootPath: '...', ... })
approveDomains: function(workerOptions, certs, cb) { , approveDomains: function (workerOptions, certs, cb) {
// opts = { domains, email, agreeTos, tosUrl } // opts = { domains, email, agreeTos, tosUrl }
// certs = { subject, altnames, expiresAt, issuedAt } // certs = { subject, altnames, expiresAt, issuedAt }
var results = { var results = {
domain: workerOptions.domains[0], domain: workerOptions.domains[0]
options: { , options: {
domains: workerOptions.domains domains: workerOptions.domains
}, }
certs: certs , certs: certs
}; };
if (certs) { if (certs) {
@ -208,8 +185,8 @@ module.exports.init = function(sharedOpts) {
// 2. Assign a default email if it isn't in the system // 2. Assign a default email if it isn't in the system
// 3. If the email has no le account, `agreeToTerms` will fire unless `agreeTos` is preset // 3. If the email has no le account, `agreeToTerms` will fire unless `agreeTos` is preset
results.options.email = "john.doe@example.com"; results.options.email = 'john.doe@example.com'
results.options.agreeTos = true; // causes agreeToTerms to be skipped results.options.agreeTos = true // causes agreeToTerms to be skipped
cb(null, results); cb(null, results);
} }
}); });
@ -218,11 +195,11 @@ module.exports.init = function(sharedOpts) {
res.end("Hello, World!"); res.end("Hello, World!");
} }
var redirectHttps = require("redirect-https")(); var redirectHttps = require('redirect-https')();
var plainServer = require("http").createServer(leWorker.middleware(redirectHttps)); var plainServer = require('http').createServer(leWorker.middleware(redirectHttps));
plainServer.listen(80); plainServer.listen(80);
var server = require("https").createServer(leWorker.httpsOptions, leWorker.middleware(app)); var server = require('https').createServer(leWorker.httpsOptions, leWorker.middleware(app));
server.listen(443); server.listen(443);
}; };
``` ```
@ -232,15 +209,16 @@ module.exports.init = function(sharedOpts) {
`node-greenlock` is **not used** directly by the worker, `node-greenlock` is **not used** directly by the worker,
but certain options are shared because certain logic is duplicated. but certain options are shared because certain logic is duplicated.
- `leOptions.renewWithin` is shared so that the worker knows how earlier to request a new cert * `leOptions.renewWithin` is shared so that the worker knows how earlier to request a new cert
- `leOptions.renewBy` is passed to `le-sni-auto` so that it staggers renewals between `renewWithin` (latest) and `renewBy` (earlier) * `leOptions.renewBy` is passed to `le-sni-auto` so that it staggers renewals between `renewWithin` (latest) and `renewBy` (earlier)
- `leWorker.middleware(nextApp)` uses `greenlock/middleware` for GET-ing `http-01`, hence `sharedOptions.webrootPath` * `leWorker.middleware(nextApp)` uses `greenlock/middleware` for GET-ing `http-01`, hence `sharedOptions.webrootPath`
- `leWorker.httpsOptions` has a default localhost certificate and the `SNICallback`. * `leWorker.httpsOptions` has a default localhost certificate and the `SNICallback`.
There are a few options that aren't shown in these examples, so if you need to change something There are a few options that aren't shown in these examples, so if you need to change something
that isn't shown here, look at the code (it's not that much) or open an issue. that isn't shown here, look at the code (it's not that much) or open an issue.
## Message Passing Message Passing
---------------
The master and workers will communicate through `process.on('message', fn)`, `process.send({})`, The master and workers will communicate through `process.on('message', fn)`, `process.send({})`,
`worker.on('message', fn)`and `worker.send({})`. `worker.on('message', fn)`and `worker.send({})`.

View File

@ -1,4 +1,5 @@
## greenlock cluster examples greenlock cluster examples
-------------------
First you need to change the email address in `examples/worker.js`. First you need to change the email address in `examples/worker.js`.

View File

@ -1,17 +1,21 @@
"use strict"; 'use strict';
var cluster = require("cluster"); var cluster = require('cluster');
module.exports.init = function (sharedOpts) { module.exports.init = function (sharedOpts) {
var numCores = 2; // // Math.max(2, require('os').cpus().length) var numCores = 2; // // Math.max(2, require('os').cpus().length)
var i; var i;
var master = require("../master").create({ var master = require('../master').create({
debug: true, debug: true
server: "staging",
webrootPath: sharedOpts.webrootPath,
approveDomains: function(masterOptions, certs, cb) {
, server: 'staging'
, webrootPath: sharedOpts.webrootPath
, approveDomains: function (masterOptions, certs, cb) {
// Depending on your setup it may be more efficient // Depending on your setup it may be more efficient
// for you to implement the approveDomains function // for you to implement the approveDomains function
// in your master or in your workers. // in your master or in your workers.
@ -23,6 +27,8 @@ module.exports.init = function(sharedOpts) {
} }
}); });
for (i = 0; i < numCores; i += 1) { for (i = 0; i < numCores; i += 1) {
master.addWorker(cluster.fork()); master.addWorker(cluster.fork());
} }

View File

@ -1,27 +1,33 @@
"use strict"; 'use strict';
var cluster = require("cluster"); var cluster = require('cluster');
var main; var main;
// You'll often see examples where people use cluster // You'll often see examples where people use cluster
// master and worker all in the same file, which is fine, // master and worker all in the same file, which is fine,
// but in order to conserve memory and especially to be // but in order to conserve memory and especially to be
// less confusing, I'm splitting the code into two files // less confusing, I'm splitting the code into two files
if (cluster.isMaster) { if (cluster.isMaster) {
main = require("./master"); main = require('./master');
} else {
main = require("./worker");
} }
else {
main = require('./worker');
}
// this is nothing greenlock-cluster specific // this is nothing greenlock-cluster specific
// I'm just arbitrarily choosing to share some configuration // I'm just arbitrarily choosing to share some configuration
// that I know I'm going to use in both places // that I know I'm going to use in both places
main.init({ main.init({
// Depending on the strategy, the whole le-challenge-<<strategy>> // Depending on the strategy, the whole le-challenge-<<strategy>>
// could be shared between worker and server, but since I'm just // could be shared between worker and server, but since I'm just
// using using le-challenge-fs (as you'll see), I'm only sharing the webrootPath // using using le-challenge-fs (as you'll see), I'm only sharing the webrootPath
webrootPath: require("os").tmpdir() + require("path").sep + "acme-challenge", webrootPath: require('os').tmpdir() + require('path').sep + 'acme-challenge'
// this is used both by node-greenlock (master) and le-sni-auto (worker) // this is used both by node-greenlock (master) and le-sni-auto (worker)
renewWithin: 15 * 24 * 60 * 60 * 1000 , renewWithin: 15 * 24 * 60 * 60 * 1000
}); });

View File

@ -1,16 +1,22 @@
"use strict"; 'use strict';
module.exports.init = function (sharedOpts) { module.exports.init = function (sharedOpts) {
var worker = require("../worker").create({ var worker = require('../worker').create({
debug: true, debug: true
// We want both to renew well before the expiration date // We want both to renew well before the expiration date
// and also to stagger the renewals, just a touch // and also to stagger the renewals, just a touch
// here we specify to renew between 10 and 15 days // here we specify to renew between 10 and 15 days
renewWithin: sharedOpts.renewWithin, , renewWithin: sharedOpts.renewWithin
renewBy: 10 * 24 * 60 * 60 * 1000, // optional , renewBy: 10 * 24 * 60 * 60 * 1000 // optional
, webrootPath: sharedOpts.webrootPath
webrootPath: sharedOpts.webrootPath,
/* /*
challenge: { challenge: {
@ -27,22 +33,25 @@ module.exports.init = function(sharedOpts) {
} }
*/ */
// There are two approval processes: // There are two approval processes:
// 1. emails are tied to private keys (accounts) which must agree to the tos url // 1. emails are tied to private keys (accounts) which must agree to the tos url
// 2. domains are tied to accounts (and should be verifiable via loopback) // 2. domains are tied to accounts (and should be verifiable via loopback)
approveDomains: function(workerOptions, certs, cb) { , approveDomains: function (workerOptions, certs, cb) {
// opts = { domains, email, agreeTos, tosUrl } // opts = { domains, email, agreeTos, tosUrl }
// certs = { subject, altnames, expiresAt, issuedAt } // certs = { subject, altnames, expiresAt, issuedAt }
var results = { var results = {
domain: workerOptions.domains[0], domain: workerOptions.domains[0]
options: { , options: {
domains: (certs && certs.altnames) || workerOptions.domains, domains: certs && certs.altnames || workerOptions.domains
email: "john.doe@example.com", , email: 'john.doe@example.com'
agreeTos: true , agreeTos: true
}, }
certs: certs , certs: certs
}; };
// We might want to do a check to make sure that all of the domains // 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 // specified in altnames are still approved to be renewed and have
// the correct dns entries, but generally speaking it's probably okay // the correct dns entries, but generally speaking it's probably okay
@ -53,6 +62,9 @@ module.exports.init = function(sharedOpts) {
return; return;
} }
// This is where we would check our database to make sure that // This is where we would check our database to make sure that
// this user (specified by email address) has agreed to the terms // this user (specified by email address) has agreed to the terms
// and do some check that they have access to this domain // and do some check that they have access to this domain
@ -64,11 +76,12 @@ module.exports.init = function(sharedOpts) {
res.end("Hello, World!"); res.end("Hello, World!");
} }
// worker.handleAcmeOrRedirectToHttps() // worker.handleAcmeOrRedirectToHttps()
// worker.handleAcmeOrUse(app) // worker.handleAcmeOrUse(app)
var redirectHttps = require("redirect-https")(); var redirectHttps = require('redirect-https')();
var plainServer = require("http").createServer(worker.middleware(redirectHttps)); var plainServer = require('http').createServer(worker.middleware(redirectHttps));
var server = require("https").createServer(worker.httpsOptions, worker.middleware(app)); var server = require('https').createServer(worker.httpsOptions, worker.middleware(app));
plainServer.listen(80); plainServer.listen(80);
server.listen(443); server.listen(443);
}; };

View File

@ -1,3 +1,12 @@
"use strict"; 'use strict';
module.exports = require("@root/greenlock-express"); console.error("");
console.error("One does not simply require('greenlock-cluster');");
console.error("");
console.error("Usage:");
console.error("\trequire('greenlock-cluster/master').create({ ... });");
console.error("\trequire('greenlock-cluster/worker').create({ ... });");
console.error("");
console.error("");
process.exit(1);

View File

@ -1,15 +1,13 @@
"use strict"; 'use strict';
// opts.addWorker(worker) // opts.addWorker(worker)
// opts.approveDomains(options, certs, cb) // opts.approveDomains(options, certs, cb)
module.exports.create = function (opts) { module.exports.create = function (opts) {
opts = opts || { }; opts = opts || { };
opts._workers = []; opts._workers = [];
opts.webrootPath = opts.webrootPath || require("os").tmpdir() + require("path").sep + "acme-challenge"; opts.webrootPath = opts.webrootPath || require('os').tmpdir() + require('path').sep + 'acme-challenge';
if (!opts.greenlock) { if (!opts.greenlock) { opts.greenlock = require('greenlock').create(opts); }
opts.greenlock = require("greenlock").create(opts); if ('function' !== typeof opts.approveDomains) {
}
if ("function" !== typeof opts.approveDomains) {
throw new Error("You must provide opts.approveDomains(domain, certs, callback) to approve certificates"); throw new Error("You must provide opts.approveDomains(domain, certs, callback) to approve certificates");
} }
@ -27,26 +25,26 @@ module.exports.create = function(opts) {
opts.addWorker = function (worker) { opts.addWorker = function (worker) {
opts._workers.push(worker); opts._workers.push(worker);
worker.on("online", function() { worker.on('online', function () {
log(opts.debug, "worker is up"); log(opts.debug, 'worker is up');
}); });
worker.on("message", function(msg) { worker.on('message', function (msg) {
log(opts.debug, "Message from worker " + worker.id); log(opts.debug, 'Message from worker ' + worker.id);
if ("LE_REQUEST" !== (msg && msg.type)) { if ('LE_REQUEST' !== (msg && msg.type)) {
log(opts.debug, "Ignoring irrelevant message"); log(opts.debug, 'Ignoring irrelevant message');
log(opts.debug, msg); log(opts.debug, msg);
return; return;
} }
log(opts.debug, "about to approveDomains"); log(opts.debug, 'about to approveDomains');
opts.approveDomains(msg.options, msg.certs, function (err, results) { opts.approveDomains(msg.options, msg.certs, function (err, results) {
if (err) { if (err) {
log(opts.debug, "Approval got ERROR", err.stack || err); log(opts.debug, 'Approval got ERROR', err.stack || err);
worker.send({ worker.send({
type: "LE_RESPONSE", type: 'LE_RESPONSE'
domain: msg.domain, , domain: msg.domain
error: { message: err.message, code: err.code, stack: err.stack } , error: { message: err.message, code: err.code, stack: err.stack }
}); });
return; return;
} }
@ -55,7 +53,7 @@ module.exports.create = function(opts) {
// //
/* /*
var certs = require('localhost.example.com-certificates').merge({ var certs = require('localhost.daplie.com-certificates').merge({
subject: msg.domain subject: msg.domain
, altnames: [ msg.domain ] , altnames: [ msg.domain ]
, issuedAt: Date.now() , issuedAt: Date.now()
@ -70,23 +68,21 @@ module.exports.create = function(opts) {
if (results.certs) { if (results.certs) {
promise = opts.greenlock.renew(results.options, results.certs); promise = opts.greenlock.renew(results.options, results.certs);
} else { }
else {
promise = opts.greenlock.register(results.options); promise = opts.greenlock.register(results.options);
} }
promise.then( promise.then(function (certs) {
function(certs) { log(opts.debug, 'Approval got certs', certs);
log(opts.debug, "Approval got certs", certs);
// certs = { subject, domains, issuedAt, expiresAt, privkey, cert, chain }; // certs = { subject, domains, issuedAt, expiresAt, privkey, cert, chain };
opts._workers.forEach(function (w) { opts._workers.forEach(function (w) {
w.send({ type: "LE_RESPONSE", domain: msg.domain, certs: certs }); w.send({ type: 'LE_RESPONSE', domain: msg.domain, certs: certs });
});
}, function (err) {
log(opts.debug, 'Approval got ERROR', err.stack || err);
worker.send({ type: 'LE_RESPONSE', domain: msg.domain, error: err });
}); });
},
function(err) {
log(opts.debug, "Approval got ERROR", err.stack || err);
worker.send({ type: "LE_RESPONSE", domain: msg.domain, error: err });
}
);
}); });
}); });
}; };

149
package-lock.json generated
View File

@ -1,149 +0,0 @@
{
"name": "greenlock-cluster",
"version": "3.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@root/acme": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@root/acme/-/acme-3.0.8.tgz",
"integrity": "sha512-VmBvLvWdCDkolkanI9Dzm1ouSWPaAa2eCCwcDZcVQbWoNiUIOqbbd57fcMA/gZxLyuJPStD2WXFuEuSMPDxcww==",
"requires": {
"@root/encoding": "^1.0.1",
"@root/keypairs": "^0.9.0",
"@root/pem": "^1.0.4",
"@root/request": "^1.3.11",
"@root/x509": "^0.7.2"
}
},
"@root/asn1": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@root/asn1/-/asn1-1.0.0.tgz",
"integrity": "sha512-0lfZNuOULKJDJmdIkP8V9RnbV3XaK6PAHD3swnFy4tZwtlMDzLKoM/dfNad7ut8Hu3r91wy9uK0WA/9zym5mig==",
"requires": {
"@root/encoding": "^1.0.1"
}
},
"@root/csr": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@root/csr/-/csr-0.8.1.tgz",
"integrity": "sha512-hKl0VuE549TK6SnS2Yn9nRvKbFZXn/oAg+dZJU/tlKl/f/0yRXeuUzf8akg3JjtJq+9E592zDqeXZ7yyrg8fSQ==",
"requires": {
"@root/asn1": "^1.0.0",
"@root/pem": "^1.0.4",
"@root/x509": "^0.7.2"
}
},
"@root/encoding": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@root/encoding/-/encoding-1.0.1.tgz",
"integrity": "sha512-OaEub02ufoU038gy6bsNHQOjIn8nUjGiLcaRmJ40IUykneJkIW5fxDqKxQx48cszuNflYldsJLPPXCrGfHs8yQ=="
},
"@root/greenlock": {
"version": "3.0.25",
"resolved": "https://registry.npmjs.org/@root/greenlock/-/greenlock-3.0.25.tgz",
"integrity": "sha512-VC8H9MTkbqxlB2LGntmcq5cstkE0TdZLvxm25SO5i7c6abJBVMQafhTD415OXwoGimnmWTn6SZ93Fj73d9QX/w==",
"requires": {
"@root/acme": "^3.0.8",
"@root/csr": "^0.8.1",
"@root/keypairs": "^0.9.0",
"@root/mkdirp": "^1.0.0",
"@root/request": "^1.3.10",
"acme-http-01-standalone": "^3.0.5",
"cert-info": "^1.5.1",
"greenlock-manager-fs": "^3.0.1",
"greenlock-store-fs": "^3.2.0",
"safe-replace": "^1.1.0"
}
},
"@root/greenlock-express": {
"version": "3.0.13",
"resolved": "https://registry.npmjs.org/@root/greenlock-express/-/greenlock-express-3.0.13.tgz",
"integrity": "sha512-SgFsP4rBDPRBp52yqb8kONw7ZCkgyYrBFJLg4xhfIMbsMct4dfqB+N5eJbeF/exJh4+BHM7tppvf31Xuz6EO2Q==",
"requires": {
"@root/greenlock": "^3.0.25",
"redirect-https": "^1.1.5"
}
},
"@root/keypairs": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/@root/keypairs/-/keypairs-0.9.0.tgz",
"integrity": "sha512-NXE2L9Gv7r3iC4kB/gTPZE1vO9Ox/p14zDzAJ5cGpTpytbWOlWF7QoHSJbtVX4H7mRG/Hp7HR3jWdWdb2xaaXg==",
"requires": {
"@root/encoding": "^1.0.1",
"@root/pem": "^1.0.4",
"@root/x509": "^0.7.2"
}
},
"@root/mkdirp": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@root/mkdirp/-/mkdirp-1.0.0.tgz",
"integrity": "sha512-hxGAYUx5029VggfG+U9naAhQkoMSXtOeXtbql97m3Hi6/sQSRL/4khKZPyOF6w11glyCOU38WCNLu9nUcSjOfA=="
},
"@root/pem": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@root/pem/-/pem-1.0.4.tgz",
"integrity": "sha512-rEUDiUsHtild8GfIjFE9wXtcVxeS+ehCJQBwbQQ3IVfORKHK93CFnRtkr69R75lZFjcmKYVc+AXDB+AeRFOULA=="
},
"@root/request": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@root/request/-/request-1.4.2.tgz",
"integrity": "sha512-J8FM4+SJuc7WRC+Jz17m+VT2lgI7HtatHhxN1F2ck5aIKUAxJEaR4u/gLBsgT60mVHevKCjKN0O8115UtJjwLw=="
},
"@root/x509": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/@root/x509/-/x509-0.7.2.tgz",
"integrity": "sha512-ENq3LGYORK5NiMFHEVeNMt+fTXaC7DTS6sQXoqV+dFdfT0vmiL5cDLjaXQhaklJQq0NiwicZegzJRl1ZOTp3WQ==",
"requires": {
"@root/asn1": "^1.0.0",
"@root/encoding": "^1.0.1"
}
},
"acme-http-01-standalone": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/acme-http-01-standalone/-/acme-http-01-standalone-3.0.5.tgz",
"integrity": "sha512-W4GfK+39GZ+u0mvxRVUcVFCG6gposfzEnSBF20T/NUwWAKG59wQT1dUbS1NixRIAsRuhpGc4Jx659cErFQH0Pg=="
},
"cert-info": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/cert-info/-/cert-info-1.5.1.tgz",
"integrity": "sha512-eoQC/yAgW3gKTKxjzyClvi+UzuY97YCjcl+lSqbsGIy7HeGaWxCPOQFivhUYm27hgsBMhsJJFya3kGvK6PMIcQ=="
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"greenlock-manager-fs": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/greenlock-manager-fs/-/greenlock-manager-fs-3.0.1.tgz",
"integrity": "sha512-vZfGFq1TTKxaAqdGDUwNservrNzXx0xCwT/ovG/N378GrhS+U5S8B8LUlNtQU7Fdw6RToMiBcm22OOxSrvZ2zw==",
"requires": {
"@root/mkdirp": "^1.0.0",
"safe-replace": "^1.1.0"
}
},
"greenlock-store-fs": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/greenlock-store-fs/-/greenlock-store-fs-3.2.0.tgz",
"integrity": "sha512-zqcPnF+173oYq5qU7FoGtuqeG8dmmvAiSnz98kEHAHyvgRF9pE1T0MM0AuqDdj45I3kXlCj2gZBwutnRi37J3g==",
"requires": {
"@root/mkdirp": "^1.0.0",
"safe-replace": "^1.1.0"
}
},
"redirect-https": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/redirect-https/-/redirect-https-1.3.0.tgz",
"integrity": "sha512-9GzwI/+Cqw3jlSg0CW6TgBQbhiVhkHSDvW8wjgRQ9IK34wtxS71YJiQeazSCSEqbvowHCJuQZgmQFl1xUHKEgg==",
"requires": {
"escape-html": "^1.0.3"
}
},
"safe-replace": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/safe-replace/-/safe-replace-1.1.0.tgz",
"integrity": "sha512-9/V2E0CDsKs9DWOOwJH7jYpSl9S3N05uyevNjvsnDauBqRowBPOyot1fIvV5N2IuZAbYyvrTXrYFVG0RZInfFw=="
}
}
}

View File

@ -1,19 +1,24 @@
{ {
"name": "greenlock-cluster", "name": "greenlock-cluster",
"version": "3.0.0", "version": "2.0.1",
"description": "Use automatic letsencrypt (free ssl certs) on multiple cores or even multiple machines", "description": "Use automatic letsencrypt (free ssl certs) on multiple cores or even multiple machines",
"main": "index.js", "main": "index.js",
"directories": { "directories": {
"example": "examples" "example": "examples"
}, },
"dependencies": { "dependencies": {
"@root/greenlock-express": "^3.0.13" "le-sni-auto": "^2.0.1",
"greenlock": "^2.0.4",
"localhost.daplie.com-certificates": "^1.2.3",
"redirect-https": "^1.1.0"
}, },
"devDependencies": {}, "devDependencies": {},
"scripts": {}, "scripts": {
"test": "node examples/serve.js"
},
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://git.coolaj86.com/coolaj86/greenlock-cluster.js.git" "url": "git+https://git.daplie.com/Daplie/greenlock-cluster.git"
}, },
"keywords": [ "keywords": [
"cluster", "cluster",
@ -32,9 +37,9 @@
"node.js" "node.js"
], ],
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)", "author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
"license": "MPL-2.0", "license": "(MIT OR Apache-2.0)",
"bugs": { "bugs": {
"url": "https://git.coolaj86.com/coolaj86/greenlock-cluster.js/issues" "url": "https://git.daplie.com/Daplie/greenlock-cluster/issues"
}, },
"homepage": "https://git.coolaj86.com/coolaj86/greenlock-cluster.js" "homepage": "https://git.daplie.com/Daplie/greenlock-cluster#readme"
} }

View File

@ -1,4 +1,4 @@
"use strict"; 'use strict';
function log(debug) { function log(debug) {
if (!debug) { if (!debug) {
@ -11,20 +11,23 @@ function log(debug) {
console.log.apply(console, args); console.log.apply(console, args);
} }
module.exports.create = function (opts) { module.exports.create = function (opts) {
// if another worker updates the certs, // if another worker updates the certs,
// receive a copy from master here as well // receive a copy from master here as well
// and update the sni cache manually // and update the sni cache manually
process.on("message", function(msg) { process.on('message', function (msg) {
if ("LE_RESPONSE" === msg.type && msg.certs) { if ('LE_RESPONSE' === msg.type && msg.certs) {
opts.sni.cacheCerts(msg.certs); opts.sni.cacheCerts(msg.certs);
} }
}); });
opts.sni = require("le-sni-auto").create({ opts.sni = require('le-sni-auto').create({
renewWithin: opts.renewWithin || 10 * 24 * 60 * 60 * 1000, renewWithin: opts.renewWithin || (10 * 24 * 60 * 60 * 1000)
renewBy: opts.renewBy || 5 * 24 * 60 * 60 * 1000, , renewBy: opts.renewBy || (5 * 24 * 60 * 60 * 1000)
getCertificates: function(domain, certs, cb) { , getCertificates: function (domain, certs, cb) {
var workerOptions = { domains: [ domain ] }; var workerOptions = { domains: [ domain ] };
opts.approveDomains(workerOptions, certs, function (_err, results) { opts.approveDomains(workerOptions, certs, function (_err, results) {
if (_err) { if (_err) {
@ -32,12 +35,11 @@ module.exports.create = function(opts) {
return; return;
} }
process.send({ type: "LE_REQUEST", domain: domain, options: results.options, certs: results.certs });
process.on("message", function(msg) {
var err = new Error("___MESSAGE___"); var err = new Error("___MESSAGE___");
process.send({ type: 'LE_REQUEST', domain: domain, options: results.options, certs: results.certs });
log(opts.debug, "Message from master"); process.on('message', function (msg) {
log(opts.debug, 'Message from master');
log(opts.debug, msg); log(opts.debug, msg);
if (msg.domain !== domain) { if (msg.domain !== domain) {
@ -48,9 +50,9 @@ module.exports.create = function(opts) {
err.message = msg.error.message || "unknown error sent from cluster master to worker"; err.message = msg.error.message || "unknown error sent from cluster master to worker";
err.stack.replace("___MESSAGE___", err.message); err.stack.replace("___MESSAGE___", err.message);
err = { err = {
message: err.message, message: err.message
stack: err.stack, , stack: err.stack
data: { options: workerOptions, certs: certs } , data: { options: workerOptions, certs: certs }
}; };
} else { } else {
err = null; err = null;
@ -62,17 +64,24 @@ module.exports.create = function(opts) {
} }
}); });
opts.httpsOptions = { SNICallback: opts.sni.sniCallback };
opts.httpsOptions = require('localhost.daplie.com-certificates').merge({ SNICallback: opts.sni.sniCallback });
opts.challenge = { opts.challenge = {
get: get: opts.getChallenge
opts.getChallenge || || (opts.challenge && opts.challenge.get)
(opts.challenge && opts.challenge.get) || || require('le-challenge-fs').create({ webrootPath: opts.webrootPath }).get
require("le-challenge-fs").create({ webrootPath: opts.webrootPath }).get
}; };
// opts.challenge.get, opts.acmeChallengePrefix // opts.challenge.get, opts.acmeChallengePrefix
opts.middleware = require("greenlock/lib/middleware").create(opts); opts.middleware = require('greenlock/lib/middleware').create(opts);
return opts; return opts;
}; };