From b4b3c0497a9f486ff5b1494b0ddb8f8292904996 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Sun, 13 Dec 2015 08:05:24 +0000 Subject: [PATCH] turn off debug mode, add more docs & examples --- README.md | 135 +++++++++++++++++++++++++++++++- backends-python.js | 10 +-- examples/commandline-minimal.js | 53 +++++++++++++ examples/commandline.js | 2 +- examples/config-minimal.js | 31 ++++++++ examples/express-minimal.js | 57 ++++++++++++++ examples/express.js | 2 +- 7 files changed, 279 insertions(+), 11 deletions(-) create mode 100644 examples/commandline-minimal.js create mode 100644 examples/config-minimal.js create mode 100644 examples/express-minimal.js diff --git a/README.md b/README.md index 39d0d46..407bb74 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,11 @@ Automatic [Let's Encrypt](https://lettsencrypt.org) HTTPS Certificates for node. * Automatic HTTPS with ExpressJS * Automatic live renewal (in-process) - * safe for use with node's cluster module - * configurable for automatic registration (in-process) + * On-the-fly HTTPS certificates for Dynamic DNS (in-process, no server restart) + * Works with node cluster out of the box * usable via commandline as well - * [90-day certificates](https://letsencrypt.org/2015/11/09/why-90-days.html) * Free SSL (HTTPS Certificates for TLS) + * [90-day certificates](https://letsencrypt.org/2015/11/09/why-90-days.html) Install ======= @@ -48,15 +48,142 @@ le.register({ **However**, due to the nature of what this library does, it has a few more "moving parts" than what makes sense to show in a minimal snippet. +### One Time Registration + * [commandline (standalone with "webroot")](https://github.com/Daplie/node-letsencrypt/blob/master/examples/commandline.js) +```javascript +'use strict'; + +var LE = require('letsencrypt'); +var config = require('./config-minimal'); + +// Note: you should make this special dir in your product and leave it empty +config.le.webrootPath = __dirname + '/../tests/acme-challenge'; +config.le.server = LE.stagingServer; + + +// +// Manual Registration +// +var le = LE.create(config.backend, config.le); +le.register({ + agreeTos: true +, domains: ['example.com'] // CHANGE TO YOUR DOMAIN +, email: 'user@email.com' // CHANGE TO YOUR EMAIL +}, function (err) { + if (err) { + console.error('[Error]: node-letsencrypt/examples/standalone'); + console.error(err.stack); + } else { + console.log('success'); + } + + plainServer.close(); + tlsServer.close(); +}); + + +// +// Express App +// +var app = require('express')(); +app.use('/', le.middleware()); + + +// +// HTTP & HTTPS servers +// (required for domain validation) +// +var plainServer = require('http').createServer(app).listen(config.plainPort, function () { + console.log('Listening http', this.address()); +}); + +var tlsServer = require('https').createServer({ + key: config.tlsKey +, cert: config.tlsCert +, SNICallback: le.sniCallback +}, app).listen(config.tlsPort, function () { + console.log('Listening http', this.address()); +}); +``` + ```bash # manual standalone registration via commandline # (runs against testing server on tls port 5001) node examples/commandline.js example.com,www.example.com user@example.net agree ``` -* [expressjs (fully automatic https)](https://github.com/Daplie/node-letsencrypt/blob/master/examples/express.js) +### Express + +Fully Automatic HTTPS with ExpressJS using Free SSL certificates from Let's Encrypt + +* [Minimal ExpressJS Example](https://github.com/Daplie/node-letsencrypt/blob/master/examples/express-minimal.js) + +```javascript +'use strict'; + +var LE = require('letsencrypt'); +var config = require('./config-minimal'); + +// Note: you should make this special dir in your product and leave it empty +config.le.webrootPath = __dirname + '/../tests/acme-challenge'; +config.le.server = LE.stagingServer; + +// +// Automatically Register / Renew Domains +// +var le = LE.create(config.backend, config.le, { + sniRegisterCallback: function (args, expiredCert, cb) { + // Security: check that this is actually a subdomain we allow + // (otherwise an attacker can cause you to rate limit against the LE server) + + var hostname = args.domains[0]; + if (!/\.example\.com$/.test(hostname)) { + console.error("bad domain '" + hostname + "', not a subdomain of example.com"); + cb(nul, null); + } + + // agree to the LE TOS for this domain + args.agreeTos = true; + args.email = 'user@example.com'; + + // use the cert even though it's expired + if (expiredCert) { + cb(null, expiredCert); + cb = function () { /*ignore*/ }; + } + + // register / renew the certificate in the background + le.register(args, function () {}); + } +}); + + +// +// Express App +// +var app = require('express')(); +app.use('/', le.middleware()); + + +// +// HTTP & HTTPS servers +// +require('http').createServer(app).listen(config.plainPort, function () { + console.log('Listening http', this.address()); +}); + +require('https').createServer({ + key: config.tlsKey +, cert: config.tlsCert +, SNICallback: le.sniCallback +}, app).listen(config.tlsPort, function () { + console.log('Listening http', this.address()); +}); +``` + +* [Full ExpressJS Example](https://github.com/Daplie/node-letsencrypt/blob/master/examples/express.js) ```bash # clear out the certificates diff --git a/backends-python.js b/backends-python.js index ccd2f6c..70498b0 100644 --- a/backends-python.js +++ b/backends-python.js @@ -3,13 +3,13 @@ var PromiseA = require('bluebird'); var fs = PromiseA.promisifyAll(require('fs')); -module.exports.create = function (leBinPath, defaults) { - defaults.webroot = true; // standalone should not be set to true +module.exports.create = function (leBinPath, defaults, opts) { + defaults.webroot = true; defaults.renewByDefault = true; defaults.text = true; var LEP = require('letsencrypt-python'); - var lep = PromiseA.promisifyAll(LEP.create(leBinPath, { debug: true })); + var lep = PromiseA.promisifyAll(LEP.create(leBinPath, opts)); var wrapped = { registerAsync: function (args) { return lep.registerAsync('certonly', args); @@ -28,7 +28,7 @@ module.exports.create = function (leBinPath, defaults) { return { key: arr[0] // privkey.pem , cert: arr[1] // fullchain.pem - // TODO parse centificate + // TODO parse centificate for lifetime / expiresAt , issuedAt: arr[2].mtime.valueOf() }; }, function () { @@ -38,4 +38,4 @@ module.exports.create = function (leBinPath, defaults) { }; return wrapped; -} +}; diff --git a/examples/commandline-minimal.js b/examples/commandline-minimal.js new file mode 100644 index 0000000..48dd923 --- /dev/null +++ b/examples/commandline-minimal.js @@ -0,0 +1,53 @@ +'use strict'; + +var LE = require('../'); +var config = require('./config-minimal'); + +// Note: you should make this special dir in your product and leave it empty +config.le.webrootPath = __dirname + '/../tests/acme-challenge'; +config.le.server = LE.stagingServer; + + +// +// Manual Registration +// +var le = LE.create(config.backend, config.le); +le.register({ + agreeTos: true +, domains: ['example.com'] // CHANGE TO YOUR DOMAIN +, email: 'user@example.com' // CHANGE TO YOUR EMAIL +}, function (err) { + if (err) { + console.error('[Error]: node-letsencrypt/examples/standalone'); + console.error(err.stack); + } else { + console.log('success'); + } + + plainServer.close(); + tlsServer.close(); +}); + + +// +// Express App +// +var app = require('express')(); +app.use('/', le.middleware()); + + +// +// HTTP & HTTPS servers +// (required for domain validation) +// +var plainServer = require('http').createServer(app).listen(config.plainPort, function () { + console.log('Listening http', this.address()); +}); + +var tlsServer = require('https').createServer({ + key: config.tlsKey +, cert: config.tlsCert +, SNICallback: le.sniCallback +}, app).listen(config.tlsPort, function () { + console.log('Listening http', this.address()); +}); diff --git a/examples/commandline.js b/examples/commandline.js index 6bf18f0..deb0c1a 100644 --- a/examples/commandline.js +++ b/examples/commandline.js @@ -29,7 +29,7 @@ var bkDefaults = { , text: true }; -var leBinPath = require('homedir')() + '/.local/share/letsencrypt/bin/letsencrypt'; +var leBinPath = require('os').homedir() + '/.local/share/letsencrypt/bin/letsencrypt'; var LEB = require('../backends-python'); var backend = LEB.create(leBinPath, bkDefaults, { debug: true }); diff --git a/examples/config-minimal.js b/examples/config-minimal.js new file mode 100644 index 0000000..5caa94b --- /dev/null +++ b/examples/config-minimal.js @@ -0,0 +1,31 @@ +'use strict'; + +var path = require('path'); + +var binpath = require('os').homedir() + '/.local/share/letsencrypt/bin/letsencrypt'; + +var config = { + + plainPort: 80 +, tlsPort: 5001 // 5001 for testing, normally 443 +, tlsKey: require('localhost.daplie.com-certificates').key +, tlsCert: require('localhost.daplie.com-certificates').cert + + +, le: { + webrootPath: path.join(__dirname, '..', 'tests', 'acme-challenge') + , fullchainTpl: '/live/:hostname/fullchain.pem' + , privkeyTpl: '/live/:hostname/privkey.pem' + , configDir: path.join(__dirname, '..', 'tests', 'letsencrypt.config') + + // these are specific to the python client and won't be needed with the purejs library + , logsDir: path.join(__dirname, '..', 'tests', 'letsencrypt.logs') + , workDir: path.join(__dirname, '..', 'tests', 'letsencrypt.work') + } + +}; + +//config.backend = require('letsencrypt/backends-python').create(binpath, config.le); +config.backend = require('../backends-python').create(binpath, config.le); + +module.exports = config; diff --git a/examples/express-minimal.js b/examples/express-minimal.js new file mode 100644 index 0000000..dde1717 --- /dev/null +++ b/examples/express-minimal.js @@ -0,0 +1,57 @@ +'use strict'; + +var LE = require('../'); +var config = require('./config-minimal'); + +// Note: you should make this special dir in your product and leave it empty +config.le.webrootPath = __dirname + '/../tests/acme-challenge'; +config.le.server = LE.stagingServer; + +var le = LE.create(config.backend, config.le, { + sniRegisterCallback: function (args, expiredCert, cb) { + // In theory you should never get an expired certificate because + // the certificates automatically renew in the background starting + // about a week before they expire. + // (the default behavior is to randomly stagger renewals) + // so in this case we'll just return the expired certificate + if (expiredCert) { return cb(null, expiredCert); } + + // If we get here that means this domain hasn't been registered yet + // Security Warning: you should either manually register domains + // and return null here or check that the sni header isn't being + // spoofed and this is actually a domain you own before registering + // + // cb(null, null); + + var hostname = args.domains[0]; + console.log("[TODO] check that '" + hostname + "' is one I expect"); + + args.agreeTos = true; + args.email = 'user@example.com'; + + le.register(args, cb); + } +}); + + +// +// Express App +// +var app = require('express')(); +app.use('/', le.middleware()); + + +// +// HTTP & HTTPS servers +// +require('http').createServer(app).listen(config.plainPort, function () { + console.log('Listening http', this.address()); +}); + +require('https').createServer({ + key: config.tlsKey +, cert: config.tlsCert +, SNICallback: le.sniCallback +}, app).listen(config.tlsPort, function () { + console.log('Listening http', this.address()); +}); diff --git a/examples/express.js b/examples/express.js index 30c52ad..bf79abb 100644 --- a/examples/express.js +++ b/examples/express.js @@ -28,7 +28,7 @@ var bkDefaults = { , server: LE.stagingServer }; -var leBinPath = require('homedir')() + '/.local/share/letsencrypt/bin/letsencrypt'; +var leBinPath = require('os').homedir() + '/.local/share/letsencrypt/bin/letsencrypt'; var LEB = require('../backends-python'); var backend = LEB.create(leBinPath, bkDefaults, { debug: true });