lex v2.x
This commit is contained in:
parent
fe039dc137
commit
26eb38fb25
36
README.md
36
README.md
|
@ -59,6 +59,8 @@ require('letsencrypt-express').create({
|
||||||
|
|
||||||
, agreeTos: true
|
, agreeTos: true
|
||||||
|
|
||||||
|
, approvedDomains: [ 'example.com' ]
|
||||||
|
|
||||||
, app: require('express')().use('/', function (req, res) {
|
, app: require('express')().use('/', function (req, res) {
|
||||||
res.end('Hello, World!');
|
res.end('Hello, World!');
|
||||||
})
|
})
|
||||||
|
@ -134,7 +136,7 @@ var lex = require('letsencrypt-express').create({
|
||||||
|
|
||||||
|
|
||||||
// handles acme-challenge and redirects to https
|
// handles acme-challenge and redirects to https
|
||||||
require('http').createServer(lex.middleware()).listen(80, function () {
|
require('http').createServer(le.middleware()).listen(80, function () {
|
||||||
console.log("Listening for ACME http-01 challenges on", this.address());
|
console.log("Listening for ACME http-01 challenges on", this.address());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -146,7 +148,7 @@ app.use('/', function (req, res) {
|
||||||
});
|
});
|
||||||
|
|
||||||
// handles your app
|
// handles your app
|
||||||
require('https').createServer(lex.httpsOptions, lex.middleware(app)).listen(443, function () {
|
require('https').createServer(le.httpsOptions, le.middleware(app)).listen(443, function () {
|
||||||
console.log("Listening for ACME tls-sni-01 challenges and serve app on", this.address());
|
console.log("Listening for ACME tls-sni-01 challenges and serve app on", this.address());
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
@ -154,22 +156,22 @@ require('https').createServer(lex.httpsOptions, lex.middleware(app)).listen(443,
|
||||||
API
|
API
|
||||||
===
|
===
|
||||||
|
|
||||||
All options are passed directly to `node-letsencrypt`,
|
This module is an elaborate ruse (to provide an oversimplified example and to nab some SEO).
|
||||||
so `lex` is an instance of `letsencrypt`, but has a few
|
|
||||||
extra helper methods and options.
|
|
||||||
|
|
||||||
See [node-letsencrypt options](https://github.com/Daplie/node-letsencrypt)
|
The API is actually located at [node-letsencrypt options](https://github.com/Daplie/node-letsencrypt)
|
||||||
|
(because all options are simply passed through to `node-letsencrypt` proper without modification).
|
||||||
|
|
||||||
* `lexOptions.approveDomains(options, certs, cb)` is special for `letsencrypt-express`, but will probably be included in `node-letsencrypt` in the future (no API change).
|
The only "API" consists of two options, the rest is just a wrapper around `node-letsencrypt` to take LOC from 15 to 5:
|
||||||
|
|
||||||
* `lexOptions.app` is just an elaborate ruse used for the Quickstart. It's sole purpose is to trim out 5 lines of code for setting http and https servers so that whiners won't whine. Real programmers don't use this.
|
* `opts.app` An express app in the format `function (req, res) { ... }` (no `next`).
|
||||||
* `leOptions.server` set to https://acme-v01.api.letsencrypt.org/directory in production
|
* `lex.listen(plainPort, tlsPort)` Accepts port numbers (or arrays of port numbers) to listen on.
|
||||||
* `leOptions.email` useful for simple sites where there is only one owner. Leave this `null` and use `approveDomains` otherwise.
|
|
||||||
* `leOptions.agreeTos` useful for simple sites where there is only one owner. Leave this `null` and use `approveDomains` otherwise.
|
|
||||||
* `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)
|
|
||||||
* `lex.middleware(nextApp)` uses `letsencrypt/middleware` for GET-ing `http-01`, hence `sharedOptions.webrootPath`
|
|
||||||
* `lex.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
|
Brief overview of some simple options for `node-letsencrypt`:
|
||||||
that isn't shown here, look at the code (it's not that much) or open an issue.
|
|
||||||
|
* `opts.server` set to https://acme-v01.api.letsencrypt.org/directory in production
|
||||||
|
* `opts.email` The default email to use to accept agreements.
|
||||||
|
* `opts.agreeTos` When set to `true`, this always accepts the LetsEncrypt TOS. When a string it checks the agreement url first.
|
||||||
|
* `opts.approvedDomains` An explicit array of The allowed domains (can be used instead of `approveDomains`).
|
||||||
|
* `opts.approveDomains` A callback for checking your database before allowing a domain `function (opts, certs, cb) { }`
|
||||||
|
* `opts.renewWithin` is the **maximum** number of days (in ms) before expiration to renew a certificate.
|
||||||
|
* `opts.renewBy` is the **minimum** number of days (in ms) before expiration to renew a certificate.
|
||||||
|
|
12
index.js
12
index.js
|
@ -1,12 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
console.error("");
|
|
||||||
console.error("One does not simply require('letsencrypt-cluster');");
|
|
||||||
console.error("");
|
|
||||||
console.error("Usage:");
|
|
||||||
console.error("\trequire('letsencrypt-cluster/master').create({ ... });");
|
|
||||||
console.error("\trequire('letsencrypt-cluster/worker').create({ ... });");
|
|
||||||
console.error("");
|
|
||||||
console.error("");
|
|
||||||
|
|
||||||
process.exit(1);
|
|
118
master.js
118
master.js
|
@ -1,91 +1,55 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// 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 || { };
|
// accept all defaults for le.challenges, le.store, le.middleware
|
||||||
opts._workers = [];
|
var le = require('letsencrypt').create(opts);
|
||||||
opts.webrootPath = opts.webrootPath || require('os').tmpdir() + require('path').sep + 'acme-challenge';
|
|
||||||
if (!opts.letsencrypt) { opts.letsencrypt = require('letsencrypt').create(opts); }
|
|
||||||
if ('function' !== typeof opts.approveDomains) {
|
|
||||||
throw new Error("You must provide opts.approveDomains(domain, certs, callback) to approve certificates");
|
|
||||||
}
|
|
||||||
|
|
||||||
function log(debug) {
|
opts.app = opts.app || require('express')().use('/', function (req, res) {
|
||||||
if (!debug) {
|
res.end("Hello, World!\nWith Love,\nLet's Encrypt Express");
|
||||||
return;
|
});
|
||||||
|
|
||||||
|
opts.listen = function (plainPort, port) {
|
||||||
|
var PromiseA = require('bluebird');
|
||||||
|
var promises = [];
|
||||||
|
var plainPorts = plainPort;
|
||||||
|
var ports = port;
|
||||||
|
var servers = [];
|
||||||
|
|
||||||
|
if (!plainPorts || !ports) {
|
||||||
|
plainPorts = 80;
|
||||||
|
ports = 443;
|
||||||
}
|
}
|
||||||
|
|
||||||
var args = Array.prototype.slice.call(arguments);
|
if (!Array.isArray(plainPorts)) {
|
||||||
args.shift();
|
plainPorts = [ plainPorts ];
|
||||||
args.unshift("[le/lib/core.js]");
|
ports = [ ports ];
|
||||||
console.log.apply(console, args);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
opts.addWorker = function (worker) {
|
plainPorts.forEach(function (p) {
|
||||||
opts._workers.push(worker);
|
promises.push(new PromiseA(function (resolve, reject) {
|
||||||
|
require('http').createServer(le.middleware(require('https-redirect').create())).listen(p, function () {
|
||||||
worker.on('online', function () {
|
resolve();
|
||||||
log(opts.debug, 'worker is up');
|
}).on('error', reject);
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
worker.on('message', function (msg) {
|
ports.forEach(function (p) {
|
||||||
log(opts.debug, 'Message from worker ' + worker.id);
|
promises.push(new PromiseA(function (resolve, reject) {
|
||||||
if ('LE_REQUEST' !== (msg && msg.type)) {
|
var server = require('https').createServer(le.httpsOptions, le.middleware(le.app)).listen(p, function () {
|
||||||
log(opts.debug, 'Ignoring irrelevant message');
|
resolve();
|
||||||
log(opts.debug, msg);
|
}).on('error', reject);
|
||||||
return;
|
servers.push(server);
|
||||||
}
|
}));
|
||||||
|
|
||||||
log(opts.debug, 'about to approveDomains');
|
|
||||||
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'
|
|
||||||
, domain: msg.domain
|
|
||||||
, error: { message: err.message, code: err.code, stack: err.stack }
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var promise;
|
|
||||||
|
|
||||||
//
|
|
||||||
/*
|
|
||||||
var certs = require('localhost.daplie.com-certificates').merge({
|
|
||||||
subject: msg.domain
|
|
||||||
, altnames: [ msg.domain ]
|
|
||||||
, issuedAt: Date.now()
|
|
||||||
, expiresAt: Date.now() + (90 * 24 * 60 * 60 * 1000)
|
|
||||||
});
|
|
||||||
certs.privkey = certs.key.toString('ascii');
|
|
||||||
certs.cert = certs.cert.toString('ascii');
|
|
||||||
certs.chain = '';
|
|
||||||
worker.send({ type: 'LE_RESPONSE', domain: msg.domain, certs: certs });
|
|
||||||
return;
|
|
||||||
// */
|
|
||||||
|
|
||||||
if (results.certs) {
|
|
||||||
promise = opts.letsencrypt.renew(results.options, results.certs);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
promise = opts.letsencrypt.register(results.options);
|
|
||||||
}
|
|
||||||
|
|
||||||
promise.then(function (certs) {
|
|
||||||
log(opts.debug, 'Approval got certs', certs);
|
|
||||||
// certs = { subject, domains, issuedAt, expiresAt, privkey, cert, chain };
|
|
||||||
opts._workers.forEach(function (w) {
|
|
||||||
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 });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!Array.isArray(port)) {
|
||||||
|
servers = servers[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return servers;
|
||||||
};
|
};
|
||||||
|
|
||||||
return opts;
|
|
||||||
|
return le;
|
||||||
};
|
};
|
||||||
|
|
87
worker.js
87
worker.js
|
@ -1,87 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports.create = function (opts) {
|
|
||||||
|
|
||||||
// if another worker updates the certs,
|
|
||||||
// receive a copy from master here as well
|
|
||||||
// and update the sni cache manually
|
|
||||||
process.on('message', function (msg) {
|
|
||||||
if ('LE_RESPONSE' === msg.type && msg.certs) {
|
|
||||||
opts.sni.cacheCerts(msg.certs);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
opts.sni = require('le-sni-auto').create({
|
|
||||||
renewWithin: opts.renewWithin || (10 * 24 * 60 * 60 * 1000)
|
|
||||||
, renewBy: opts.renewBy || (5 * 24 * 60 * 60 * 1000)
|
|
||||||
, getCertificates: function (domain, certs, cb) {
|
|
||||||
var workerOptions = { domains: [ domain ] };
|
|
||||||
opts.approveDomains(workerOptions, certs, function (_err, results) {
|
|
||||||
if (_err) {
|
|
||||||
cb(_err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var err = new Error("___MESSAGE___");
|
|
||||||
process.send({ type: 'LE_REQUEST', domain: domain, options: results.options, certs: results.certs });
|
|
||||||
|
|
||||||
process.on('message', function (msg) {
|
|
||||||
log(opts.debug, 'Message from master');
|
|
||||||
log(opts.debug, msg);
|
|
||||||
|
|
||||||
if (msg.domain !== domain) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg.error) {
|
|
||||||
err.message = msg.error.message || "unknown error sent from cluster master to worker";
|
|
||||||
err.stack.replace("___MESSAGE___", err.message);
|
|
||||||
err = {
|
|
||||||
message: err.message
|
|
||||||
, stack: err.stack
|
|
||||||
, data: { options: workerOptions, certs: certs }
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
err = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
cb(err, msg.certs);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
opts.httpsOptions = require('localhost.daplie.com-certificates').merge({ SNICallback: opts.sni.sniCallback });
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
opts.challenge = {
|
|
||||||
get: opts.getChallenge
|
|
||||||
|| (opts.challenge && opts.challenge.get)
|
|
||||||
|| require('le-challenge-fs').create({ webrootPath: opts.webrootPath }).get
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// opts.challenge.get, opts.acmeChallengePrefix
|
|
||||||
opts.middleware = require('letsencrypt/lib/middleware').create(opts);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return opts;
|
|
||||||
};
|
|
Loading…
Reference in New Issue