Compare commits

...

12 Commits

19 changed files with 593 additions and 329 deletions

View File

@ -1,4 +1,36 @@
# Migrating from Greenlock v2 to v3 # 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. **Greenlock Express** uses Greenlock directly, the same as before.
@ -195,11 +227,11 @@ as well as a set of callbacks for easy configurability.
### Default Manager ### Default Manager
The default manager is `greenlock-manager-fs` and the default `configFile` is `~/.config/greenlock/manager.json`. The default manager is `@greenlock/manager` and the default `configDir` is `./.greenlock.d`.
The config file should look something like this: The config file should look something like this:
`~/.config/greenlock/manager.json`: `./greenlock.d/config.json`:
```json ```json
{ {
@ -256,29 +288,20 @@ The same is true with `greenlock-store-*` plugins:
### Customer Manager, the lazy way ### Customer Manager, the lazy way
At the very least you have to implement `find({ servername })`. At the very least you have to implement `get({ servername, wildname })`.
Since this is a very common use case, it's supported out of the box as part of the default manager plugin:
```js ```js
var greenlock = Greenlock.create({ var greenlock = Greenlock.create({
packageAgent: pkg.name + '/' + pkg.version, packageAgent: pkg.name + '/' + pkg.version,
maintainerEmail: 'jon@example.com', maintainerEmail: 'jon@example.com',
notify: notify, notify: notify,
find: find
packageRoot: __dirname,
manager: {
module: './manager.js'
}
}); });
// In the simplest case you can ignore all incoming options
// and return a single site config in the same format as the config file
function find(options) {
var servername = options.servername; // www.example.com
var wildname = options.wildname; // *.example.com
return Promise.resolve([
{ subject: 'example.com', altnames: ['example.com', 'www.example.com'] }
]);
}
function notify(ev, args) { function notify(ev, args) {
if ('error' === ev || 'warning' === ev) { if ('error' === ev || 'warning' === ev) {
console.error(ev, args); console.error(ev, args);
@ -288,102 +311,61 @@ function notify(ev, args) {
} }
``` ```
If you want to use wildcards or local domains, you must specify the `dns-01` challenge plugin to use: In the simplest case you can ignore all incoming options
and return a single site config in the same format as the config file
```js `./manager.js`:
function find(options) {
var subject = options.subject;
// may include wildcard
var altnames = options.altnames;
var wildname = options.wildname; // *.example.com
return Promise.resolve([
{
subject: 'example.com',
altnames: ['example.com', 'www.example.com'],
challenges: {
'dns-01': { module: 'acme-dns-01-namedotcom', apikey: 'xxxx' }
}
}
]);
}
```
### Customer Manager, complete
To use a fully custom manager, you give the npm package name, or absolute path to the file to load
```js
Greenlock.create({
// Greenlock Options
maintainerEmail: 'jon@example.com',
packageAgent: 'my-package/v2.1.1',
notify: notify,
// file path or npm package name
manager: '/path/to/manager.js',
// options that get passed to the manager
myFooOption: 'whatever'
});
```
The manager itself is, again relatively simple:
- find(options)
- set(siteConfig)
- remove(options)
- defaults(globalOptions) (as setter)
- defaults() => globalOptions (as getter)
`/path/to/manager.js`:
```js ```js
'use strict'; 'use strict';
module.exports.create = function() { module.exports.create = function() {
var manager = {}; return {
get: async function({ servername }) {
manager.find = async function({ subject, altnames, renewBefore }) { // do something to fetch the site
if (subject) { var site = {
return getSiteConfigBySubject(subject); subject: 'example.com',
} altnames: ['example.com', 'www.example.com']
if (altnames) {
// may include wildcards
return getSiteConfigByAnyAltname(altnames);
}
if (renewBefore) {
return getSiteConfigsWhereRenewAtIsLessThan(renewBefore);
}
return [];
}; };
manage.set = function(opts) { return site;
// this is called by greenlock.add({ subject, altnames })
// it's also called by greenlock._update({ subject, renewAt })
return mergSiteConfig(subject, opts);
};
manage.remove = function({ subject, altname }) {
if (subject) {
return removeSiteConfig(subject);
} }
return removeFromSiteConfigAndResetRenewAtToZero(altname);
};
// set the global config
manage.defaults = function(options) {
if (!options) {
return getGlobalConfig();
}
return mergeGlobalConfig(options);
}; };
}; };
``` ```
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 # ACME Challenge Plugins
The ACME challenge plugins are just a few simple callbacks: The ACME challenge plugins are just a few simple callbacks:
@ -419,99 +401,3 @@ They are described here:
- [greenlock store documentation](https://git.rootprojects.org/root/greenlock-store-test.js) - [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: If you are just implenting in-house and are not going to publish a module, you can also do some hack things like this:
### Custome Store, The hacky / lazy way
`/path/to/project/my-hacky-store.js`:
```js
'use strict';
module.exports.create = function(options) {
// ex: /path/to/account.ecdsa.jwk.json
var accountJwk = require(options.accountJwkPath);
// ex: /path/to/privkey.rsa.pem
var serverPem = fs.readFileSync(options.serverPemPath, 'ascii');
var accounts = {};
var certificates = {};
var store = { accounts, certificates };
// bare essential account callbacks
accounts.checkKeypair = function() {
// ignore all options and just return a single, global keypair
return Promise.resolve({
privateKeyJwk: accountJwk
});
};
accounts.setKeypair = function() {
// this will never get called if checkKeypair always returns
return Promise.resolve({});
};
// bare essential cert and key callbacks
certificates.checkKeypair = function() {
// ignore all options and just return a global server keypair
return {
privateKeyPem: serverPem
};
};
certificates.setKeypair = function() {
// never gets called if checkKeypair always returns an existing key
return Promise.resolve(null);
};
certificates.check = function(args) {
var subject = args.subject;
// make a database call or whatever to get a certificate
return goGetCertBySubject(subject).then(function() {
return {
pems: {
chain: '<PEM>',
cert: '<PEM>'
}
};
});
};
certificates.set = function(args) {
var subject = args.subject;
var cert = args.pems.cert;
var chain = args.pems.chain;
// make a database call or whatever to get a certificate
return goSaveCert({
subject,
cert,
chain
});
};
};
```
### Using the hacky / lazy store plugin
That sort of implementation won't pass the test suite, but it'll work just fine a use case where you only have one subscriber email (most of the time),
you only have one server key (not recommended, but works), and you only really want to worry about storing cetificates.
Then you could assign it as the default for all of your sites:
```json
{
"subscriberEmail": "jon@example.com",
"agreeToTerms": true,
"sites": {
"example.com": {
"subject": "example.com",
"altnames": ["example.com", "www.example.com"]
}
},
"store": {
"module": "/path/to/project/my-hacky-store.js",
"accountJwkPath": "/path/to/account.ecdsa.jwk.json",
"serverPemPath": "/path/to/privkey.rsa.pem"
}
}
```

View File

@ -1,12 +1,10 @@
# New Documentation &amp; [v2/v3 Migration Guide](https://git.rootprojects.org/root/greenlock.js/src/branch/v3/MIGRATION_GUIDE_V2_V3.md) # New Documentation &amp; [v4 Migration Guide](https://git.rootprojects.org/root/greenlock.js/src/branch/master/MIGRATION_GUIDE.md)
Greenlock v3 was just released from private beta **today** (Nov 1st, 2019).
We're still working on the full documentation for this new version, We're still working on the full documentation for this new version,
so please be patient. so please be patient.
To start, check out the To start, check out the
[Migration Guide](https://git.rootprojects.org/root/greenlock.js/src/branch/v3/MIGRATION_GUIDE_V2_V3.md). [Migration Guide](https://git.rootprojects.org/root/greenlock.js/src/branch/master/MIGRATION_GUIDE.md).
!["Greenlock Logo"](https://git.rootprojects.org/root/greenlock.js/raw/branch/master/logo/greenlock-1063x250.png 'Greenlock lock logo and work mark') !["Greenlock Logo"](https://git.rootprojects.org/root/greenlock.js/raw/branch/master/logo/greenlock-1063x250.png 'Greenlock lock logo and work mark')
@ -85,12 +83,10 @@ Certificates are renewed every 45 days by default, and renewal checks will happe
var pkg = require('./package.json'); var pkg = require('./package.json');
var Greenlock = require('greenlock'); var Greenlock = require('greenlock');
var greenlock = Greenlock.create({ var greenlock = Greenlock.create({
configDir: './greenlock.d/config.json',
packageAgent: pkg.name + '/' + pkg.version, packageAgent: pkg.name + '/' + pkg.version,
maintainerEmail: pkg.author, maintainerEmail: pkg.author,
staging: true, staging: true,
manager: require('greenlock-manager-fs').create({
configFile: '~/.config/greenlock/manager.json'
}),
notify: function(event, details) { notify: function(event, details) {
if ('error' === event) { if ('error' === event) {
// `details` is an error object in this case // `details` is an error object in this case
@ -171,7 +167,7 @@ greenlock
--> -->
<details> <details>
<summary>Greenlock.create({ packageAgent, maintainerEmail, staging })</summary> <summary>Greenlock.create({ configDir, packageAgent, maintainerEmail, staging })</summary>
## Greenlock.create() ## Greenlock.create()
@ -181,12 +177,15 @@ Creates an instance of greenlock with _environment_-level values.
var pkg = require('./package.json'); var pkg = require('./package.json');
var gl = Greenlock.create({ var gl = Greenlock.create({
configDir: './greenlock.d/config.json',
// Staging for testing environments // Staging for testing environments
staging: true, staging: true,
// This should be the contact who receives critical bug and security notifications // This should be the contact who receives critical bug and security notifications
// Optionally, you may receive other (very few) updates, such as important new features // Optionally, you may receive other (very few) updates, such as important new features
maintainerEmail: 'jon@example.com', maintainerEmail: 'jon@example.com',
// for an RFC 8555 / RFC 7231 ACME client user agent // for an RFC 8555 / RFC 7231 ACME client user agent
packageAgent: pkg.name + '/' pkg.version packageAgent: pkg.name + '/' pkg.version
}); });
@ -194,6 +193,7 @@ var gl = Greenlock.create({
| Parameter | Description | | Parameter | Description |
| --------------- | ------------------------------------------------------------------------------------ | | --------------- | ------------------------------------------------------------------------------------ |
| configDir | the directory to use for file-based plugins |
| maintainerEmail | the developer contact for critical bug and security notifications | | maintainerEmail | the developer contact for critical bug and security notifications |
| packageAgent | if you publish your package for others to use, `require('./package.json').name` here | | packageAgent | if you publish your package for others to use, `require('./package.json').name` here |
| staging | use the Let's Encrypt staging URL instead of the production URL | | staging | use the Let's Encrypt staging URL instead of the production URL |

View File

@ -8,7 +8,7 @@ var cli = require('./lib/cli.js');
var Flags = require('./lib/flags.js'); var Flags = require('./lib/flags.js');
Flags.init().then(function({ flagOptions, rc, greenlock, mconf }) { Flags.init().then(function({ flagOptions, greenlock, mconf }) {
var myFlags = {}; var myFlags = {};
[ [
'subject', 'subject',
@ -34,11 +34,11 @@ Flags.init().then(function({ flagOptions, rc, greenlock, mconf }) {
cli.parse(myFlags); cli.parse(myFlags);
cli.main(function(argList, flags) { cli.main(function(argList, flags) {
Flags.mangleFlags(flags, mconf); Flags.mangleFlags(flags, mconf);
main(argList, flags, rc, greenlock); main(argList, flags, greenlock);
}, args); }, args);
}); });
async function main(_, flags, rc, greenlock) { async function main(_, flags, greenlock) {
if (!flags.subject || !flags.altnames) { if (!flags.subject || !flags.altnames) {
console.error( console.error(
'--subject and --altnames must be provided and should be valid domains' '--subject and --altnames must be provided and should be valid domains'

View File

@ -8,7 +8,7 @@ var cli = require('./lib/cli.js');
var Flags = require('./lib/flags.js'); var Flags = require('./lib/flags.js');
Flags.init().then(function({ flagOptions, rc, greenlock, mconf }) { Flags.init().then(function({ flagOptions, greenlock, mconf }) {
var myFlags = {}; var myFlags = {};
['all', 'subject', 'servername' /*, 'servernames', 'altnames'*/].forEach( ['all', 'subject', 'servername' /*, 'servernames', 'altnames'*/].forEach(
function(k) { function(k) {
@ -19,11 +19,11 @@ Flags.init().then(function({ flagOptions, rc, greenlock, mconf }) {
cli.parse(myFlags); cli.parse(myFlags);
cli.main(function(argList, flags) { cli.main(function(argList, flags) {
Flags.mangleFlags(flags, mconf); Flags.mangleFlags(flags, mconf);
main(argList, flags, rc, greenlock); main(argList, flags, greenlock);
}, args); }, args);
}); });
async function main(_, flags, rc, greenlock) { async function main(_, flags, greenlock) {
var servernames = [flags.subject] var servernames = [flags.subject]
.concat([flags.servername]) .concat([flags.servername])
//.concat(flags.servernames) //.concat(flags.servernames)

View File

@ -10,7 +10,6 @@ var Flags = require('./lib/flags.js');
Flags.init({ forceSave: true }).then(function({ Flags.init({ forceSave: true }).then(function({
flagOptions, flagOptions,
rc,
greenlock, greenlock,
mconf mconf
}) { }) {
@ -38,11 +37,11 @@ Flags.init({ forceSave: true }).then(function({
cli.parse(myFlags); cli.parse(myFlags);
cli.main(function(argList, flags) { cli.main(function(argList, flags) {
Flags.mangleFlags(flags, mconf, null, { forceSave: true }); Flags.mangleFlags(flags, mconf, null, { forceSave: true });
main(argList, flags, rc, greenlock); main(argList, flags, greenlock);
}, args); }, args);
}); });
async function main(_, flags, rc, greenlock) { async function main(_, flags, greenlock) {
greenlock.manager greenlock.manager
.defaults(flags) .defaults(flags)
.catch(function(err) { .catch(function(err) {

View File

@ -3,24 +3,25 @@
var P = require('../plugins.js'); var P = require('../plugins.js');
var args = process.argv.slice(3); var args = process.argv.slice(3);
var cli = require('./lib/cli.js'); var cli = require('./lib/cli.js');
//var path = require('path'); var Greenlock = require('../');
//var pkgpath = path.join(__dirname, '..', 'package.json');
//var pkgpath = path.join(process.cwd(), 'package.json');
var Flags = require('./lib/flags.js'); var Flags = require('./lib/flags.js');
var flagOptions = Flags.flags(); var flagOptions = Flags.flags();
var myFlags = {}; var myFlags = {};
['maintainer-email', 'cluster', 'manager', 'manager-xxxx'].forEach(function(k) { [
'config-dir',
'maintainer-email',
'cluster',
'manager',
'manager-xxxx'
].forEach(function(k) {
myFlags[k] = flagOptions[k]; myFlags[k] = flagOptions[k];
}); });
cli.parse(myFlags); cli.parse(myFlags);
cli.main(async function(argList, flags) { cli.main(async function(argList, flags) {
var path = require('path'); var pkgRoot = process.cwd();
var pkgpath = path.join(process.cwd(), 'package.json');
var pkgdir = path.dirname(pkgpath);
//var rcpath = path.join(pkgpath, '.greenlockrc');
var manager = flags.manager; var manager = flags.manager;
if (['fs', 'cloud'].includes(manager)) { if (['fs', 'cloud'].includes(manager)) {
@ -32,7 +33,7 @@ cli.main(async function(argList, flags) {
flags.manager = flags.managerOpts; flags.manager = flags.managerOpts;
delete flags.managerOpts; delete flags.managerOpts;
flags.manager.manager = manager; flags.manager.module = manager;
try { try {
P._loadSync(manager); P._loadSync(manager);
@ -49,12 +50,18 @@ cli.main(async function(argList, flags) {
} }
} }
var GreenlockRc = require('../greenlockrc.js'); var greenlock = Greenlock.create({
//var rc = await GreenlockRc(pkgpath, manager, flags.manager); packageRoot: pkgRoot,
await GreenlockRc(pkgpath, manager, flags.manager); manager: flags.manager,
writeGreenlockJs(pkgdir, flags); configDir: flags.configDir,
writeServerJs(pkgdir, flags); maintainerEmail: flags.maintainerEmail,
writeAppJs(pkgdir); _mustPackage: true
});
await greenlock.manager.defaults();
//writeGreenlockJs(pkgdir, flags);
writeServerJs(pkgRoot, flags);
writeAppJs(pkgRoot);
/* /*
rc._bin_mode = true; rc._bin_mode = true;
@ -66,6 +73,7 @@ cli.main(async function(argList, flags) {
*/ */
}, args); }, args);
/*
function writeGreenlockJs(pkgdir, flags) { function writeGreenlockJs(pkgdir, flags) {
var greenlockJs = 'greenlock.js'; var greenlockJs = 'greenlock.js';
var fs = require('fs'); var fs = require('fs');
@ -92,15 +100,13 @@ function writeGreenlockJs(pkgdir, flags) {
fs.writeFileSync(path.join(pkgdir, greenlockJs), tmpl); fs.writeFileSync(path.join(pkgdir, greenlockJs), tmpl);
console.info("created '%s'", greenlockJs); console.info("created '%s'", greenlockJs);
} }
*/
function writeServerJs(pkgdir, flags) { function writeServerJs(pkgdir, flags) {
var serverJs = 'server.js'; var serverJs = 'server.js';
var fs = require('fs'); var fs = require('fs');
var path = require('path'); var path = require('path');
var tmpl = fs.readFileSync( var tmpl;
path.join(__dirname, 'tmpl/server.tmpl.js'),
'utf8'
);
try { try {
fs.accessSync(path.join(pkgdir, serverJs)); fs.accessSync(path.join(pkgdir, serverJs));
@ -111,7 +117,22 @@ function writeServerJs(pkgdir, flags) {
} }
if (flags.cluster) { if (flags.cluster) {
tmpl = fs.readFileSync(
path.join(__dirname, 'tmpl/cluster.tmpl.js'),
'utf8'
);
tmpl = tmpl.replace(/cluster: false/g, 'cluster: true'); tmpl = tmpl.replace(/cluster: false/g, 'cluster: true');
} else {
tmpl = fs.readFileSync(
path.join(__dirname, 'tmpl/server.tmpl.js'),
'utf8'
);
}
if (flags.maintainerEmail) {
tmpl = tmpl
.replace(/pkg.author/g, JSON.stringify(flags.maintainerEmail))
.replace(/\/\/maintainerEmail/g, 'maintainerEmail');
} }
fs.writeFileSync(path.join(pkgdir, serverJs), tmpl); fs.writeFileSync(path.join(pkgdir, serverJs), tmpl);

View File

@ -2,10 +2,9 @@
var Flags = module.exports; var Flags = module.exports;
var path = require('path'); //var path = require('path');
//var pkgpath = path.join(__dirname, '..', 'package.json'); var pkgRoot = process.cwd();
var pkgpath = path.join(process.cwd(), 'package.json'); //var Init = require('../../lib/init.js');
var GreenlockRc = require('../../greenlockrc.js');
// These are ALL options // These are ALL options
// The individual CLI files each select a subset of them // The individual CLI files each select a subset of them
@ -68,6 +67,11 @@ Flags.flags = function(mconf, myOpts) {
"the email address of the Let's Encrypt or ACME Account subscriber (not necessarily the domain owner)", "the email address of the Let's Encrypt or ACME Account subscriber (not necessarily the domain owner)",
'string' 'string'
], ],
'config-dir': [
false,
'the directory in which config.json and other config and storage files should be written',
'string'
],
'maintainer-email': [ 'maintainer-email': [
false, false,
'the maintainance contact for security and critical bug notices', 'the maintainance contact for security and critical bug notices',
@ -100,7 +104,7 @@ Flags.flags = function(mconf, myOpts) {
false, false,
'the module name or file path of the manager module to use', 'the module name or file path of the manager module to use',
'string', 'string',
'greenlock-manager-fs' '@greenlock/manager'
], ],
'manager-xxxx': [ 'manager-xxxx': [
false, false,
@ -174,17 +178,19 @@ Flags.flags = function(mconf, myOpts) {
}; };
Flags.init = async function(myOpts) { Flags.init = async function(myOpts) {
var rc = await GreenlockRc(pkgpath);
rc._bin_mode = true;
var Greenlock = require('../../'); var Greenlock = require('../../');
// this is a copy, so it's safe to modify // this is a copy, so it's safe to modify
rc.packageRoot = path.dirname(pkgpath); var greenlock = Greenlock.create({
var greenlock = Greenlock.create(rc); packageRoot: pkgRoot,
_mustPackage: true,
_init: true,
_bin_mode: true
});
var mconf = await greenlock.manager.defaults(); var mconf = await greenlock.manager.defaults();
var flagOptions = Flags.flags(mconf, myOpts); var flagOptions = Flags.flags(mconf, myOpts);
return { return {
flagOptions, flagOptions,
rc,
greenlock, greenlock,
mconf mconf
}; };

View File

@ -8,7 +8,7 @@ var cli = require('./lib/cli.js');
var Flags = require('./lib/flags.js'); var Flags = require('./lib/flags.js');
Flags.init().then(function({ flagOptions, rc, greenlock, mconf }) { Flags.init().then(function({ flagOptions, greenlock, mconf }) {
var myFlags = {}; var myFlags = {};
['subject'].forEach(function(k) { ['subject'].forEach(function(k) {
myFlags[k] = flagOptions[k]; myFlags[k] = flagOptions[k];
@ -17,11 +17,11 @@ Flags.init().then(function({ flagOptions, rc, greenlock, mconf }) {
cli.parse(myFlags); cli.parse(myFlags);
cli.main(function(argList, flags) { cli.main(function(argList, flags) {
Flags.mangleFlags(flags, mconf); Flags.mangleFlags(flags, mconf);
main(argList, flags, rc, greenlock); main(argList, flags, greenlock);
}, args); }, args);
}); });
async function main(_, flags, rc, greenlock) { async function main(_, flags, greenlock) {
if (!flags.subject) { if (!flags.subject) {
console.error('--subject must be provided as a valid domain'); console.error('--subject must be provided as a valid domain');
process.exit(1); process.exit(1);

30
bin/tmpl/cluster.tmpl.js Normal file
View File

@ -0,0 +1,30 @@
'use strict';
require('greenlock-express')
.init(function() {
// var pkg = require('./package.json');
return {
// where to find .greenlockrc and set default paths
packageRoot: __dirname,
// name & version for ACME client user agent
//packageAgent: pkg.name + '/' + pkg.version,
// contact for security and critical bug notices
//maintainerEmail: pkg.author,
// where to look for configuration
configDir: './greenlock.d',
// whether or not to run at cloudscale
cluster: true
};
})
.ready(function(glx) {
var app = require('./app.js');
// Serves on 80 and 443
// Get's SSL certificates magically!
glx.serveApp(app);
});

View File

@ -6,7 +6,7 @@ module.exports = require('@root/greenlock').create({
packageAgent: pkg.name + '/' + pkg.version, packageAgent: pkg.name + '/' + pkg.version,
// contact for security and critical bug notices // contact for security and critical bug notices
maintainerEmail: pkg.author, //maintainerEmail: pkg.author,
// where to find .greenlockrc and set default paths // where to find .greenlockrc and set default paths
packageRoot: __dirname packageRoot: __dirname

View File

@ -1,18 +1,20 @@
'use strict'; 'use strict';
var app = require('./app.js');
require('greenlock-express') require('greenlock-express')
.init(function() { .init({
return {
packageRoot: __dirname, packageRoot: __dirname,
// contact for security and critical bug notices
//maintainerEmail: pkg.author,
// where to look for configuration
configDir: './greenlock.d',
// whether or not to run at cloudscale // whether or not to run at cloudscale
cluster: false cluster: false
};
}) })
.ready(function(glx) {
var app = require('./app.js');
// Serves on 80 and 443 // Serves on 80 and 443
// Get's SSL certificates magically! // Get's SSL certificates magically!
glx.serveApp(app); .serve(app);
});

View File

@ -4,7 +4,7 @@ var args = process.argv.slice(3);
var cli = require('./lib/cli.js'); var cli = require('./lib/cli.js');
var Flags = require('./lib/flags.js'); var Flags = require('./lib/flags.js');
Flags.init().then(function({ flagOptions, rc, greenlock, mconf }) { Flags.init().then(function({ flagOptions, greenlock, mconf }) {
var myFlags = {}; var myFlags = {};
[ [
'subject', 'subject',
@ -31,11 +31,11 @@ Flags.init().then(function({ flagOptions, rc, greenlock, mconf }) {
cli.main(async function(argList, flags) { cli.main(async function(argList, flags) {
var sconf = await greenlock._config({ servername: flags.subject }); var sconf = await greenlock._config({ servername: flags.subject });
Flags.mangleFlags(flags, mconf, sconf); Flags.mangleFlags(flags, mconf, sconf);
main(argList, flags, rc, greenlock); main(argList, flags, greenlock);
}, args); }, args);
}); });
async function main(_, flags, rc, greenlock) { async function main(_, flags, greenlock) {
if (!flags.subject) { if (!flags.subject) {
console.error('--subject must be provided as a valid domain'); console.error('--subject must be provided as a valid domain');
process.exit(1); process.exit(1);

View File

@ -5,6 +5,7 @@ var pkg = require('./package.json');
var ACME = require('@root/acme'); var ACME = require('@root/acme');
var Greenlock = module.exports; var Greenlock = module.exports;
var request = require('@root/request'); var request = require('@root/request');
var process = require('process');
var G = Greenlock; var G = Greenlock;
var U = require('./utils.js'); var U = require('./utils.js');
@ -18,7 +19,7 @@ var ChWrapper = require('./lib/challenges-wrapper.js');
var MngWrapper = require('./lib/manager-wrapper.js'); var MngWrapper = require('./lib/manager-wrapper.js');
var UserEvents = require('./user-events.js'); var UserEvents = require('./user-events.js');
var GreenlockRc = require('./greenlockrc.js'); var Init = require('./lib/init.js');
var caches = {}; var caches = {};
@ -48,27 +49,27 @@ G.create = function(gconf) {
}); });
} }
if (!gconf.packageRoot) {
gconf.packageRoot = process.cwd();
console.warn(
'`packageRoot` not defined, trying ' + gconf.packageRoot
);
}
if ('function' === typeof gconf.notify) { if ('function' === typeof gconf.notify) {
gdefaults.notify = gconf.notify; gdefaults.notify = gconf.notify;
} else { } else {
gdefaults.notify = _notify; gdefaults.notify = _notify;
} }
var rc = GreenlockRc.resolve(gconf); gconf = Init._init(gconf);
gconf = Object.assign(rc, gconf);
// OK: /path/to/blah // OK: /path/to/blah
// OK: npm-name-blah // OK: npm-name-blah
// NOT OK: ./rel/path/to/blah // NOT OK: ./rel/path/to/blah
if ('.' === (gconf.manager || '')[0]) { // Error: .blah
gconf.manager = gconf.packageRoot + '/' + gconf.manager; if ('.' === (gconf.manager.module || '')[0]) {
if (!gconf.packageRoot) {
gconf.packageRoot = process.cwd();
console.warn(
'`packageRoot` not defined, trying ' + gconf.packageRoot
);
}
gconf.manager.module =
gconf.packageRoot + '/' + gconf.manager.module.slice(2);
} }
// Wraps each of the following with appropriate error checking // Wraps each of the following with appropriate error checking
@ -90,7 +91,7 @@ G.create = function(gconf) {
// greenlock.challenges.get // greenlock.challenges.get
ChWrapper.wrap(greenlock); ChWrapper.wrap(greenlock);
DIR._getDefaultDirectoryUrl('', gconf.staging); DIR._getDefaultDirectoryUrl('', gconf.staging, '');
if (gconf.directoryUrl) { if (gconf.directoryUrl) {
gdefaults.directoryUrl = gconf.directoryUrl; gdefaults.directoryUrl = gconf.directoryUrl;
} }
@ -370,7 +371,7 @@ G.create = function(gconf) {
return renewedOrFailed; return renewedOrFailed;
}; };
greenlock._acme = async function(mconf, args) { greenlock._acme = async function(mconf, args, dirUrl) {
var packageAgent = gconf.packageAgent || ''; var packageAgent = gconf.packageAgent || '';
// because Greenlock_Express/v3.x Greenlock/v3 is redundant // because Greenlock_Express/v3.x Greenlock/v3 is redundant
if (!/greenlock/i.test(packageAgent)) { if (!/greenlock/i.test(packageAgent)) {
@ -383,10 +384,6 @@ G.create = function(gconf) {
debug: greenlock._defaults.debug || args.debug debug: greenlock._defaults.debug || args.debug
}); });
var dirUrl = DIR._getDirectoryUrl(
args.directoryUrl || mconf.directoryUrl
);
var dir = caches[dirUrl]; var dir = caches[dirUrl];
// don't cache more than an hour // don't cache more than an hour
if (dir && Date.now() - dir.ts < 1 * 60 * 60 * 1000) { if (dir && Date.now() - dir.ts < 1 * 60 * 60 * 1000) {
@ -415,19 +412,35 @@ G.create = function(gconf) {
}; };
greenlock._order = async function(mconf, siteConf) { greenlock._order = async function(mconf, siteConf) {
// packageAgent, maintainerEmail // packageAgent, maintainerEmail
var acme = await greenlock._acme(mconf, siteConf);
var dirUrl = DIR._getDirectoryUrl(
siteConf.directoryUrl || mconf.directoryUrl,
siteConf.subject
);
var acme = await greenlock._acme(mconf, siteConf, dirUrl);
var storeConf = siteConf.store || mconf.store; var storeConf = siteConf.store || mconf.store;
storeConf = JSON.parse(JSON.stringify(storeConf)); storeConf = JSON.parse(JSON.stringify(storeConf));
storeConf.packageRoot = gconf.packageRoot; storeConf.packageRoot = gconf.packageRoot;
var path = require('path');
if (!storeConf.basePath) { if (!storeConf.basePath) {
storeConf.basePath = 'greenlock'; storeConf.basePath = gconf.configDir;
} }
storeConf.basePath = path.resolve(
gconf.packageRoot || process.cwd(), if ('.' === (storeConf.basePath || '')[0]) {
if (!gconf.packageRoot) {
gconf.packageRoot = process.cwd();
console.warn(
'`packageRoot` not defined, trying ' + gconf.packageRoot
);
}
storeConf.basePath = require('path').resolve(
gconf.packageRoot || '',
storeConf.basePath storeConf.basePath
); );
}
storeConf.directoryUrl = dirUrl;
var store = await P._loadStore(storeConf); var store = await P._loadStore(storeConf);
var account = await A._getOrCreate( var account = await A._getOrCreate(
greenlock, greenlock,
@ -508,6 +521,7 @@ function mergeDefaults(MCONF, gconf) {
} }
} }
/*
if ('greenlock-store-fs' === MCONF.store.module && !MCONF.store.basePath) { if ('greenlock-store-fs' === MCONF.store.module && !MCONF.store.basePath) {
//homedir = require('os').homedir(); //homedir = require('os').homedir();
if (gconf.configFile) { if (gconf.configFile) {
@ -516,6 +530,7 @@ function mergeDefaults(MCONF, gconf) {
MCONF.store.basePath = './greenlock.d'; MCONF.store.basePath = './greenlock.d';
} }
} }
*/
// just to test that it loads // just to test that it loads
P._loadSync(MCONF.store.module); P._loadSync(MCONF.store.module);
@ -561,8 +576,13 @@ function mergeDefaults(MCONF, gconf) {
console.info('[default] renewStagger: ' + MCONF.renewStagger); console.info('[default] renewStagger: ' + MCONF.renewStagger);
} }
var vers = process.versions.node.split('.');
var defaultKeyType = 'EC-P256';
if (vers[0] < 10 || (vers[0] === '10' && vers[1] < '12')) {
defaultKeyType = 'RSA-2048';
}
if (!MCONF.accountKeyType) { if (!MCONF.accountKeyType) {
MCONF.accountKeyType = gconf.accountKeyType || 'EC-P256'; MCONF.accountKeyType = gconf.accountKeyType || defaultKeyType;
console.info('[default] accountKeyType: ' + MCONF.accountKeyType); console.info('[default] accountKeyType: ' + MCONF.accountKeyType);
} }
if (!MCONF.serverKeyType) { if (!MCONF.serverKeyType) {
@ -570,10 +590,10 @@ function mergeDefaults(MCONF, gconf) {
console.info('[default] serverKeyType: ' + MCONF.serverKeyType); console.info('[default] serverKeyType: ' + MCONF.serverKeyType);
} }
if (false !== MCONF.subscriberEmail) { if (!MCONF.subscriberEmail && false !== MCONF.subscriberEmail) {
MCONF.subscriberEmail = MCONF.subscriberEmail =
gconf.subscriberEmail || gconf.maintainerEmail || undefined; gconf.subscriberEmail || gconf.maintainerEmail || undefined;
MCONF.subscriberEmail = gconf.agreeToTerms || undefined; MCONF.agreeToTerms = gconf.agreeToTerms || undefined;
console.info(''); console.info('');
console.info('[default] subscriberEmail: ' + MCONF.subscriberEmail); console.info('[default] subscriberEmail: ' + MCONF.subscriberEmail);
console.info( console.info(
@ -582,6 +602,7 @@ function mergeDefaults(MCONF, gconf) {
gconf.agreeToTerms || gconf.agreeToTerms ||
'(show notice on use)') '(show notice on use)')
); );
console.info('');
} }
} }

View File

@ -1,9 +1,9 @@
var DIR = module.exports; var DIR = module.exports;
// This will ALWAYS print out a notice if the URL is clearly a staging URL // This will ALWAYS print out a notice if the URL is clearly a staging URL
DIR._getDirectoryUrl = function(dirUrl) { DIR._getDirectoryUrl = function(dirUrl, domain) {
var liveUrl = 'https://acme-v02.api.letsencrypt.org/directory'; var liveUrl = 'https://acme-v02.api.letsencrypt.org/directory';
dirUrl = DIR._getDefaultDirectoryUrl(dirUrl); dirUrl = DIR._getDefaultDirectoryUrl(dirUrl, '', domain);
if (!dirUrl) { if (!dirUrl) {
dirUrl = liveUrl; dirUrl = liveUrl;
// This will print out a notice (just once) if no directoryUrl has been supplied // This will print out a notice (just once) if no directoryUrl has been supplied
@ -16,7 +16,7 @@ DIR._getDirectoryUrl = function(dirUrl) {
}; };
// Handle staging URLs, pebble test server, etc // Handle staging URLs, pebble test server, etc
DIR._getDefaultDirectoryUrl = function(dirUrl, staging) { DIR._getDefaultDirectoryUrl = function(dirUrl, staging, domain) {
var stagingUrl = 'https://acme-staging-v02.api.letsencrypt.org/directory'; var stagingUrl = 'https://acme-staging-v02.api.letsencrypt.org/directory';
var stagingRe = /(^http:|staging|^127\.0\.|^::|localhost)/; var stagingRe = /(^http:|staging|^127\.0\.|^::|localhost)/;
var env = ''; var env = '';
@ -36,8 +36,7 @@ DIR._getDefaultDirectoryUrl = function(dirUrl, staging) {
dirUrl = stagingUrl; dirUrl = stagingUrl;
} }
console.info('[staging] ACME Staging Directory URL:', dirUrl, env); console.info('[staging] ACME Staging Directory URL:', dirUrl, env);
console.warn(''); console.warn('FAKE CERTIFICATES (for testing) only', env, domain);
console.warn('FAKE CERTIFICATES (for testing) only', env);
console.warn(''); console.warn('');
} }

192
lib/init.js Normal file
View File

@ -0,0 +1,192 @@
'use strict';
var Init = module.exports;
var fs = require('fs');
var path = require('path');
//var promisify = require("util").promisify;
Init._init = function(opts) {
//var Rc = require("@root/greenlock/rc");
var Rc = require('./rc.js');
var pkgText;
var pkgErr;
var msgErr;
//var emailErr;
var realPkg;
var userPkg;
var myPkg = {};
// we want to be SUPER transparent that we're reading from package.json
// we don't want anything unexpected
var implicitConfig = [];
var rc;
if (opts.packageRoot) {
try {
pkgText = fs.readFileSync(
path.resolve(opts.packageRoot, 'package.json'),
'utf8'
);
opts._hasPackage = true;
} catch (e) {
pkgErr = e;
if (opts._mustPackage) {
console.error(
'Should be run from package root (the same directory as `package.json`)'
);
process.exit(1);
return;
}
console.warn(
'`packageRoot` should be the root of the package (probably `__dirname`)'
);
}
}
if (pkgText) {
try {
realPkg = JSON.parse(pkgText);
} catch (e) {
pkgErr = e;
}
}
userPkg = opts.package;
if (realPkg || userPkg) {
userPkg = userPkg || {};
realPkg = realPkg || {};
// build package agent
if (!opts.packageAgent) {
// name
myPkg.name = userPkg.name;
if (!myPkg.name) {
myPkg.name = realPkg.name;
implicitConfig.push('name');
}
// version
myPkg.version = userPkg.version;
if (!myPkg.version) {
myPkg.version = realPkg.version;
implicitConfig.push('version');
}
if (myPkg.name && myPkg.version) {
opts.packageAgent = myPkg.name + '/' + myPkg.version;
}
}
// build author
myPkg.author = opts.maintainerEmail;
if (!myPkg.author) {
myPkg.author =
(userPkg.author && userPkg.author.email) || userPkg.author;
}
if (!myPkg.author) {
implicitConfig.push('author');
myPkg.author =
(realPkg.author && realPkg.author.email) || realPkg.author;
}
if (!opts._init) {
opts.maintainerEmail = myPkg.author;
}
}
if (!opts.packageAgent) {
msgErr =
'missing `packageAgent` and also failed to read `name` and/or `version` from `package.json`';
if (pkgErr) {
msgErr += ': ' + pkgErr.message;
}
throw new Error(msgErr);
}
if (!opts._init) {
opts.maintainerEmail = parseMaintainer(opts.maintainerEmail);
if (!opts.maintainerEmail) {
msgErr =
'missing or malformed `maintainerEmail` (or `author` from `package.json`), which is used as the contact for support notices';
throw new Error(msgErr);
}
}
if (opts.packageRoot) {
// Place the rc file in the packageroot
rc = Rc._initSync(opts.packageRoot, opts.manager, opts.configDir);
opts.configDir = rc.configDir;
opts.manager = rc.manager;
}
if (!opts.configDir && !opts.manager) {
throw new Error(
'missing `packageRoot` and `configDir`, but no `manager` was supplied'
);
}
//var mkdirp = promisify(require("@root/mkdirp"));
opts.configFile = path.join(opts.configDir, 'config.json');
var config;
try {
config = JSON.parse(fs.readFileSync(opts.configFile));
} catch (e) {
if ('ENOENT' !== e.code) {
throw e;
}
config = { defaults: {} };
}
opts.manager =
rc.manager ||
(config.defaults && config.defaults.manager) ||
config.manager;
if (!opts.manager) {
opts.manager = '@greenlock/manager';
}
if ('string' === typeof opts.manager) {
opts.manager = {
module: opts.manager
};
}
opts.manager = JSON.parse(JSON.stringify(opts.manager));
var confconf = ['configDir', 'configFile', 'staging', 'directoryUrl'];
Object.keys(opts).forEach(function(k) {
if (!confconf.includes(k)) {
return;
}
if ('undefined' !== typeof opts.manager[k]) {
return;
}
opts.manager[k] = opts[k];
});
/*
var ignore = ["packageRoot", "maintainerEmail", "packageAgent", "staging", "directoryUrl", "manager"];
Object.keys(opts).forEach(function(k) {
if (ignore.includes(k)) {
return;
}
opts.manager[k] = opts[k];
});
*/
// Place the rc file in the configDir itself
//Rc._initSync(opts.configDir, opts.configDir);
return opts;
};
// ex: "John Doe <john@example.com> (https://john.doe)"
// ex: "John Doe <john@example.com>"
// ex: "<john@example.com>"
// ex: "john@example.com"
var looseEmailRe = /(^|[\s<])([^'" <>:;`]+@[^'" <>:;`]+\.[^'" <>:;`]+)/;
function parseMaintainer(maintainerEmail) {
try {
maintainerEmail = maintainerEmail.match(looseEmailRe)[2];
} catch (e) {
maintainerEmail = null;
}
return maintainerEmail;
}

View File

@ -398,6 +398,7 @@ function loadManager(gconf) {
// 1. Get the manager // 1. Get the manager
// 2. Figure out if we need to wrap it // 2. Figure out if we need to wrap it
/*
if (!gconf.manager) { if (!gconf.manager) {
gconf.manager = '@greenlock/manager'; gconf.manager = '@greenlock/manager';
} }
@ -407,9 +408,10 @@ function loadManager(gconf) {
'`manager` should be a string representing the npm name or file path of the module' '`manager` should be a string representing the npm name or file path of the module'
); );
} }
*/
try { try {
// wrap this to be safe for @greenlock/manager // wrap this to be safe for @greenlock/manager
m = require(gconf.manager).create(gconf); m = require(gconf.manager.module).create(gconf.manager);
} catch (e) { } catch (e) {
console.error('Error loading manager:'); console.error('Error loading manager:');
console.error(e.code); console.error(e.code);
@ -490,8 +492,46 @@ function mergeManager(gconf) {
} }
if (mini.get) { if (mini.get) {
mega.get = function(opts) { mega.get = async function(opts) {
if (mini.set) {
return mini.get(opts); return mini.get(opts);
}
if (!mega._get) {
mega._get = m().get;
}
var existing = await mega._get(opts);
var site = await mini.get(opts);
if (!existing) {
// Add
if (!site) {
return;
}
site.renewAt = 1;
site.deletedAt = 0;
await mega.set(site);
existing = await mega._get(opts);
} else if (!site) {
// Delete
existing.deletedAt = site.deletedAt || Date.now();
await mega.set(existing);
existing = null;
} else if (
site.subject !== existing.subject ||
site.altnames.join(' ') !== existing.altnames.join(' ')
) {
// Update
site.renewAt = 1;
site.deletedAt = 0;
await mega.set(site);
existing = await mega._get(opts);
if (!existing) {
throw new Error('failed to `get` after `set`');
}
}
return existing;
}; };
} else if (mini.find) { } else if (mini.find) {
mega.get = function(opts) { mega.get = function(opts) {
@ -506,7 +546,8 @@ function mergeManager(gconf) {
}; };
} else if (mini.set) { } else if (mini.set) {
throw new Error( throw new Error(
gconf.manager + ' implements `set()`, but not `get()` or `find()`' gconf.manager.module +
' implements `set()`, but not `get()` or `find()`'
); );
} else { } else {
mega.find = m().find; mega.find = m().find;

77
lib/rc.js Normal file
View File

@ -0,0 +1,77 @@
'use strict';
var Rc = module.exports;
var fs = require('fs');
var path = require('path');
// This is only called if packageRoot is specified
// (which it should be most of the time)
Rc._initSync = function(dirname, manager, configDir) {
if (!dirname) {
return {};
}
// dirname / opts.packageRoot
var rcpath = path.resolve(dirname, '.greenlockrc');
var rc;
try {
rc = JSON.parse(fs.readFileSync(rcpath));
} catch (e) {
if ('ENOENT' !== e.code) {
throw e;
}
rc = {};
}
var changed = true;
// In the general case the manager should be specified in the
// config file, which is in the config dir, but for the specific
// case in which all custom plugins are being used and no config
// dir is needed, we allow the manager to be read from the rc.
// ex: manager: { module: 'name', xxxx: 'xxxx' }
if (manager) {
if (rc.manager) {
if (
('string' === typeof rc.manager && rc.manager !== manager) ||
('string' !== typeof rc.manager &&
rc.manager.module !== manager.module)
) {
changed = true;
console.info(
"changing `manager` from '%s' to '%s'",
rc.manager.module || rc.manager,
manager.module || manager
);
}
}
rc.manager = manager;
}
if (!configDir) {
configDir = rc.configDir;
}
if (configDir && configDir !== rc.configDir) {
if (rc.configDir) {
console.info(
"changing `configDir` from '%s' to '%s'",
rc.configDir,
configDir
);
}
changed = true;
rc.configDir = configDir;
} else if (!rc.configDir) {
changed = true;
configDir = './greenlock.d';
rc.configDir = configDir;
}
if (changed) {
fs.writeFileSync(rcpath, JSON.stringify(rc));
}
return rc;
};

37
package-lock.json generated
View File

@ -1,21 +1,21 @@
{ {
"name": "@root/greenlock", "name": "@root/greenlock",
"version": "3.1.5", "version": "4.0.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@greenlock/manager": { "@greenlock/manager": {
"version": "3.0.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/@greenlock/manager/-/manager-3.0.0.tgz", "resolved": "https://registry.npmjs.org/@greenlock/manager/-/manager-3.1.0.tgz",
"integrity": "sha512-ijgJrFdzJPmzrDk8aKXYoYR8LNfG3hXd9/s54ZY7IgxTulyPQ/qOPgl7sWgCxxLhZBzSY1xI6eC/6Y5TQ01agg==", "integrity": "sha512-PBy5CMK+j4oD7sj7hF5qE+xKEOSiiuL2hHd5X5ttEbtnTSDKjNeqbrR5k2ZddwVNdjOVeBIeuqlm81IFZ+Ftew==",
"requires": { "requires": {
"greenlock-manager-fs": "^3.0.5" "greenlock-manager-fs": "^3.1.0"
}, },
"dependencies": { "dependencies": {
"greenlock-manager-fs": { "greenlock-manager-fs": {
"version": "3.0.5", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/greenlock-manager-fs/-/greenlock-manager-fs-3.0.5.tgz", "resolved": "https://registry.npmjs.org/greenlock-manager-fs/-/greenlock-manager-fs-3.1.1.tgz",
"integrity": "sha512-r/q+tEFuDwklfzPfiGhcIrHuJxMrppC+EseESpu5f0DMokh+1iZVm9nGC/VE7/7GETdOYfEYhhQkmspsi8Gr/A==", "integrity": "sha512-np6qdnPIOZx40PAcSQcqK1eMPWjTKxsxcgRd/OVg0ai49WC1Ds74CTrwmB84pq2n53ikbnDBQFmKEQ4AC0DK8w==",
"requires": { "requires": {
"@root/mkdirp": "^1.0.0", "@root/mkdirp": "^1.0.0",
"safe-replace": "^1.1.0" "safe-replace": "^1.1.0"
@ -79,9 +79,9 @@
"integrity": "sha512-rEUDiUsHtild8GfIjFE9wXtcVxeS+ehCJQBwbQQ3IVfORKHK93CFnRtkr69R75lZFjcmKYVc+AXDB+AeRFOULA==" "integrity": "sha512-rEUDiUsHtild8GfIjFE9wXtcVxeS+ehCJQBwbQQ3IVfORKHK93CFnRtkr69R75lZFjcmKYVc+AXDB+AeRFOULA=="
}, },
"@root/request": { "@root/request": {
"version": "1.3.11", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/@root/request/-/request-1.3.11.tgz", "resolved": "https://registry.npmjs.org/@root/request/-/request-1.4.2.tgz",
"integrity": "sha512-3a4Eeghcjsfe6zh7EJ+ni1l8OK9Fz2wL1OjP4UCa0YdvtH39kdXB9RGWuzyNv7dZi0+Ffkc83KfH0WbPMiuJFw==" "integrity": "sha512-J8FM4+SJuc7WRC+Jz17m+VT2lgI7HtatHhxN1F2ck5aIKUAxJEaR4u/gLBsgT60mVHevKCjKN0O8115UtJjwLw=="
}, },
"@root/x509": { "@root/x509": {
"version": "0.7.2", "version": "0.7.2",
@ -108,19 +108,10 @@
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==",
"dev": true "dev": true
}, },
"greenlock-manager-fs": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/greenlock-manager-fs/-/greenlock-manager-fs-3.0.5.tgz",
"integrity": "sha512-r/q+tEFuDwklfzPfiGhcIrHuJxMrppC+EseESpu5f0DMokh+1iZVm9nGC/VE7/7GETdOYfEYhhQkmspsi8Gr/A==",
"requires": {
"@root/mkdirp": "^1.0.0",
"safe-replace": "^1.1.0"
}
},
"greenlock-store-fs": { "greenlock-store-fs": {
"version": "3.2.0", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/greenlock-store-fs/-/greenlock-store-fs-3.2.0.tgz", "resolved": "https://registry.npmjs.org/greenlock-store-fs/-/greenlock-store-fs-3.2.2.tgz",
"integrity": "sha512-zqcPnF+173oYq5qU7FoGtuqeG8dmmvAiSnz98kEHAHyvgRF9pE1T0MM0AuqDdj45I3kXlCj2gZBwutnRi37J3g==", "integrity": "sha512-92ejLB4DyV4qv/2b6VLGF2nKfYQeIfg3o+e/1cIoYLjlIaUFdbBXkzLTRozFlHsQPZt2ALi5qYrpC9IwH7GK8A==",
"requires": { "requires": {
"@root/mkdirp": "^1.0.0", "@root/mkdirp": "^1.0.0",
"safe-replace": "^1.1.0" "safe-replace": "^1.1.0"

View File

@ -1,6 +1,6 @@
{ {
"name": "@root/greenlock", "name": "@root/greenlock",
"version": "3.1.5", "version": "4.0.1",
"description": "The easiest Let's Encrypt client for Node.js and Browsers", "description": "The easiest Let's Encrypt client for Node.js and Browsers",
"homepage": "https://rootprojects.org/greenlock/", "homepage": "https://rootprojects.org/greenlock/",
"main": "greenlock.js", "main": "greenlock.js",
@ -38,16 +38,15 @@
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)", "author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
"license": "MPL-2.0", "license": "MPL-2.0",
"dependencies": { "dependencies": {
"@greenlock/manager": "^3.0.0", "@greenlock/manager": "^3.1.0",
"@root/acme": "^3.0.8", "@root/acme": "^3.0.8",
"@root/csr": "^0.8.1", "@root/csr": "^0.8.1",
"@root/keypairs": "^0.9.0", "@root/keypairs": "^0.9.0",
"@root/mkdirp": "^1.0.0", "@root/mkdirp": "^1.0.0",
"@root/request": "^1.3.10", "@root/request": "^1.4.2",
"acme-http-01-standalone": "^3.0.5", "acme-http-01-standalone": "^3.0.5",
"cert-info": "^1.5.1", "cert-info": "^1.5.1",
"greenlock-manager-fs": "^3.0.5", "greenlock-store-fs": "^3.2.2",
"greenlock-store-fs": "^3.2.0",
"safe-replace": "^1.1.0" "safe-replace": "^1.1.0"
}, },
"devDependencies": { "devDependencies": {