!["Greenlock Logo"](https://git.coolaj86.com/coolaj86/greenlock.js/raw/branch/master/logo/greenlock-1063x250.png) Greenlock™ for node.js ===== Greenlock provides Free SSL, Free Wildcard SSL, and Fully Automated HTTPS
certificates issued by Let's Encrypt v2 via [ACME](https://git.coolaj86.com/coolaj86/acme-v2.js) | Sponsored by [ppl](https://ppl.family) | Greenlock works in the [Commandline](https://git.coolaj86.com/coolaj86/greenlock-cli.js) (cli), as a [Web Server](https://git.coolaj86.com/coolaj86/greenlock-server.js), in [Web Browsers](https://git.coolaj86.com/coolaj86/greenlock.html) (WebCrypto), and with **node.js** ([npm](https://www.npmjs.com/package/greenlock)). Features ======== - [x] Actively Maintained and Supported - [x] Automatic HTTPS - [x] Free SSL - [x] Free Wildcard SSL - [x] Multiple domain support (up to 100 altnames per SAN) - [x] Dynamic Virtual Hosting (vhost) - [x] Automatical renewal (10 to 14 days before expiration) - [x] Great ACME support via [acme.js](https://git.coolaj86.com/coolaj86/acme-v2.js) - [x] ACME draft 11 - [x] Let's Encrypt v2 - [x] Let's Encrypt v1 - [x] [Commandline](https://git.coolaj86.com/coolaj86/greenlock-cli.js) (cli) Utilities - [x] Works with `bash`, `fish`, `zsh`, `cmd.exe`, `PowerShell`, and more - [x] [Browser](https://git.coolaj86.com/coolaj86/greenlock.html) (cli) Support - [x] Full node.js support, with modules for - [x] [http/https](https://git.coolaj86.com/coolaj86/greenlock-express.js/src/branch/master/examples/https-server.js), [Express.js](https://git.coolaj86.com/coolaj86/greenlock-express.js), [cluster](https://git.coolaj86.com/coolaj86/greenlock-cluster.js), [hapi](https://git.coolaj86.com/coolaj86/greenlock-hapi.js), [Koa](https://git.coolaj86.com/coolaj86/greenlock-koa.js), [rill](https://git.coolaj86.com/coolaj86/greenlock-rill.js), [restify](https://git.coolaj86.com/coolaj86/greenlock-restify.js), spdy, etc - [x] Great for securing your Raspberry Pi - [x] Extensible Plugin Support - [x] AWS S3, AWS Route53, Azure, CloudFlare, Consul, Digital Ocean, etcd, Redis Greenlock.js for Middleware ------ Documentation for using Greenlock with [http/https](https://git.coolaj86.com/coolaj86/greenlock-express.js/src/branch/master/examples/https-server.js), [Express.js](https://git.coolaj86.com/coolaj86/greenlock-express.js), [cluster](https://git.coolaj86.com/coolaj86/greenlock-cluster.js), [hapi](https://git.coolaj86.com/coolaj86/greenlock-hapi.js), [Koa](https://git.coolaj86.com/coolaj86/greenlock-koa.js), [rill](https://git.coolaj86.com/coolaj86/greenlock-rill.js). [restify](https://git.coolaj86.com/coolaj86/greenlock-restify.js). Table of Contents ================= * Install * Simple Examples * Example with ALL OPTIONS * API * Developer API * Change History * License Install ======= ```bash npm install --save greenlock@2.x ``` **Note**: Ignore errors related to `ursa`. It is an optional dependency used when available. For many people it will not install properly, but it's only necessary on ARM devices (i.e. Raspberry Pi). Easy as 1, 2, 3... 4 ===== Greenlock is built to incredibly easy to use, without sacrificing customization or extensibility. The following examples range from just a few lines of code for getting started, to more robust examples that you might start with for an enterprise-grade use of the ACME api. * Automatic HTTPS (for single sites) * Fully Automatic HTTPS (for multi-domain vhosts) * Manual HTTPS (for API integration) Automatic HTTPS --------------- **Note**: For (fully) automatic HTTPS you may prefer the [Express.js module](https://git.coolaj86.com/coolaj86/greenlock-express.js) This works for most people, but it's not as fun as some of the other examples. Great when - [x] You only need a limited number of certificates - [x] You want to use the bare node http and https modules without fluff ```js //////////////////// // INIT GREENLOCK // //////////////////// var path = require('path'); var os = require('os') var Greenlock = require('greenlock'); var acmeEnv = 'staging-'; var greenlock = Greenlock.create({ agreeTos: true // Accept Let's Encrypt v2 Agreement , email: 'user@example.com' // IMPORTANT: Change email and domains , approveDomains: [ 'example.com' ] , communityMember: false // Optionally get important updates (security, api changes, etc) // and submit stats to help make Greenlock better , version: 'draft-11' , server: 'https://acme-' + acmeEnv + 'v02.api.letsencrypt.org/directory' , configDir: path.join(os.homedir(), 'acme/etc') }); //////////////////// // CREATE SERVERS // //////////////////// var redir = require('redirect-https')(); require('http').createServer(greenlock.middleware(redir)).listen(80); require('https').createServer(greenlock.tlsOptions, function (req, res) { res.end('Hello, Secure World!'); }).listen(443); ``` Fully Automatic HTTPS ------------ **Note**: For (fully) automatic HTTPS you may prefer the [Express.js module](https://git.coolaj86.com/coolaj86/greenlock-express.js) Great when - [x] You have a growing number of domains - [x] You're integrating into your own hosting solution - [x] Customize ACME http-01 or dns-01 challenge ```js //////////////////// // INIT GREENLOCK // //////////////////// var path = require('path'); var os = require('os') var Greenlock = require('greenlock'); var acmeEnv = 'staging-'; var greenlock = Greenlock.create({ version: 'draft-11' , server: 'https://acme-' + acmeEnv + 'v02.api.letsencrypt.org/directory' // approve a growing list of domains , approveDomains: approveDomains // If you wish to replace the default account and domain key storage plugin , store: require('le-store-certbot').create({ configDir: path.join(os.homedir(), 'acme/etc') , webrootPath: '/tmp/acme-challenges' }) }); ///////////////////// // APPROVE DOMAINS // ///////////////////// var http01 = require('le-challenge-fs').create({ webrootPath: '/tmp/acme-challenges' }); function approveDomains(opts, certs, cb) { // This is where you check your database and associated // email addresses with domains and agreements and such // Opt-in to submit stats and get important updates opts.communityMember = true; // If you wish to replace the default challenge plugin, you may do so here opts.challenges = { 'http-01': http01 }; // 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; } // NOTE: you can also change other options such as `challengeType` and `challenge` // opts.challengeType = 'http-01'; // opts.challenge = require('le-challenge-fs').create({}); cb(null, { options: opts, certs: certs }); } //////////////////// // CREATE SERVERS // //////////////////// var redir = require('redirect-https')(); require('http').createServer(greenlock.middleware(redir)).listen(80); require('https').createServer(greenlock.tlsOptions, function (req, res) { res.end('Hello, Secure World!'); }).listen(443); ``` Manual HTTPS ------------- Here's a taste of the API that you might use if building a commandline tool or API integration that doesn't use node's SNICallback. ``` var staging = true; ///////////////////// // SET USER PARAMS // ///////////////////// var opts = { domains: [ 'example.com' // CHANGE EMAIL AND DOMAINS , 'www.example.com' ] , email: 'user@example.com' , agreeTos: true // Accept Let's Encrypt v2 Agreement , communityMember: true // Help make Greenlock better by submitting // stats and getting updates }; //////////////////// // INIT GREENLOCK // //////////////////// var greenlock = require('greenlock').create({ version: 'draft-11' , server: 'https://acme-' + (staging ? 'staging-' : '') + 'v02.api.letsencrypt.org/directory' , configDir: '/tmp/acme/etc' }); /////////////////// // GET TLS CERTS // /////////////////// greenlock.register(opts).then(function (certs) { console.log(certs); // privkey, cert, chain, expiresAt, issuedAt, subject, altnames }, function (err) { console.error(err); }); ``` The domain key and ssl certificates you get back can be used in a webserver like this: ```js var tlsOptions = { key: certs.privkey, cert: certs.cert + '\r\n' + certs.chain }; require('https').createServer(tlsOptions, function (req, res) { res.end('Hello, Secure World!'); }).listen(443); ``` Example with ALL OPTIONS ========= 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' }); 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 } , 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 // renewals happen at a random time within this window , renewWithin: 14 * 24 * 60 * 60 * 1000 // certificate renewal may begin at this time , renewBy: 10 * 24 * 60 * 60 * 1000 // certificate renewal should happen by this time , 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 * 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.2 - Let's Encrypt v2 Support * v2.2.11 - documentation updates * v2.2.10 - don't let SNICallback swallow approveDomains errors 6286883fc2a6ebfff711a540a2e4d92f3ac2907c * v2.2.8 - communityMember option support * v2.2.7 - bugfix for wildcard support * v2.2.5 - node v6.x compat * v2.2.4 - don't promisify all of `dns` * v2.2.3 - `renewWithin` default to 14 days * v2.2.2 - replace git dependency with npm * v2.2.1 - April 2018 **Let's Encrypt v2** support * 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 Greenlock™ is a trademark of AJ ONeal