404 lines
11 KiB
Markdown
404 lines
11 KiB
Markdown
# Migrating Guide
|
|
|
|
Greenlock v4 is the current version.
|
|
|
|
# v3 to v4
|
|
|
|
v4 is a very minor, but breaking, change from v3
|
|
|
|
### `configFile` is replaced with `configDir`
|
|
|
|
The default config file `./greenlock.json` is now `./greenlock.d/config.json`.
|
|
|
|
This was change was mode to eliminate unnecessary configuration that was inadvertantly introduced in v3.
|
|
|
|
### `.greenlockrc` is auto-generated
|
|
|
|
`.greenlockrc` exists for the sake of tooling - so that the CLI, Web API, and your code naturally stay in sync.
|
|
|
|
It looks like this:
|
|
|
|
```json
|
|
{
|
|
"manager": {
|
|
"module": "@greenlock/manager"
|
|
},
|
|
"configDir": "./greenlock.d"
|
|
}
|
|
```
|
|
|
|
If you deploy to a read-only filesystem, it is best that you create the `.greenlockrc` file as part
|
|
of your image and use that rather than including any configuration in your code.
|
|
|
|
# v2 to v4
|
|
|
|
**Greenlock Express** uses Greenlock directly, the same as before.
|
|
|
|
All options described for `Greenlock.create({...})` also apply to the Greenlock Express `init()` callback.
|
|
|
|
# Overview of Major Differences
|
|
|
|
- Reduced API
|
|
- No code in the config
|
|
- (config is completely serializable)
|
|
- Manager callbacks replace `approveDomains`
|
|
- Greenlock Express does more, with less config
|
|
- cluster is supported out-of-the-box
|
|
- high-performance
|
|
- scalable
|
|
- ACME challenges are simplified
|
|
- init
|
|
- zones (dns-01)
|
|
- set
|
|
- get
|
|
- remove
|
|
- Store callbacks are simplified
|
|
- accounts
|
|
- checkKeypairs
|
|
- certificates
|
|
- checkKeypairs
|
|
- check
|
|
- set
|
|
|
|
# Greenlock JavaScript API greatly reduced
|
|
|
|
Whereas before there were many different methods with nuance differences,
|
|
now there's just `create`, `get`, `renew`, and sometimes `add` ().
|
|
|
|
- Greenlock.create({ maintainerEmail, packageAgent, notify })
|
|
- Greenlock.get({ servername, wildname, duplicate, force })
|
|
- (just a convenience wrapper around renew)
|
|
- Greenlock.renew({ subject, altnames, issuedBefore, expiresAfter })
|
|
- (retrieves, issues, renews, all-in-one)
|
|
- _optional_ Greenlock.add({ subject, altnames, subscriberEmail })
|
|
- (partially replaces `approveDomains`)
|
|
|
|
Also, some disambiguation on terms:
|
|
|
|
- `domains` was often ambiguous and confusing, it has been replaced by:
|
|
- `subject` refers to the subject of a certificate - the primary domain
|
|
- `altnames` refers to the domains in the SAN (Subject Alternative Names) section of the certificate
|
|
- `servername` refers to the TLS (SSL) SNI (Server Name Indication) request for a cetificate
|
|
- `wildname` refers to the wildcard version of the servername (ex: `www.example.com => *.example.com`)
|
|
|
|
When you create an instance of Greenlock, you only supply package and maintainer info.
|
|
|
|
All other configuration is A) optional and B) handled by the _Manager_.
|
|
|
|
```js
|
|
'use strict';
|
|
|
|
var pkg = require('./package.json');
|
|
|
|
var Greenlock = require('greenlock');
|
|
var greenlock = Greenlock.create({
|
|
// used for the ACME client User-Agent string as per RFC 8555 and RFC 7231
|
|
packageAgent: pkg.name + '/' + pkg.version,
|
|
|
|
// used as the contact for critical bug and security notices
|
|
// should be the same as pkg.author.email
|
|
maintainerEmail: 'jon@example.com',
|
|
|
|
// used for logging background events and errors
|
|
notify: function(ev, args) {
|
|
if ('error' === ev || 'warning' === ev) {
|
|
console.error(ev, args);
|
|
return;
|
|
}
|
|
console.info(ev, args);
|
|
}
|
|
});
|
|
```
|
|
|
|
By default **no certificates will be issued**. See the _manager_ section.
|
|
|
|
When you want to get a single certificate, you use `get`, which will:
|
|
|
|
- will return null if neither the `servername` or its `wildname` (wildcard) variant can be found
|
|
- retrieve a non-expired certificate, if possible
|
|
- will renew the certificate in the background, if stale
|
|
- will wait for the certificate to be issued if new
|
|
|
|
```js
|
|
greenlock
|
|
.get({ servername: 'www.example.com' })
|
|
.then(function(result) {
|
|
if (!result) {
|
|
// certificate is not on the approved list
|
|
return null;
|
|
}
|
|
|
|
var fullchain = result.pems.cert + '\n' + result.pems.chain + '\n';
|
|
var privkey = result.pems.privkey;
|
|
|
|
return {
|
|
fullchain: fullchain,
|
|
privkey: privkey
|
|
};
|
|
})
|
|
.catch(function(e) {
|
|
// something went wrong in the renew process
|
|
console.error(e);
|
|
});
|
|
```
|
|
|
|
By default **no certificates will be issued**. See the _manager_ section.
|
|
|
|
When you want to renew certificates, _en masse_, you use `renew`, which will:
|
|
|
|
- check all certificates matching the given criteria
|
|
- only renew stale certificates by default
|
|
- return error objects (will NOT throw exception for failed renewals)
|
|
|
|
```js
|
|
greenlock
|
|
.renew({})
|
|
.then(function(results) {
|
|
if (!result.length) {
|
|
// no certificates found
|
|
return null;
|
|
}
|
|
|
|
// [{ site, error }]
|
|
return results;
|
|
})
|
|
.catch(function(e) {
|
|
// an unexpected error, not related to renewal
|
|
console.error(e);
|
|
});
|
|
```
|
|
|
|
Options:
|
|
|
|
| Option | Description |
|
|
| ------------- | -------------------------------------------------------------------------- |
|
|
| `altnames` | only check and renew certs matching these altnames (including wildcards) |
|
|
| `renewBefore` | only check and renew certs marked for renewal before the given date, in ms |
|
|
| `duplicate` | renew certificates regardless of timing |
|
|
| `force` | allow silly things, like tiny `renewOffset`s |
|
|
|
|
By default **no certificates will be issued**. See the _manager_ section.
|
|
|
|
# Greenlock Express Example
|
|
|
|
The options that must be returned from `init()` are the same that are used in `Greenlock.create()`,
|
|
with a few extra that are specific to Greenlock Express:
|
|
|
|
```js
|
|
require('@root/greenlock-express')
|
|
.init(function() {
|
|
// This object will be passed to Greenlock.create()
|
|
|
|
var options = {
|
|
// some options, like cluster, are special to Greenlock Express
|
|
|
|
cluster: false,
|
|
|
|
// The rest are the same as for Greenlock
|
|
|
|
packageAgent: pkg.name + '/' + pkg.version,
|
|
maintainerEmail: 'jon@example.com',
|
|
notify: function(ev, args) {
|
|
console.info(ev, args);
|
|
}
|
|
};
|
|
|
|
return options;
|
|
})
|
|
.serve(function(glx) {
|
|
// will start servers on port 80 and 443
|
|
|
|
glx.serveApp(function(req, res) {
|
|
res.end('Hello, Encrypted World!');
|
|
});
|
|
|
|
// you can get access to the raw server (i.e. for websockets)
|
|
|
|
glx.httpsServer(); // returns raw server object
|
|
});
|
|
```
|
|
|
|
# _Manager_ replaces `approveDomains`
|
|
|
|
`approveDomains` was always a little confusing. Most people didn't need it.
|
|
|
|
Instead, now there is a simple config file that will work for most people,
|
|
as well as a set of callbacks for easy configurability.
|
|
|
|
### Default Manager
|
|
|
|
The default manager is `@greenlock/manager` and the default `configDir` is `./.greenlock.d`.
|
|
|
|
The config file should look something like this:
|
|
|
|
`./greenlock.d/config.json`:
|
|
|
|
```json
|
|
{
|
|
"subscriberEmail": "jon@example.com",
|
|
"agreeToTerms": true,
|
|
"sites": {
|
|
"example.com": {
|
|
"subject": "example.com",
|
|
"altnames": ["example.com", "www.example.com"]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
You can specify a `acme-dns-01-*` or `acme-http-01-*` challenge plugin globally, or per-site.
|
|
|
|
```json
|
|
{
|
|
"subscriberEmail": "jon@example.com",
|
|
"agreeToTerms": true,
|
|
"sites": {
|
|
"example.com": {
|
|
"subject": "example.com",
|
|
"altnames": ["example.com", "www.example.com"],
|
|
"challenges": {
|
|
"dns-01": {
|
|
"module": "acme-dns-01-digitalocean",
|
|
"token": "apikey-xxxxx"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
The same is true with `greenlock-store-*` plugins:
|
|
|
|
```json
|
|
{
|
|
"subscriberEmail": "jon@example.com",
|
|
"agreeToTerms": true,
|
|
"sites": {
|
|
"example.com": {
|
|
"subject": "example.com",
|
|
"altnames": ["example.com", "www.example.com"]
|
|
}
|
|
},
|
|
"store": {
|
|
"module": "greenlock-store-fs",
|
|
"basePath": "~/.config/greenlock"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Customer Manager, the lazy way
|
|
|
|
At the very least you have to implement `get({ servername, wildname })`.
|
|
|
|
```js
|
|
var greenlock = Greenlock.create({
|
|
packageAgent: pkg.name + '/' + pkg.version,
|
|
maintainerEmail: 'jon@example.com',
|
|
notify: notify,
|
|
|
|
packageRoot: __dirname,
|
|
manager: {
|
|
module: './manager.js'
|
|
}
|
|
});
|
|
|
|
function notify(ev, args) {
|
|
if ('error' === ev || 'warning' === ev) {
|
|
console.error(ev, args);
|
|
return;
|
|
}
|
|
console.info(ev, args);
|
|
}
|
|
```
|
|
|
|
In the simplest case you can ignore all incoming options
|
|
and return a single site config in the same format as the config file
|
|
|
|
`./manager.js`:
|
|
|
|
```js
|
|
'use strict';
|
|
|
|
module.exports.create = function() {
|
|
return {
|
|
get: async function({ servername }) {
|
|
// do something to fetch the site
|
|
var site = {
|
|
subject: 'example.com',
|
|
altnames: ['example.com', 'www.example.com']
|
|
};
|
|
|
|
return site;
|
|
}
|
|
};
|
|
};
|
|
```
|
|
|
|
If you want to use wildcards or local domains for a specific domain, you must specify the `dns-01` challenge plugin to use:
|
|
|
|
```js
|
|
'use strict';
|
|
|
|
module.exports.create = function() {
|
|
return {
|
|
get: async function({ servername }) {
|
|
// do something to fetch the site
|
|
var site = {
|
|
subject: 'example.com',
|
|
altnames: ['example.com', 'www.example.com'],
|
|
|
|
// dns-01 challenge
|
|
challenges: {
|
|
'dns-01': {
|
|
module: 'acme-dns-01-namedotcom',
|
|
apikey: 'xxxx'
|
|
}
|
|
}
|
|
};
|
|
|
|
return site;
|
|
}
|
|
};
|
|
};
|
|
```
|
|
|
|
### Customer Manager, Complete
|
|
|
|
See <https://git.rootprojects.org/root/greenlock-manager-test.js#quick-start>
|
|
|
|
# ACME Challenge Plugins
|
|
|
|
The ACME challenge plugins are just a few simple callbacks:
|
|
|
|
- `init`
|
|
- `zones` (dns-01 only)
|
|
- `set`
|
|
- `get`
|
|
- `remove`
|
|
|
|
They are described here:
|
|
|
|
- [dns-01 documentation](https://git.rootprojects.org/root/acme-dns-01-test.js)
|
|
- [http-01 documentation](https://git.rootprojects.org/root/acme-http-01-test.js)
|
|
|
|
# Key and Cert Store Plugins
|
|
|
|
Again, these are just a few simple callbacks:
|
|
|
|
- `certificates.checkKeypair`
|
|
- `certificates.check`
|
|
- `certificates.setKeypair`
|
|
- `certificates.set`
|
|
- `accounts.checkKeypair`
|
|
- `accounts.check` (optional)
|
|
- `accounts.setKeypair`
|
|
- `accounts.set` (optional)
|
|
|
|
The name `check` is used instead of `get` because they only need to return something if it exists. They do not need to fail, nor do they need to generate anything.
|
|
|
|
They are described here:
|
|
|
|
- [greenlock store documentation](https://git.rootprojects.org/root/greenlock-store-test.js)
|
|
|
|
If you are just implenting in-house and are not going to publish a module, you can also do some hack things like this:
|