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); |  | ||||||
							
								
								
									
										110
									
								
								master.js
									
									
									
									
									
								
							
							
						
						
									
										110
									
								
								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; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     var args = Array.prototype.slice.call(arguments); |  | ||||||
|     args.shift(); |  | ||||||
|     args.unshift("[le/lib/core.js]"); |  | ||||||
|     console.log.apply(console, args); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   opts.addWorker = function (worker) { |  | ||||||
|     opts._workers.push(worker); |  | ||||||
| 
 |  | ||||||
|     worker.on('online', function () { |  | ||||||
|       log(opts.debug, 'worker is up'); |  | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|     worker.on('message', function (msg) { |   opts.listen = function (plainPort, port) { | ||||||
|       log(opts.debug, 'Message from worker ' + worker.id); |     var PromiseA = require('bluebird'); | ||||||
|       if ('LE_REQUEST' !== (msg && msg.type)) { |     var promises = []; | ||||||
|         log(opts.debug, 'Ignoring irrelevant message'); |     var plainPorts = plainPort; | ||||||
|         log(opts.debug, msg); |     var ports = port; | ||||||
|         return; |     var servers = []; | ||||||
|  | 
 | ||||||
|  |     if (!plainPorts || !ports) { | ||||||
|  |       plainPorts = 80; | ||||||
|  |       ports = 443; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|       log(opts.debug, 'about to approveDomains'); |     if (!Array.isArray(plainPorts)) { | ||||||
|       opts.approveDomains(msg.options, msg.certs, function (err, results) { |       plainPorts = [ plainPorts ]; | ||||||
|         if (err) { |       ports = [ ports ]; | ||||||
|           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; |     plainPorts.forEach(function (p) { | ||||||
| 
 |       promises.push(new PromiseA(function (resolve, reject) { | ||||||
|         //
 |         require('http').createServer(le.middleware(require('https-redirect').create())).listen(p, function () { | ||||||
|         /* |           resolve(); | ||||||
|         var certs = require('localhost.daplie.com-certificates').merge({ |         }).on('error', reject); | ||||||
|           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) { |     ports.forEach(function (p) { | ||||||
|           promise = opts.letsencrypt.renew(results.options, results.certs); |       promises.push(new PromiseA(function (resolve, reject) { | ||||||
|         } |         var server = require('https').createServer(le.httpsOptions, le.middleware(le.app)).listen(p, function () { | ||||||
|         else { |           resolve(); | ||||||
|           promise = opts.letsencrypt.register(results.options); |         }).on('error', reject); | ||||||
|  |         servers.push(server); | ||||||
|  |       })); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     if (!Array.isArray(port)) { | ||||||
|  |       servers = servers[0]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|         promise.then(function (certs) { |     return servers; | ||||||
|           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 }); |  | ||||||
|         }); |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user