2018-04-20 06:43:02 +00:00
| Sponsored by [ppl ](https://ppl.family )
2017-11-25 02:02:56 +00:00
| [greenlock (lib) ](https://git.coolaj86.com/coolaj86/greenlock.js )
2018-04-30 15:44:03 +00:00
| [gl-cli ](https://git.coolaj86.com/coolaj86/greenlock-cli.js )
| [gl-cluster ](https://git.coolaj86.com/coolaj86/greenlock-cluster.js )
| **gl-express**
| [gl-koa ](https://git.coolaj86.com/coolaj86/greenlock-koa.js )
| [gl-hapi ](https://git.coolaj86.com/coolaj86/greenlock-hapi.js )
2016-08-15 23:12:39 +00:00
|
2018-04-20 06:43:02 +00:00
greenlock-express.js
=================
2018-04-20 07:15:00 +00:00
(formerly letsencrypt-express)
2018-04-20 03:27:57 +00:00
2018-04-20 07:14:39 +00:00
Free SSL for node.js.
2016-08-15 23:12:39 +00:00
2018-04-20 07:14:39 +00:00
Fully automatic HTTPS with Express.js
(and all other middleware systems), including virtual hosting (vhost) support with multiple domains.
2018-04-20 07:09:34 +00:00
2018-04-20 07:14:39 +00:00
Certificate renewals happen in the background between 10 and 14 days before expiration (~78 days).
2016-08-15 23:12:39 +00:00
2018-04-20 03:37:56 +00:00
## Now supports Let's Encrypt v2!!
* Let's Encrypt v1 (aka v01)
* Let's Encrypt v2 (aka v02 or ACME draft 11)
* ACME draft 11 (ACME v2 is a misnomer)
2018-04-20 04:18:34 +00:00
* Wildcard domains!! (via dns-01 challenges)
* `*.example.com`
2018-04-20 03:37:56 +00:00
2016-08-12 07:02:33 +00:00
Install
=======
```bash
2017-01-25 22:02:16 +00:00
npm install --save greenlock-express@2.x
2016-08-12 07:02:33 +00:00
```
2016-08-15 23:12:39 +00:00
QuickStart
2016-08-15 23:14:57 +00:00
==========
2018-04-23 20:04:07 +00:00
<!--
2018-04-23 19:55:03 +00:00
[![Free SSL with Greenlock.js ](https://i.imgur.com/Y8ix6Ts.png )](https://youtu.be/e8vaR4CEZ5s)
2018-04-23 20:04:07 +00:00
-->
2018-04-23 19:55:03 +00:00
2018-04-23 20:02:50 +00:00
### Screencast
Watch the QuickStart demonstration: https://youtu.be/e8vaR4CEZ5s
* [0:00 ](https://youtu.be/e8vaR4CEZ5s#t=0 ) - Intro
* [2:22 ](https://youtu.be/e8vaR4CEZ5s#t=142 ) - Demonstrating QuickStart Example
* [6:37 ](https://youtu.be/e8vaR4CEZ5s?t=397 ) - Troubleshooting / Gotchas
### Working Example Code
2018-04-20 07:14:39 +00:00
Here's a completely working example that will get you started.
2018-04-20 08:59:33 +00:00
```
git clone https://git.coolaj86.com/coolaj86/greenlock-express.js.git
pushd greenlock-express.js
npm install
popd
# edit 'email' and 'approveDomains' in
# greenlock-express.js/examples/simple.js
node greenlock-express.js/examples/simple.js
```
2018-04-20 07:14:39 +00:00
All you have to do is start the webserver and then visit it at its domain name.
2016-08-12 07:02:33 +00:00
2016-08-15 23:12:39 +00:00
`app.js` :
2016-08-12 07:02:33 +00:00
```javascript
'use strict';
2017-01-25 22:02:16 +00:00
require('greenlock-express').create({
2016-08-12 07:02:33 +00:00
2018-04-20 04:35:39 +00:00
// Let's Encrypt v2 is ACME draft 11
version: 'draft-11'
// You MUST change 'acme-staging-v02' to 'acme-v02' in production
2018-04-20 03:37:56 +00:00
, server: 'https://acme-staging-v02.api.letsencrypt.org/directory' // staging
2016-08-12 07:02:33 +00:00
2018-04-20 04:35:39 +00:00
// You MUST change this to a valid email address
2016-08-15 23:12:39 +00:00
, email: 'john.doe@example.com'
2016-08-12 07:02:33 +00:00
2018-04-20 04:35:39 +00:00
// You MUST NOT build clients that accept the ToS without asking the user
2016-08-15 23:12:39 +00:00
, agreeTos: true
2016-08-12 07:02:33 +00:00
2018-04-20 04:35:39 +00:00
// You MUST change these to valid domains
// NOTE: all domains will validated and listed on the certificate
, approveDomains: [ 'example.com', 'www.example.com' ]
// You MUST have access to write to directory where certs are saved
// ex: /home/foouser/acme/etc
, configDir: require('path').join(require('os').homedir(), 'acme', 'etc')
2016-08-16 01:15:16 +00:00
2016-08-15 23:12:39 +00:00
, app: require('express')().use('/', function (req, res) {
2018-04-20 04:35:39 +00:00
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end('Hello, World!\n\n💚 🔒.js');
2016-08-15 23:12:39 +00:00
})
2016-08-12 07:02:33 +00:00
2018-04-20 04:35:39 +00:00
//, debug: true
2016-08-15 23:12:39 +00:00
}).listen(80, 443);
2016-08-12 07:02:33 +00:00
```
2018-04-20 04:35:39 +00:00
### What if the example didn't work?
2018-04-20 07:23:22 +00:00
Double check the following:
* **Public Facing IP** for `http-01` challenges
* Are you running this *as* a public-facing webserver (good)? or localhost (bad)?
* Does `ifconfig` show a public address (good)? or a private one - 10.x, 192.168.x, etc (bad)?
* If you're on a non-public server, are you using the `dns-01` challenge?
* **correct ACME version**
* Let's Encrypt **v2** (ACME v2) must use `version: 'draft-11'`
* Let's Encrypt v1 must use `version: 'v01'`
* **valid email**
* You MUST set `email` to a **valid address**
* MX records must validate (`dig MX example.com` for `'john@example.com'` )
* **valid DNS records**
* You MUST set `approveDomains` to real domains
* Must have public DNS records (test with `dig +trace A example.com; dig +trace www.example.com` for `[ 'example.com', 'www.example.com' ]` )
* **write access**
* You MUST set `configDir` to a writeable location (test with `touch ~/acme/etc/tmp.tmp` )
* **port binding privileges**
2018-04-21 02:24:50 +00:00
* You MUST be able to bind to ports 80 and 443
2018-04-20 07:23:22 +00:00
* You can do this via `sudo` or [`setcap` ](https://gist.github.com/firstdoit/6389682 )
* **API limits**
* You MUST NOT exceed the API [**usage limits** ](https://letsencrypt.org/docs/staging-environment/ ) per domain, certificate, IP address, etc
* **Red Lock, Untrusted**
* You MUST change the `server` value **in production**
* Shorten the 'acme-staging-v02' part of the server URL to 'acme-v02'
2016-08-12 07:02:33 +00:00
2018-04-20 07:09:34 +00:00
### Get it working in staging first!
2016-08-12 07:02:33 +00:00
2016-08-15 23:12:39 +00: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 22:02:16 +00:00
when using greenlock for your first time.
2016-08-12 07:02:33 +00:00
2016-08-15 23:12:39 +00:00
In order to avoid being blocked by hitting rate limits with bad requests,
2018-04-20 05:29:33 +00:00
you should always test against the `staging` server
2018-04-20 03:37:56 +00:00
(`https://acme-staging-v02.api.letsencrypt.org/directory`) first.
2016-08-12 07:02:33 +00:00
2018-04-30 15:42:33 +00:00
Plugins
=====
2018-04-30 15:46:34 +00:00
**IMPORTANT**: Community plugins may or may not be maintained and working. Please try with the defaults before switching to community plugins.
2018-04-30 15:42:33 +00:00
| | challenge | store |
|:--------------:|:---------:|:-----:|
| Build Your Own | [le-challenge-SPEC ](https://git.coolaj86.com/coolaj86/le-challenge-manual.js.git ) | [le-store-SPEC ](https://git.coolaj86.com/coolaj86/le-store-SPEC.js.git ) |
| Defaults (fs) | [le-challenge-fs ](https://git.coolaj86.com/coolaj86/le-challenge-fs.js.git ) | [le-store-certbot ](https://git.coolaj86.com/coolaj86/le-store-certbot.js.git ) |
| Full List | [Search le-store- on npm ](https://www.npmjs.com/search?q=le-store- ) | [Search le-challenge- on npm ](https://www.npmjs.com/search?q=le-challenge- ) |
| AWS Route 53 | [thadeetrompetter/le-challenge-route53 ](https://github.com/thadeetrompetter/le-challenge-route53 ) | - |
| AWS S3 | | [paco3346/le-store-awss3 ](https://github.com/paco3346/le-store-awss3 ) |
| AWS S3 | [llun/le-challenge-s3 ](https://github.com/llun/le-challenge-s3 ) | [llun/le-store-s3 ](https://github.com/llun/le-store-s3 ) |
| json | - | [paulgrove/le-store-simple-fs ](https://github.com/paulgrove/le-store-simple-fs )
| Redis | - | [digitalbazaar/le-store-redis ](https://github.com/digitalbazaar/le-store-redis ) |
2018-04-30 15:46:34 +00:00
Bugs: Please report bugs with the community plugins to the appropriate owner first, then here if you don't get a response.
2016-08-15 23:14:57 +00:00
Usage
=====
2016-08-15 23:12:39 +00: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),
2018-04-20 08:59:33 +00:00
now here's the switch.
We have another completely working example that will provides a little more to build off of.
```
git clone https://git.coolaj86.com/coolaj86/greenlock-express.js.git
pushd greenlock-express.js
npm install
popd
# replace 'fooCheckDb' in
# greenlock-express.js/examples/normal.js
node greenlock-express.js/examples/normal.js
```
It looks a little more like this:
2016-08-12 07:02:33 +00:00
2016-08-15 23:12:39 +00:00
`serve.js` :
```javascript
'use strict';
2016-08-12 07:02:33 +00:00
2018-04-20 05:32:15 +00:00
// returns an instance of greenlock.js with additional helper methods
2017-01-25 22:02:16 +00:00
var lex = require('greenlock-express').create({
2018-04-20 03:37:56 +00:00
// set to https://acme-v02.api.letsencrypt.org/directory in production
server: 'https://acme-staging-v02.api.letsencrypt.org/directory'
, version: 'draft-11' // Let's Encrypt v2 (ACME v2)
2016-08-12 07:02:33 +00:00
2016-08-15 23:12:39 +00:00
// If you wish to replace the default plugins, you may do so here
//
2016-08-19 19:31:37 +00:00
, challenges: { 'http-01': require('le-challenge-fs').create({ webrootPath: '/tmp/acme-challenges' }) }
2016-08-18 06:27:47 +00:00
, store: require('le-store-certbot').create({ webrootPath: '/tmp/acme-challenges' })
2016-08-18 18:58:40 +00:00
// You probably wouldn't need to replace the default sni handler
2017-11-25 02:02:56 +00:00
// See https://git.coolaj86.com/coolaj86/le-sni-auto if you think you do
2016-08-18 18:58:40 +00:00
//, sni: require('le-sni-auto').create({})
2016-08-12 07:48:21 +00:00
2016-08-17 15:11:10 +00:00
, approveDomains: approveDomains
2016-08-15 23:12:39 +00:00
});
2016-08-17 15:09:43 +00:00
```
2016-08-12 07:02:33 +00:00
2018-04-20 07:09:34 +00:00
The Automatic Certificate Issuance is initiated via SNI (`httpsOptions.SNICallback`).
For security, domain validation MUST have an approval callback in *production* .
2016-08-17 15:11:10 +00: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 19:37:53 +00: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 15:11:10 +00:00
cb(null, { options: opts, certs: certs });
}
```
2016-08-12 07:02:33 +00:00
2016-08-17 15:09:43 +00:00
```javascript
2016-08-15 23:12:39 +00:00
// handles acme-challenge and redirects to https
2016-08-19 22:22:57 +00:00
require('http').createServer(lex.middleware(require('redirect-https')())).listen(80, function () {
2016-08-15 23:12:39 +00:00
console.log("Listening for ACME http-01 challenges on", this.address());
});
2016-08-12 07:02:33 +00:00
2016-08-15 23:12:39 +00:00
var app = require('express')();
app.use('/', function (req, res) {
res.end('Hello, World!');
});
2016-08-12 07:02:33 +00:00
2016-08-15 23:12:39 +00:00
// handles your app
2016-08-17 15:08:47 +00:00
require('https').createServer(lex.httpsOptions, lex.middleware(app)).listen(443, function () {
2016-08-15 23:12:39 +00:00
console.log("Listening for ACME tls-sni-01 challenges and serve app on", this.address());
});
2016-08-12 07:02:33 +00:00
```
2016-08-17 15:25:07 +00: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 23:14:57 +00:00
API
===
2016-08-12 07:56:19 +00:00
2016-08-16 01:15:16 +00:00
This module is an elaborate ruse (to provide an oversimplified example and to nab some SEO).
2018-04-20 05:32:15 +00:00
The API is actually located at [greenlock.js options ](https://git.coolaj86.com/coolaj86/greenlock.js )
(because all options are simply passed through to `greenlock.js` proper without modification).
2016-08-15 23:12:39 +00:00
2018-04-20 05:32:15 +00:00
The only "API" consists of two options, the rest is just a wrapper around `greenlock.js` to take LOC from 15 to 5:
2016-08-15 23:12:39 +00:00
2016-08-16 01:15:16 +00: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 07:56:19 +00:00
2018-04-20 05:32:15 +00:00
Brief overview of some simple options for `greenlock.js` :
2016-08-12 07:56:19 +00:00
2018-04-20 05:32:15 +00:00
* `opts.server` set to https://acme-v02.api.letsencrypt.org/directory in production
2018-04-20 03:37:56 +00:00
* `opts.version` set to `v01` for Let's Encrypt v1 or `draft-11` for Let's Encrypt v2 (mistakenly called ACME v2)
2016-08-16 01:15:16 +00:00
* `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 15:25:07 +00: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-16 01:15:16 +00: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.