304 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			304 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
greenlock (node-letsencrypt)
 | 
						|
=========
 | 
						|
 | 
						|
Now supports **Let's Encrypt v2**!!
 | 
						|
 | 
						|
| [acme-v2.js](https://git.coolaj86.com/coolaj86/acme-v2.js)
 | 
						|
| **greenlock**
 | 
						|
| [greenlock-cli](https://git.coolaj86.com/coolaj86/greenlock-cli.js)
 | 
						|
| [greenlock-express](https://git.coolaj86.com/coolaj86/greenlock-express.js)
 | 
						|
| [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)
 | 
						|
<<<<<<< HEAD
 | 
						|
| Sponsored by [ppl](https://ppl.family). Created at [Daplie](https://dapliefounder.com).
 | 
						|
=======
 | 
						|
 | 
						|
| Sponsored by [ppl](https://ppl.family)
 | 
						|
>>>>>>> 84e21d2385da4e2260befd1c91ac4564e1e21301
 | 
						|
 | 
						|
Automatic [Let's Encrypt](https://letsencrypt.org) (ACME) HTTPS / TLS / SSL Certificates for node.js
 | 
						|
 | 
						|
Free SSL with [90-day](https://letsencrypt.org/2015/11/09/why-90-days.html) HTTPS / TLS Certificates
 | 
						|
 | 
						|
Are these the droids you're looking for?
 | 
						|
------
 | 
						|
 | 
						|
This is a **low-level library** for implementing ACME / LetsEncrypt Clients, CLIs,
 | 
						|
system tools, and abstracting storage backends (file vs db, etc).
 | 
						|
 | 
						|
For `express`, raw `https` or `spdy`, or `restify` (same as raw https) see
 | 
						|
[**greenlock-express** (previously letsencrypt-express)](https://git.coolaj86.com/coolaj86/greenlock-express.js) and [greenlock-cluster (previously letsencrypt-cluster)](https://git.coolaj86.com/coolaj86/greenlock-cluster.js).
 | 
						|
 | 
						|
For `hapi` see [greenlock-hapi (previously letsencrypt-hapi)](https://git.coolaj86.com/coolaj86/greenlock-hapi.js).
 | 
						|
 | 
						|
For `koa` or `rill`
 | 
						|
see [greenlock-koa (previously letsencrypt-koa)](https://git.coolaj86.com/coolaj86/greenlock-koa.js).
 | 
						|
 | 
						|
For `bash`, `fish`, `zsh`, `cmd.exe`, `PowerShell`
 | 
						|
see [**greenlock-cli** (previously letsencrypt-cli)](https://git.coolaj86.com/coolaj86/greenlock-cli.js).
 | 
						|
 | 
						|
Install
 | 
						|
=======
 | 
						|
 | 
						|
`greenlock` requires at least two plugins:
 | 
						|
one for managing certificate storage and the other for handling ACME challenges.
 | 
						|
 | 
						|
The default storage plugin is [`le-store-certbot`](https://git.coolaj86.com/coolaj86/le-store-certbot.js)
 | 
						|
and the default challenge is [`le-challenge-fs`](https://git.coolaj86.com/coolaj86/le-challenge-fs.js).
 | 
						|
 | 
						|
```bash
 | 
						|
npm install --save greenlock@2.x
 | 
						|
 | 
						|
npm install --save le-store-certbot@2.x   # default plugin for accounts, certificates, and keypairs
 | 
						|
npm install --save le-challenge-fs@2.x    # default plugin for http-01 challenge
 | 
						|
npm install --save le-challenge-sni@2.x   # default plugin for tls-sni-01 and tls-sni-02 challenge
 | 
						|
npm install --save le-acme-core@2.x       # default plugin for ACME spec
 | 
						|
npm install --save le-sni-auto@2.x        # default plugin for SNICallback
 | 
						|
```
 | 
						|
 | 
						|
**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.
 | 
						|
 | 
						|
Usage
 | 
						|
=====
 | 
						|
 | 
						|
It's very simple and easy to use, but also very complete and easy to extend and customize.
 | 
						|
 | 
						|
### Overly Simplified Example
 | 
						|
 | 
						|
Against my better judgement I'm providing a terribly oversimplified example
 | 
						|
of how to use this library:
 | 
						|
 | 
						|
```javascript
 | 
						|
var le = require('greenlock').create({ server: 'staging' });
 | 
						|
 | 
						|
var opts = {
 | 
						|
  domains: ['example.com'], email: 'user@email.com', agreeTos: true
 | 
						|
};
 | 
						|
 | 
						|
le.register(opts).then(function (certs) {
 | 
						|
  console.log(certs);
 | 
						|
  // privkey, cert, chain, expiresAt, issuedAt, subject, altnames
 | 
						|
}, function (err) {
 | 
						|
  console.error(err);
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
You also need some sort of server to handle the acme challenge:
 | 
						|
 | 
						|
```javascript
 | 
						|
var app = express();
 | 
						|
app.use('/', le.middleware());
 | 
						|
```
 | 
						|
 | 
						|
Note: The `webrootPath` string is a template.
 | 
						|
Any occurance of `:hostname` will be replaced
 | 
						|
with the domain for which we are requested certificates.
 | 
						|
 | 
						|
### Useful Example
 | 
						|
 | 
						|
The configuration consists of 3 components:
 | 
						|
 | 
						|
* Storage Backend (search npm for projects starting with 'le-store-')
 | 
						|
* ACME Challenge Handlers (search npm for projects starting with 'le-challenge-')
 | 
						|
* Letsencryt Config (this is all you)
 | 
						|
 | 
						|
```javascript
 | 
						|
'use strict';
 | 
						|
 | 
						|
var LE = require('greenlock');
 | 
						|
var le;
 | 
						|
 | 
						|
 | 
						|
// Storage Backend
 | 
						|
var leStore = require('le-store-certbot').create({
 | 
						|
  configDir: '~/acme/etc'                                 // or /etc/letsencrypt or wherever
 | 
						|
, debug: false
 | 
						|
});
 | 
						|
 | 
						|
 | 
						|
// ACME Challenge Handlers
 | 
						|
var leHttpChallenge = require('le-challenge-fs').create({
 | 
						|
  webrootPath: '~/acme/var/'                              // or template string such as
 | 
						|
, debug: false                                            // '/srv/www/:hostname/.well-known/acme-challenge'
 | 
						|
});
 | 
						|
var leSniChallenge = require('le-challenge-sni').create({
 | 
						|
, debug: false
 | 
						|
});
 | 
						|
 | 
						|
 | 
						|
function leAgree(opts, agreeCb) {
 | 
						|
  // opts = { email, domains, tosUrl }
 | 
						|
  agreeCb(null, opts.tosUrl);
 | 
						|
}
 | 
						|
 | 
						|
le = LE.create({
 | 
						|
  version: 'draft-11'                                     // 'draft-11' or 'v01'
 | 
						|
                                                          // 'draft-11' is for Let's Encrypt v2 otherwise known as ACME draft 11
 | 
						|
                                                          // 'v02' is an alias for 'draft-11'
 | 
						|
                                                          // 'v01' is for the pre-spec Let's Encrypt v1
 | 
						|
  //
 | 
						|
  // staging API
 | 
						|
  server: 'https://acme-staging-v02.api.letsencrypt.org/directory'
 | 
						|
 | 
						|
  //
 | 
						|
  // production API
 | 
						|
  //server: 'https://acme-v02.api.letsencrypt.org/directory'
 | 
						|
 | 
						|
, store: leStore                                          // handles saving of config, accounts, and certificates
 | 
						|
, challenges: {
 | 
						|
    'http-01': leHttpChallenge                            // handles /.well-known/acme-challege keys and tokens
 | 
						|
  , 'tls-sni-01': leSniChallenge                          // handles generating a certificate with the correct name
 | 
						|
  , 'tls-sni-02': leSniChallenge
 | 
						|
  }
 | 
						|
, challengeType: 'http-01'                                // default to this challenge type
 | 
						|
, agreeToTerms: leAgree                                   // hook to allow user to view and accept LE TOS
 | 
						|
//, sni: require('le-sni-auto').create({})                // handles sni callback
 | 
						|
, debug: false
 | 
						|
//, log: function (debug) {console.log.apply(console, args);} // handles debug outputs
 | 
						|
});
 | 
						|
 | 
						|
 | 
						|
// If using express you should use the middleware
 | 
						|
// app.use('/', le.middleware());
 | 
						|
//
 | 
						|
// Otherwise you should see the test file for usage of this:
 | 
						|
// le.challenges['http-01'].get(opts.domain, key, val, done)
 | 
						|
 | 
						|
 | 
						|
 | 
						|
// Check in-memory cache of certificates for the named domain
 | 
						|
le.check({ domains: [ 'example.com' ] }).then(function (results) {
 | 
						|
  if (results) {
 | 
						|
    // we already have certificates
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  // Register Certificate manually
 | 
						|
  le.register({
 | 
						|
 | 
						|
    domains: ['example.com']                                // CHANGE TO YOUR DOMAIN (list for SANS)
 | 
						|
  , email: 'user@email.com'                                 // CHANGE TO YOUR EMAIL
 | 
						|
  , agreeTos: ''                                            // set to tosUrl string (or true) to pre-approve (and skip agreeToTerms)
 | 
						|
  , rsaKeySize: 2048                                        // 2048 or higher
 | 
						|
  , challengeType: 'http-01'                                // http-01, tls-sni-01, or dns-01
 | 
						|
 | 
						|
  }).then(function (results) {
 | 
						|
 | 
						|
    console.log('success');
 | 
						|
 | 
						|
  }, function (err) {
 | 
						|
 | 
						|
    // Note: you must either use le.middleware() with express,
 | 
						|
    // manually use le.challenges['http-01'].get(opts, domain, key, val, done)
 | 
						|
    // or have a webserver running and responding
 | 
						|
    // to /.well-known/acme-challenge at `webrootPath`
 | 
						|
    console.error('[Error]: node-greenlock/examples/standalone');
 | 
						|
    console.error(err.stack);
 | 
						|
 | 
						|
  });
 | 
						|
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
Here's what `results` looks like:
 | 
						|
 | 
						|
```javascript
 | 
						|
{ privkey: ''     // PEM encoded private key
 | 
						|
, cert: ''        // PEM encoded cert
 | 
						|
, chain: ''       // PEM encoded intermediate cert
 | 
						|
, issuedAt: 0     // notBefore date (in ms) parsed from cert
 | 
						|
, expiresAt: 0    // notAfter date (in ms) parsed from cert
 | 
						|
, subject: ''     // example.com
 | 
						|
, altnames: []    // example.com,www.example.com
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
API
 | 
						|
---
 | 
						|
 | 
						|
The full end-user API is exposed in the example above and includes all relevant options.
 | 
						|
 | 
						|
```
 | 
						|
le.register(opts)
 | 
						|
le.check(opts)
 | 
						|
```
 | 
						|
 | 
						|
### Helper Functions
 | 
						|
 | 
						|
We do expose a few helper functions:
 | 
						|
 | 
						|
* LE.validDomain(hostname) // returns '' or the hostname string if it's a valid ascii or punycode domain name
 | 
						|
 | 
						|
TODO fetch domain tld list
 | 
						|
 | 
						|
### Template Strings
 | 
						|
 | 
						|
The following variables will be tempalted in any strings passed to the options object:
 | 
						|
 | 
						|
* `~/` replaced with `os.homedir()` i.e. `/Users/aj`
 | 
						|
* `:hostname` replaced with the first domain in the list i.e. `example.com`
 | 
						|
 | 
						|
Developer API
 | 
						|
-------------
 | 
						|
 | 
						|
If you are developing an `le-store-*` or `le-challenge-*` plugin you need to be aware of
 | 
						|
additional internal API expectations.
 | 
						|
 | 
						|
**IMPORTANT**:
 | 
						|
 | 
						|
Use `v2.0.0` as your initial version - NOT v0.1.0 and NOT v1.0.0 and NOT v3.0.0.
 | 
						|
This is to indicate that your module is compatible with v2.x of node-greenlock.
 | 
						|
 | 
						|
Since the public API for your module is defined by node-greenlock the major version
 | 
						|
should be kept in sync.
 | 
						|
 | 
						|
### store implementation
 | 
						|
 | 
						|
See <https://git.coolaj86.com/coolaj86/le-store-SPEC.js>
 | 
						|
 | 
						|
* getOptions()
 | 
						|
* accounts.
 | 
						|
  * checkKeypair(opts, cb)
 | 
						|
  * check(opts, cb)
 | 
						|
  * setKeypair(opts, keypair, cb)
 | 
						|
  * set(opts, reg, cb)
 | 
						|
* certificates.
 | 
						|
  * checkKeypair(opts, cb)
 | 
						|
  * check(opts, cb)
 | 
						|
  * setKeypair(opts, keypair, cb)
 | 
						|
  * set(opts, reg, cb)
 | 
						|
 | 
						|
### challenge implementation
 | 
						|
 | 
						|
See https://git.coolaj86.com/coolaj86/le-challenge-fs.js
 | 
						|
 | 
						|
* `.set(opts, domain, key, value, cb);`         // opts will be saved with domain/key
 | 
						|
* `.get(opts, domain, key, cb);`                // opts will be retrieved by domain/key
 | 
						|
* `.remove(opts, domain, key, cb);`             // opts will be retrieved by domain/key
 | 
						|
 | 
						|
Change History
 | 
						|
==============
 | 
						|
 | 
						|
* v2.1.17 - Nov 5th 2017 migrate back to personal repo
 | 
						|
* v2.1.9 - Jan 18th 2017 renamed to greenlock
 | 
						|
* v2.0.2 - Aug 9th 2016 update readme
 | 
						|
* v2.0.1 - Aug 9th 2016
 | 
						|
  * major refactor
 | 
						|
  * simplified API
 | 
						|
  * modular plugins
 | 
						|
  * knock out bugs
 | 
						|
* v1.5.0 now using letiny-core v2.0.0 and rsa-compat
 | 
						|
* v1.4.x I can't remember... but it's better!
 | 
						|
* v1.1.0 Added letiny-core, removed node-letsencrypt-python
 | 
						|
* v1.0.2 Works with node-letsencrypt-python
 | 
						|
* v1.0.0 Thar be dragons
 | 
						|
 | 
						|
LICENSE
 | 
						|
=======
 | 
						|
 | 
						|
Dual-licensed MIT and Apache-2.0
 | 
						|
 | 
						|
See LICENSE
 |