| 
									
										
										
										
											2017-01-14 20:44:04 +00:00
										 |  |  | greenlock-express (letsencrypt-express) | 
					
						
							|  |  |  | ================= | 
					
						
							| 
									
										
										
										
											2016-11-01 18:18:01 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-24 19:02:56 -07:00
										 |  |  | | [greenlock (lib)](https://git.coolaj86.com/coolaj86/greenlock.js) | 
					
						
							|  |  |  | | [greenlock-cli](https://git.coolaj86.com/coolaj86/greenlock-cli.js) | 
					
						
							| 
									
										
										
										
											2017-01-25 15:02:16 -07:00
										 |  |  | | **greenlock-express** | 
					
						
							| 
									
										
										
										
											2017-11-24 19:02:56 -07:00
										 |  |  | | [greenlock-cluster](https://git.coolaj86.com/coolaj86/greenlock-cluster.js) | 
					
						
							|  |  |  | | [greenlock-koa](https://git.coolaj86.com/coolaj86/greenlock-koa.js) | 
					
						
							|  |  |  | | [greenlock-hapi](https://git.coolaj86.com/coolaj86/greenlock-hapi.js) | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | | | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-19 21:27:57 -06:00
										 |  |  | | Sponsored by [ppl](https://ppl.family) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Now supports Let's Encrypt v2!!
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | Free SSL and managed or automatic HTTPS for node.js with Express, Koa, Connect, Hapi, and all other middleware systems. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | * Automatic Registration via SNI (`httpsOptions.SNICallback`) | 
					
						
							|  |  |  |   * **registrations** require an **approval callback** in *production* | 
					
						
							|  |  |  | * Automatic Renewal (around 80 days) | 
					
						
							|  |  |  |   * **renewals** are *fully automatic* and happen in the *background*, with **no downtime** | 
					
						
							|  |  |  | * Automatic vhost / virtual hosting | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-27 08:48:07 -05:00
										 |  |  | All you have to do is start the webserver and then visit it at its domain name. | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | Install | 
					
						
							|  |  |  | ======= | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```bash | 
					
						
							| 
									
										
										
										
											2017-01-25 15:02:16 -07:00
										 |  |  | npm install --save greenlock-express@2.x | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-30 08:54:48 -06:00
										 |  |  | **Important**: Use node v4.5+ or v6.x, node <= v4.4 has a [known bug](https://github.com/nodejs/node/issues/8053) in the `Buffer` implementation. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | QuickStart | 
					
						
							| 
									
										
										
										
											2016-08-15 19:14:57 -04:00
										 |  |  | ========== | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-16 13:05:41 -04:00
										 |  |  | Here's a completely working example that will get you started: | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | `app.js`: | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | ```javascript | 
					
						
							|  |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-25 15:02:16 -07:00
										 |  |  | require('greenlock-express').create({ | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  |   server: 'staging' | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | , email: 'john.doe@example.com' | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | , agreeTos: true | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 09:25:07 -06:00
										 |  |  | , approveDomains: [ 'example.com' ] | 
					
						
							| 
									
										
										
										
											2016-08-15 21:15:16 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | , app: require('express')().use('/', function (req, res) { | 
					
						
							|  |  |  |     res.end('Hello, World!'); | 
					
						
							|  |  |  |   }) | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | }).listen(80, 443); | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | Certificates will be stored in `~/letsencrypt`. | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | **Important**: | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | You must set `server` to `https://acme-v01.api.letsencrypt.org/directory` **after** | 
					
						
							|  |  |  | you have tested that your setup works. | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | Why You Must Use 'staging' First | 
					
						
							|  |  |  | -------------------------------- | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | There are a number of common problems related to system configuration - | 
					
						
							|  |  |  | firewalls, ports, permissions, etc - that you are likely to run up against | 
					
						
							| 
									
										
										
										
											2017-01-25 15:02:16 -07:00
										 |  |  | when using greenlock for your first time. | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | In order to avoid being blocked by hitting rate limits with bad requests, | 
					
						
							|  |  |  | you should always test against the `'staging'` server | 
					
						
							|  |  |  | (`https://acme-staging.api.letsencrypt.org/directory`) first. | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 09:25:07 -06:00
										 |  |  | Migrating from v1.x | 
					
						
							|  |  |  | =================== | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Whereas v1.x had a few hundred lines of code, v2.x is a single small file of about 50 lines. | 
					
						
							| 
									
										
										
										
											2016-08-18 13:07:55 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | A few important things to note: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 16:29:31 -06:00
										 |  |  | * Delete your v1.x `~/letsencrypt` directory, otherwise you get this: | 
					
						
							|  |  |  |   * `{ type: 'urn:acme:error:malformed', detail: 'Parse error reading JWS', status: 400 }` | 
					
						
							| 
									
										
										
										
											2016-08-18 13:07:55 -06:00
										 |  |  | * `approveRegistration` has been replaced by `approveDomains` | 
					
						
							|  |  |  | * All of the behavior has moved to the various plugins, which each have their own options | 
					
						
							|  |  |  | * Use https and http directly, don't rely on the silly `.listen()` helper. It's just there for looks. | 
					
						
							| 
									
										
										
										
											2016-10-14 09:43:50 -06:00
										 |  |  | * `lex.createAcmeResponder()` is now `lex.middleware(require('redirect-https')())` or `lex.middleware(app)` | 
					
						
							| 
									
										
										
										
											2016-08-17 09:25:07 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:14:57 -04:00
										 |  |  | Usage | 
					
						
							|  |  |  | ===== | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | The oversimplified example was the bait | 
					
						
							|  |  |  | (because everyone seems to want an example that fits in 3 lines, even if it's terribly bad practices), | 
					
						
							|  |  |  | now here's the switch: | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | `serve.js`: | 
					
						
							|  |  |  | ```javascript | 
					
						
							|  |  |  | 'use strict'; | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-25 15:02:16 -07:00
										 |  |  | // returns an instance of node-greenlock with additional helper methods | 
					
						
							|  |  |  | var lex = require('greenlock-express').create({ | 
					
						
							| 
									
										
										
										
											2016-08-15 19:15:57 -04:00
										 |  |  |   // set to https://acme-v01.api.letsencrypt.org/directory in production | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  |   server: 'staging' | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | // If you wish to replace the default plugins, you may do so here | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2016-08-19 13:31:37 -06:00
										 |  |  | , challenges: { 'http-01': require('le-challenge-fs').create({ webrootPath: '/tmp/acme-challenges' }) } | 
					
						
							| 
									
										
										
										
											2016-08-18 00:27:47 -06:00
										 |  |  | , store: require('le-store-certbot').create({ webrootPath: '/tmp/acme-challenges' }) | 
					
						
							| 
									
										
										
										
											2016-08-18 12:58:40 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | // You probably wouldn't need to replace the default sni handler | 
					
						
							| 
									
										
										
										
											2017-11-24 19:02:56 -07:00
										 |  |  | // See https://git.coolaj86.com/coolaj86/le-sni-auto if you think you do | 
					
						
							| 
									
										
										
										
											2016-08-18 12:58:40 -06:00
										 |  |  | //, sni: require('le-sni-auto').create({}) | 
					
						
							| 
									
										
										
										
											2016-08-12 03:48:21 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 09:11:10 -06:00
										 |  |  | , approveDomains: approveDomains | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | }); | 
					
						
							| 
									
										
										
										
											2016-08-17 09:09:43 -06:00
										 |  |  | ``` | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 09:11:10 -06:00
										 |  |  | ```javascript | 
					
						
							|  |  |  | function approveDomains(opts, certs, cb) { | 
					
						
							|  |  |  |   // This is where you check your database and associated | 
					
						
							|  |  |  |   // email addresses with domains and agreements and such | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // The domains being approved for the first time are listed in opts.domains | 
					
						
							|  |  |  |   // Certs being renewed are listed in certs.altnames | 
					
						
							|  |  |  |   if (certs) { | 
					
						
							|  |  |  |     opts.domains = certs.altnames; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     opts.email = 'john.doe@example.com'; | 
					
						
							|  |  |  |     opts.agreeTos = true; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-14 13:37:53 -06:00
										 |  |  |   // NOTE: you can also change other options such as `challengeType` and `challenge` | 
					
						
							|  |  |  |   // opts.challengeType = 'http-01'; | 
					
						
							|  |  |  |   // opts.challenge = require('le-challenge-fs').create({}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 09:11:10 -06:00
										 |  |  |   cb(null, { options: opts, certs: certs }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 09:09:43 -06:00
										 |  |  | ```javascript | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | // handles acme-challenge and redirects to https | 
					
						
							| 
									
										
										
										
											2016-08-19 16:22:57 -06:00
										 |  |  | require('http').createServer(lex.middleware(require('redirect-https')())).listen(80, function () { | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  |   console.log("Listening for ACME http-01 challenges on", this.address()); | 
					
						
							|  |  |  | }); | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | var app = require('express')(); | 
					
						
							|  |  |  | app.use('/', function (req, res) { | 
					
						
							|  |  |  |   res.end('Hello, World!'); | 
					
						
							|  |  |  | }); | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | // handles your app | 
					
						
							| 
									
										
										
										
											2016-08-17 09:08:47 -06:00
										 |  |  | require('https').createServer(lex.httpsOptions, lex.middleware(app)).listen(443, function () { | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  |   console.log("Listening for ACME tls-sni-01 challenges and serve app on", this.address()); | 
					
						
							|  |  |  | }); | 
					
						
							| 
									
										
										
										
											2016-08-12 03:02:33 -04:00
										 |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 09:25:07 -06:00
										 |  |  | **Security Warning**: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | If you don't do proper checks in `approveDomains(opts, certs, cb)` | 
					
						
							|  |  |  | an attacker will spoof SNI packets with bad hostnames and that will | 
					
						
							|  |  |  | cause you to be rate-limited and or blocked from the ACME server. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 19:14:57 -04:00
										 |  |  | API | 
					
						
							|  |  |  | === | 
					
						
							| 
									
										
										
										
											2016-08-12 03:56:19 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 21:15:16 -04:00
										 |  |  | This module is an elaborate ruse (to provide an oversimplified example and to nab some SEO). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-24 19:02:56 -07:00
										 |  |  | The API is actually located at [node-greenlock options](https://git.coolaj86.com/coolaj86/greenlock.js) | 
					
						
							| 
									
										
										
										
											2017-01-25 15:02:16 -07:00
										 |  |  | (because all options are simply passed through to `node-greenlock` proper without modification). | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-25 15:02:16 -07:00
										 |  |  | The only "API" consists of two options, the rest is just a wrapper around `node-greenlock` to take LOC from 15 to 5: | 
					
						
							| 
									
										
										
										
											2016-08-15 19:12:39 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 21:15:16 -04:00
										 |  |  | * `opts.app` An express app in the format `function (req, res) { ... }` (no `next`). | 
					
						
							|  |  |  | * `lex.listen(plainPort, tlsPort)` Accepts port numbers (or arrays of port numbers) to listen on. | 
					
						
							| 
									
										
										
										
											2016-08-12 03:56:19 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-25 15:02:16 -07:00
										 |  |  | Brief overview of some simple options for `node-greenlock`: | 
					
						
							| 
									
										
										
										
											2016-08-12 03:56:19 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-15 21:15:16 -04:00
										 |  |  | * `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. | 
					
						
							| 
									
										
										
										
											2016-08-17 09:25:07 -06:00
										 |  |  | * `opts.approveDomains` can be either of: | 
					
						
							|  |  |  |   * An explicit array of allowed domains such as `[ 'example.com', 'www.example.com' ]` | 
					
						
							|  |  |  |   * A callback `function (opts, certs, cb) { cb(null, { options: opts, certs: certs }); }` for setting `email`, `agreeTos`, `domains`, etc (as shown in usage example above) | 
					
						
							| 
									
										
										
										
											2016-08-15 21:15:16 -04:00
										 |  |  | * `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. |