mirror of
				https://github.com/therootcompany/greenlock.js.git
				synced 2024-11-16 17:29:00 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			570 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			570 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # Greenlock v3 on its way (Nov 1st, 2019)
 | |
| 
 | |
| Greenlock v3 is in private beta (for backers) and will be available publicly by Nov 1st.
 | |
| 
 | |
| You can keep an eye for updates on the [campaign page](https://indiegogo.com/at/greenlock) and,
 | |
| if this has been a useful project that's saved you time, [please contribute](https://paypal.me/rootprojects/99).
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| # [Greenlock](https://git.rootprojects.org/root/greenlock.js)™ for node.js | a [Root](https://rootprojects.org) project
 | |
| 
 | |
| Greenlock provides Free SSL, Free Wildcard SSL, and Fully Automated HTTPS <br>
 | |
| <small>certificates issued by Let's Encrypt v2 via [ACME](https://git.rootprojects.org/root/acme-v2.js)</small>
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| Greenlock works
 | |
| in the [Commandline](https://git.rootprojects.org/root/greenlock-cli.js) (cli),
 | |
| as a [Web Server](https://git.rootprojects.org/root/greenlock-express.js),
 | |
| in [Web Browsers](https://greenlock.domains) (WebCrypto),
 | |
| and with **node.js** ([npm](https://www.npmjs.com/package/greenlock)).
 | |
| 
 | |
| # Features
 | |
| 
 | |
| -   [x] Actively Maintained and Commercially Supported
 | |
|     -   [x] VanillaJS
 | |
|     -   [x] Limited Dependencies
 | |
|     -   [x] MPL-2.0 licensed (great for hobbyists and DIYers)
 | |
|     -   [x] [Contact us](mailto:support@rootprojects.org?subject=Greenlock%20Commercial%20Support) for Business Support Plans and Commercial LTS Licensing (great for IoT, On-Prem, Web Hosting, etc)
 | |
| -   [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.rootprojects.org/root/acme-v2.js)
 | |
|     -   [x] "dry run" with self-diagnostics
 | |
|     -   [x] ACME draft 12
 | |
|     -   [x] Let's Encrypt v2
 | |
|     -   [x] ~Let's Encrypt v1~ (deprecated)
 | |
| -   [x] [Commandline](https://git.rootprojects.org/root/greenlock-cli.js) (cli) Utilities
 | |
|     -   [x] Works with `bash`, `fish`, `zsh`, `cmd.exe`, `PowerShell`, and more
 | |
| -   [x] [Browser](https://git.rootprojects.org/root/greenlock.html) Support
 | |
| -   [x] Full node.js support, with modules for
 | |
|     -   [x] [http/https](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples), [Express.js](https://git.rootprojects.org/root/greenlock-express.js), [hapi](https://git.rootprojects.org/root/greenlock-hapi.js), [Koa](https://git.rootprojects.org/root/greenlock-koa.js), [rill](https://git.rootprojects.org/root/greenlock-rill.js), spdy, etc
 | |
| -   [x] Great for securing your Raspberry Pi and IoT projects
 | |
| -   [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.rootprojects.org/root/greenlock-express.js/src/branch/master/examples),
 | |
| [Express.js](https://git.rootprojects.org/root/greenlock-express.js),
 | |
| [hapi](https://git.rootprojects.org/root/greenlock-hapi.js),
 | |
| [Koa](https://git.rootprojects.org/root/greenlock-koa.js),
 | |
| [rill](https://git.rootprojects.org/root/greenlock-rill.js).
 | |
| 
 | |
| # Table of Contents
 | |
| 
 | |
| -   Install
 | |
| -   **QuickStart**
 | |
| -   Simple Examples
 | |
| -   Example with ALL OPTIONS
 | |
| -   API
 | |
| -   Developer API
 | |
| -   Change History
 | |
| -   License
 | |
| 
 | |
| # Install
 | |
| 
 | |
| ```bash
 | |
| npm install --save greenlock@2.x
 | |
| ```
 | |
| 
 | |
| **Optional** for _more efficient_ RSA key generation you must use node v10.12+
 | |
| <small>(important for those on ARM devices like Raspberry Pi)</small>
 | |
| 
 | |
| ### Production vs Staging
 | |
| 
 | |
| If at first you don't succeed, stop and switch to staging.
 | |
| 
 | |
| I've implemented a "dry run" loopback test with self diagnostics
 | |
| so it's pretty safe to start off with the production URLs
 | |
| and be far less likely to hit the bad request rate limits.
 | |
| 
 | |
| However, if your first attempt to get a certificate fails
 | |
| I'd recommend switching to the staging acme server to debug -
 | |
| unless you're very clear on what the failure was and how to fix it.
 | |
| 
 | |
| ```
 | |
| { server: 'https://acme-staging-v02.api.letsencrypt.org/directory' }
 | |
| ```
 | |
| 
 | |
| ### QuickStart Screencast
 | |
| 
 | |
| Watch the QuickStart demonstration: [https://youtu.be/e8vaR4CEZ5s](https://youtu.be/e8vaR4CEZ5s)
 | |
| 
 | |
| <a href="https://www.youtube.com/watch?v=e8vaR4CEZ5s&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk"><img src="https://i.imgur.com/Y8ix6Ts.png" title="QuickStart Video" alt="YouTube Video Preview" /></a>
 | |
| 
 | |
| -   [0:00](https://www.youtube.com/watch?v=e8vaR4CEZ5s&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk#t=0) - Intro
 | |
| -   [2:22](https://www.youtube.com/watch?v=e8vaR4CEZ5s&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk#t=142) - Demonstrating QuickStart Example
 | |
| -   [6:37](https://www.youtube.com/watch?v=e8vaR4CEZ5s&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk?t=397) - Troubleshooting / Gotchas
 | |
| 
 | |
| #### Production Configuration (Part 2)
 | |
| 
 | |
| -   [1:00](https://www.youtube.com/watch?v=bTEn93gxY50&index=2&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk&t=60) - Bringing Greenlock into an Existing Express Project
 | |
| -   [2:26](https://www.youtube.com/watch?v=bTEn93gxY50&index=2&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk&t=146) - The `approveDomains` callback
 | |
| 
 | |
| #### Security Concerns (Part 3)
 | |
| 
 | |
| -   [0:00](https://www.youtube.com/watch?v=aZgVqPzoZTY&index=3&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk) - Potential Attacks, and Mitigation
 | |
| 
 | |
| # 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.rootprojects.org/root/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 greenlock = require('greenlock').create({
 | |
| 	email: 'user@example.com', // IMPORTANT: Change email and domains
 | |
| 	agreeTos: true, // Accept Let's Encrypt v2 Agreement
 | |
| 	configDir: '~/.config/acme', // A writable folder (a non-fs plugin)
 | |
| 
 | |
| 	communityMember: true, // Get (rare) non-mandatory updates about cool greenlock-related stuff (default false)
 | |
| 	securityUpdates: true // Important and mandatory notices related to security or breaking API changes (default true)
 | |
| });
 | |
| ```
 | |
| 
 | |
| ```js
 | |
| ////////////////////
 | |
| // CREATE SERVERS //
 | |
| ////////////////////
 | |
| 
 | |
| var redir = require('redirect-https')();
 | |
| require('http')
 | |
| 	.createServer(greenlock.middleware(redir))
 | |
| 	.listen(80);
 | |
| 
 | |
| require('spdy')
 | |
| 	.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.rootprojects.org/root/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 greenlock = Greenlock.create({
 | |
| 	version: 'draft-12',
 | |
| 	server: 'https://acme-v02.api.letsencrypt.org/directory',
 | |
| 
 | |
| 	// Use the approveDomains callback to set per-domain config
 | |
| 	// (default: approve any domain that passes self-test of built-in challenges)
 | |
| 	approveDomains: approveDomains,
 | |
| 
 | |
| 	// the default servername to use when the client doesn't specify
 | |
| 	servername: 'example.com',
 | |
| 
 | |
| 	// If you wish to replace the default account and domain key storage plugin
 | |
| 	store: require('le-store-fs').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
 | |
| 	// certs.domains;
 | |
| 	// certs.altnames;
 | |
| 	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.
 | |
| 
 | |
| ```
 | |
| 
 | |
| 
 | |
| /////////////////////
 | |
| // 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-12'
 | |
| , server: 'https://acme-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 Greenlock = require('greenlock');
 | |
| var greenlock;
 | |
| 
 | |
| 
 | |
| // Storage Backend
 | |
| var leStore = require('greenlock-store-fs').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);
 | |
| }
 | |
| 
 | |
| greenlock = Greenlock.create({
 | |
|   version: 'draft-12'                                     // 'draft-12' or 'v01'
 | |
|                                                           // 'draft-12' is for Let's Encrypt v2 otherwise known as ACME draft 12
 | |
|                                                           // 'v02' is an alias for 'draft-12'
 | |
|                                                           // '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('/', greenlock.middleware());
 | |
| //
 | |
| // Otherwise you should see the test file for usage of this:
 | |
| // greenlock.challenges['http-01'].get(opts.domain, key, val, done)
 | |
| 
 | |
| 
 | |
| 
 | |
| // Check in-memory cache of certificates for the named domain
 | |
| greenlock.check({ domains: [ 'example.com' ] }).then(function (results) {
 | |
|   if (results) {
 | |
|     // we already have certificates
 | |
|     return;
 | |
|   }
 | |
| 
 | |
| 
 | |
|   // Register Certificate manually
 | |
|   greenlock.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 greenlock.middleware() with express,
 | |
|     // manually use greenlock.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.
 | |
| 
 | |
| ```
 | |
| greenlock.register(opts)
 | |
| greenlock.check(opts)
 | |
| ```
 | |
| 
 | |
| ### Helper Functions
 | |
| 
 | |
| We do expose a few helper functions:
 | |
| 
 | |
| -   Greenlock.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`
 | |
| 
 | |
| ### Dangerous Options
 | |
| 
 | |
| By default SNI is made to lowercase and is automatically rejected if it contains invalid characters for a domain.
 | |
| This behavior can be modified:
 | |
| 
 | |
| -   `__dns_allow_dangerous_names` allow SNI names like "Robert'); DROP TABLE Students;"
 | |
| -   `__dns_preserve_case` passes SNI names such as "ExAMpLE.coM" without converting to lower case
 | |
| 
 | |
| ## 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 `v3.0.0` as your initial version - NOT v0.1.0 and NOT v1.0.0 and NOT v2.0.0.
 | |
| This is to indicate that your module is compatible with v3 (v2.7+) 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 [greenlock-store-test](https://git.rootprojects.org/root/greenlock-store-test.js)
 | |
| and [greenlock-store-fs](https://git.rootprojects.org/root/greenlock-store-fs.js)
 | |
| 
 | |
| -   accounts.
 | |
|     -   checkKeypair(opts)
 | |
|     -   check(opts)
 | |
|     -   setKeypair(opts)
 | |
|     -   set(opts)
 | |
| -   certificates.
 | |
|     -   checkKeypair(opts)
 | |
|     -   check(opts)
 | |
|     -   setKeypair(opts)
 | |
|     -   set(opts)
 | |
| 
 | |
| ### challenge implementation
 | |
| 
 | |
| See [greenlock-challenge-test](https://git.rootprojects.org/root/greenlock-challenge-test.js),
 | |
| [acme-http-01-cli](https://git.rootprojects.org/root/acme-http-01-cli.js),
 | |
| and [acme-dns-01-cli](https://git.rootprojects.org/root/acme-dns-01-cli.js)
 | |
| 
 | |
| -   `.set(opts);`
 | |
| -   `.get(opts);`
 | |
| -   `.remove(opts);`
 | |
| 
 | |
| # Change History
 | |
| 
 | |
| -   v2.7
 | |
|     -   API: transitional for v3 API (Promies, async/await)
 | |
|     -   Security: Zero external dependencies
 | |
|     -   Plugins: `greenlock-store-fs` replaces `le-store-certbot` as the default storage plugin
 | |
|     -   Features: Full wildcard support
 | |
|     -   Licensing: Commercial licensing and support plans now available
 | |
| -   v2.6
 | |
|     -   better defaults, fewer explicit options
 | |
|     -   better pre-flight self-tests, explicit domains not required
 | |
| -   v2.5
 | |
|     -   bugfix JWK (update rsa-compat)
 | |
|     -   eliminate all external non-optional dependencies
 | |
| -   v2.4
 | |
|     -   v2.4.3 - add security updates (default true) independent of community updates (default false)
 | |
| -   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
 | |
| 
 | |
| # Commercial Licensing
 | |
| 
 | |
| As the number of businesses using Greenlock commercially has increased, we've become more aware of the need for quick-turnaround support and licenses that allow for local private modifications. Currently we offer LTS support and commercial licensing models for IoT, On-Prem, and Web Hosting. Please [contact us](mailto:support@rootprojects.org?subject=Greenlock%20Commercial%20Support) to learn more.
 | |
| 
 | |
| Our [trademark policy](https://therootcompany.com/legal/#trademark) is pretty much "attribute, but don't confuse". Your users should understand that your product _uses_ Greenlock and not be confused to think that it _is_ Greenlock.
 | |
| 
 | |
| # Legal & Rules of the Road
 | |
| 
 | |
| Greenlock™ is a [trademark](https://rootprojects.org/legal/#trademark) of AJ ONeal
 | |
| 
 | |
| The rule of thumb is "attribute, but don't confuse". For example:
 | |
| 
 | |
| > Built with [Greenlock.js](https://git.rootprojects.org/root/greenlock.js) (a [Root](https://rootprojects.org) project).
 | |
| 
 | |
| Please [contact us](mailto:aj@therootcompany.com) if you have any questions in regards to our trademark,
 | |
| attribution, and/or visible source policies. We want to build great software and a great community.
 | |
| 
 | |
| [Greenlock™](https://git.rootprojects.org/root/greenlock.js) |
 | |
| MPL-2.0 |
 | |
| [Terms of Use](https://therootcompany.com/legal/#terms) |
 | |
| [Privacy Policy](https://therootcompany.com/legal/#privacy)
 |