cli: add, update, config
This commit is contained in:
parent
060979daf2
commit
2dbd61158f
178
bin/add.js
178
bin/add.js
|
@ -2,133 +2,85 @@
|
|||
|
||||
var args = process.argv.slice(3);
|
||||
var cli = require('./cli.js');
|
||||
var path = require('path');
|
||||
//var path = require('path');
|
||||
//var pkgpath = path.join(__dirname, '..', 'package.json');
|
||||
var pkgpath = path.join(process.cwd(), 'package.json');
|
||||
//var pkgpath = path.join(process.cwd(), 'package.json');
|
||||
|
||||
require('./greenlockrc')(pkgpath).then(async function(rc) {
|
||||
var Greenlock = require('../');
|
||||
// this is a copy, so it's safe to modify
|
||||
rc._bin_mode = true;
|
||||
var greenlock = Greenlock.create(rc);
|
||||
var mconf = await greenlock.manager.defaults();
|
||||
var Flags = require('./flags.js');
|
||||
|
||||
cli.parse({
|
||||
subject: [
|
||||
false,
|
||||
'the "subject" (primary domain) of the certificate',
|
||||
'string'
|
||||
],
|
||||
altnames: [
|
||||
false,
|
||||
'the "subject alternative names" (additional domains) on the certificate, the first of which MUST be the subject',
|
||||
'string'
|
||||
],
|
||||
'renew-offset': [
|
||||
false,
|
||||
"time to wait until renewing the cert such as '45d' (45 days after being issued) or '-3w' (3 weeks before expiration date)",
|
||||
'string',
|
||||
mconf.renewOffset
|
||||
],
|
||||
'server-key-type': [
|
||||
false,
|
||||
"either 'RSA-2048' or 'P-256' (ECDSA) - although other values are technically supported, they don't make sense and won't work with many services (More bits != More security)",
|
||||
'string',
|
||||
mconf.serverKeyType
|
||||
],
|
||||
challenge: [
|
||||
false,
|
||||
'the name name of file path of the HTTP-01, DNS-01, or TLS-ALPN-01 challenge module to use',
|
||||
'string',
|
||||
Object.keys(mconf.challenges)
|
||||
.map(function(typ) {
|
||||
return mconf.challenges[typ].module;
|
||||
})
|
||||
.join(',')
|
||||
],
|
||||
'challenge-xxxx': [
|
||||
false,
|
||||
'an option for the chosen challenge module, such as --challenge-apikey or --challenge-bucket',
|
||||
'bag'
|
||||
],
|
||||
'challenge-json': [
|
||||
false,
|
||||
'a JSON string containing all option for the chosen challenge module (instead of --challenge-xxxx)',
|
||||
'json',
|
||||
'{}'
|
||||
],
|
||||
'force-save': [
|
||||
false,
|
||||
"save all options for this site, even if it's the same as the defaults",
|
||||
'boolean',
|
||||
false
|
||||
]
|
||||
Flags.init().then(function({ flagOptions, rc, greenlock, mconf }) {
|
||||
var myFlags = {};
|
||||
[
|
||||
'subject',
|
||||
'altnames',
|
||||
'renew-offset',
|
||||
'server-key-type',
|
||||
'challenge',
|
||||
'challenge-xxxx',
|
||||
'challenge-json',
|
||||
'force-save'
|
||||
].forEach(function(k) {
|
||||
myFlags[k] = flagOptions[k];
|
||||
});
|
||||
|
||||
// ignore certonly and extraneous arguments
|
||||
async function main(_, options) {
|
||||
if (!options.subject || !options.altnames) {
|
||||
cli.parse(myFlags);
|
||||
cli.main(function(argList, flags) {
|
||||
main(argList, flags, rc, greenlock, mconf);
|
||||
}, args);
|
||||
});
|
||||
|
||||
async function main(_, flags, rc, greenlock, mconf) {
|
||||
if (!flags.subject) {
|
||||
console.error(
|
||||
'--subject and --altnames must be provided and should be valid domains'
|
||||
'--subject must be provided as the id of the site/certificate'
|
||||
);
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
options.altnames = options.altnames.split(/[,\s]+/);
|
||||
|
||||
Object.keys(options).forEach(function(k) {
|
||||
if (options[k] === mconf[k] && !options.forceSave) {
|
||||
delete options[k];
|
||||
}
|
||||
});
|
||||
Flags.mangleFlags(flags, mconf);
|
||||
|
||||
var typ;
|
||||
var challenge;
|
||||
if (options.challenge) {
|
||||
if (/http-01/.test(options.challenge)) {
|
||||
typ = 'http-01';
|
||||
} else if (/dns-01/.test(options.challenge)) {
|
||||
typ = 'dns-01';
|
||||
} else if (/tls-alpn-01/.test(options.challenge)) {
|
||||
typ = 'tls-alpn-01';
|
||||
}
|
||||
|
||||
challenge = options.challengeOpts;
|
||||
challenge.module = options.challenge;
|
||||
options.challenges = {};
|
||||
options.challenges[typ] = challenge;
|
||||
delete options.challengeOpts;
|
||||
delete options.challenge;
|
||||
|
||||
var chall = mconf.challenges[typ];
|
||||
if (challenge.module === chall.module) {
|
||||
var keys = Object.keys(challenge);
|
||||
var same =
|
||||
!keys.length ||
|
||||
keys.every(function(k) {
|
||||
return chall[k] === challenge[k];
|
||||
});
|
||||
if (same && !options.forceSave) {
|
||||
delete options.challenges;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete options.forceSave;
|
||||
|
||||
/*
|
||||
console.log('manager conf:');
|
||||
console.log(mconf);
|
||||
console.log('cli options:');
|
||||
console.log(options);
|
||||
*/
|
||||
|
||||
greenlock.add(options).catch(function(err) {
|
||||
greenlock
|
||||
.add(flags)
|
||||
.catch(function(err) {
|
||||
console.error();
|
||||
console.error('error:', err.message);
|
||||
console.error();
|
||||
})
|
||||
.then(function() {
|
||||
return greenlock
|
||||
._config({
|
||||
servername:
|
||||
flags.altnames[
|
||||
Math.floor(Math.random() * flags.altnames.length)
|
||||
]
|
||||
})
|
||||
.then(function(site) {
|
||||
if (!site) {
|
||||
console.info();
|
||||
console.info(
|
||||
'Internal bug or configuration mismatch: No config found.'
|
||||
);
|
||||
console.info();
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
console.info();
|
||||
console.info('Created config!');
|
||||
console.info();
|
||||
|
||||
Object.keys(site).forEach(function(k) {
|
||||
if ('defaults' === k) {
|
||||
console.info(k + ':');
|
||||
Object.keys(site.defaults).forEach(function(key) {
|
||||
var value = JSON.stringify(site.defaults[key]);
|
||||
console.info('\t' + key + ':' + value);
|
||||
});
|
||||
} else {
|
||||
console.info(k + ': ' + JSON.stringify(site[k]));
|
||||
}
|
||||
});
|
||||
console.info();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
cli.main(main, process.argv.slice(3));
|
||||
});
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
'use strict';
|
||||
|
||||
var args = process.argv.slice(3);
|
||||
var cli = require('./cli.js');
|
||||
//var path = require('path');
|
||||
//var pkgpath = path.join(__dirname, '..', 'package.json');
|
||||
//var pkgpath = path.join(process.cwd(), 'package.json');
|
||||
|
||||
var Flags = require('./flags.js');
|
||||
|
||||
Flags.init().then(function({ flagOptions, rc, greenlock, mconf }) {
|
||||
var myFlags = {};
|
||||
['subject', 'servername' /*, 'servernames', 'altnames'*/].forEach(function(
|
||||
k
|
||||
) {
|
||||
myFlags[k] = flagOptions[k];
|
||||
});
|
||||
|
||||
cli.parse(myFlags);
|
||||
cli.main(function(argList, flags) {
|
||||
Flags.mangleFlags(flags, mconf);
|
||||
main(argList, flags, rc, greenlock, mconf);
|
||||
}, args);
|
||||
});
|
||||
|
||||
async function main(_, flags, rc, greenlock /*, mconf */) {
|
||||
var servernames = [flags.subject]
|
||||
.concat([flags.servername])
|
||||
//.concat(flags.servernames)
|
||||
//.concat(flags.altnames)
|
||||
.filter(Boolean);
|
||||
delete flags.subject;
|
||||
delete flags.altnames;
|
||||
flags.servernames = servernames;
|
||||
if (flags.servernames.length > 1) {
|
||||
console.error('Error: should only have one servername');
|
||||
process.exit(1);
|
||||
return;
|
||||
} else if (flags.servernames.length !== 1) {
|
||||
console.error('Error: need a servername to check');
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
flags.servername = flags.servernames[0];
|
||||
delete flags.servernames;
|
||||
|
||||
greenlock
|
||||
._config(flags)
|
||||
.catch(function(err) {
|
||||
console.error();
|
||||
console.error('error:', err.message);
|
||||
console.log(err.stack);
|
||||
console.error();
|
||||
})
|
||||
.then(function(site) {
|
||||
if (!site) {
|
||||
console.info();
|
||||
console.info('No config found for ');
|
||||
console.info();
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
console.info();
|
||||
console.info(
|
||||
'Config for ' + JSON.stringify(flags.servername) + ':'
|
||||
);
|
||||
console.info(JSON.stringify(site, null, 2));
|
||||
console.info();
|
||||
});
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
store: [
|
||||
false,
|
||||
'the npm name or path of the storage module or callbacks file',
|
||||
'string',
|
||||
'greenlock-store-fs'
|
||||
],
|
||||
'store-xxxx': [
|
||||
false,
|
||||
'an option for the chosen storage module, such as --store-apikey or --store-bucket',
|
||||
'bag'
|
||||
],
|
||||
'store-json': [
|
||||
false,
|
||||
'a JSON string containing all option for the chosen store module (instead of --store-xxxx)',
|
||||
'json',
|
||||
'{}'
|
||||
],
|
|
@ -0,0 +1,138 @@
|
|||
'use strict';
|
||||
|
||||
var Flags = module.exports;
|
||||
|
||||
var path = require('path');
|
||||
//var pkgpath = path.join(__dirname, '..', 'package.json');
|
||||
var pkgpath = path.join(process.cwd(), 'package.json');
|
||||
var GreenlockRc = require('./greenlockrc.js');
|
||||
|
||||
Flags.init = function() {
|
||||
return GreenlockRc(pkgpath).then(async function(rc) {
|
||||
var Greenlock = require('../');
|
||||
// this is a copy, so it's safe to modify
|
||||
rc._bin_mode = true;
|
||||
var greenlock = Greenlock.create(rc);
|
||||
var mconf = await greenlock.manager.defaults();
|
||||
|
||||
var flagOptions = {
|
||||
subject: [
|
||||
false,
|
||||
'the "subject" (primary domain) of the certificate',
|
||||
'string'
|
||||
],
|
||||
altnames: [
|
||||
false,
|
||||
'the "subject alternative names" (additional domains) on the certificate, the first of which MUST be the subject',
|
||||
'string'
|
||||
],
|
||||
servername: [
|
||||
false,
|
||||
'a name that matches a subject or altname',
|
||||
'string'
|
||||
],
|
||||
servernames: [
|
||||
false,
|
||||
'a list of names that matches a subject or altname',
|
||||
'string'
|
||||
],
|
||||
'renew-offset': [
|
||||
false,
|
||||
"time to wait until renewing the cert such as '45d' (45 days after being issued) or '-3w' (3 weeks before expiration date)",
|
||||
'string',
|
||||
mconf.renewOffset
|
||||
],
|
||||
'server-key-type': [
|
||||
false,
|
||||
"either 'RSA-2048' or 'P-256' (ECDSA) - although other values are technically supported, they don't make sense and won't work with many services (More bits != More security)",
|
||||
'string',
|
||||
mconf.serverKeyType
|
||||
],
|
||||
challenge: [
|
||||
false,
|
||||
'the name name of file path of the HTTP-01, DNS-01, or TLS-ALPN-01 challenge module to use',
|
||||
'string',
|
||||
Object.keys(mconf.challenges)
|
||||
.map(function(typ) {
|
||||
return mconf.challenges[typ].module;
|
||||
})
|
||||
.join(',')
|
||||
],
|
||||
'challenge-xxxx': [
|
||||
false,
|
||||
'an option for the chosen challenge module, such as --challenge-apikey or --challenge-bucket',
|
||||
'bag'
|
||||
],
|
||||
'challenge-json': [
|
||||
false,
|
||||
'a JSON string containing all option for the chosen challenge module (instead of --challenge-xxxx)',
|
||||
'json',
|
||||
'{}'
|
||||
],
|
||||
'force-save': [
|
||||
false,
|
||||
"save all options for this site, even if it's the same as the defaults",
|
||||
'boolean',
|
||||
false
|
||||
]
|
||||
};
|
||||
|
||||
return {
|
||||
flagOptions,
|
||||
rc,
|
||||
greenlock,
|
||||
mconf
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
Flags.mangleFlags = function(flags, mconf) {
|
||||
if ('altnames' in flags) {
|
||||
flags.altnames = (flags.altnames || '').split(/[,\s]+/).filter(Boolean);
|
||||
}
|
||||
if ('servernames' in flags) {
|
||||
flags.servernames = (flags.servernames || '')
|
||||
.split(/[,\s]+/)
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
Object.keys(flags).forEach(function(k) {
|
||||
if (flags[k] === mconf[k] && !flags.forceSave) {
|
||||
delete flags[k];
|
||||
}
|
||||
});
|
||||
|
||||
var typ;
|
||||
var challenge;
|
||||
if (flags.challenge) {
|
||||
if (/http-01/.test(flags.challenge)) {
|
||||
typ = 'http-01';
|
||||
} else if (/dns-01/.test(flags.challenge)) {
|
||||
typ = 'dns-01';
|
||||
} else if (/tls-alpn-01/.test(flags.challenge)) {
|
||||
typ = 'tls-alpn-01';
|
||||
}
|
||||
|
||||
challenge = flags.challengeOpts;
|
||||
challenge.module = flags.challenge;
|
||||
flags.challenges = {};
|
||||
flags.challenges[typ] = challenge;
|
||||
delete flags.challengeOpts;
|
||||
delete flags.challenge;
|
||||
|
||||
var chall = mconf.challenges[typ];
|
||||
if (challenge.module === chall.module) {
|
||||
var keys = Object.keys(challenge);
|
||||
var same =
|
||||
!keys.length ||
|
||||
keys.every(function(k) {
|
||||
return chall[k] === challenge[k];
|
||||
});
|
||||
if (same && !flags.forceSave) {
|
||||
delete flags.challenges;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete flags.forceSave;
|
||||
};
|
|
@ -5,7 +5,7 @@ var args = process.argv.slice(2);
|
|||
var arg0 = args[0];
|
||||
//console.log(args);
|
||||
|
||||
var found = ['certonly', 'add', 'config', 'defaults', 'remove'].some(function(
|
||||
var found = ['certonly', 'add', 'update', 'config', 'defaults', 'remove'].some(function(
|
||||
k
|
||||
) {
|
||||
if (k === arg0) {
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
'use strict';
|
||||
|
||||
var args = process.argv.slice(3);
|
||||
var cli = require('./cli.js');
|
||||
var Flags = require('./flags.js');
|
||||
|
||||
Flags.init().then(function({ flagOptions, rc, greenlock, mconf }) {
|
||||
var myFlags = {};
|
||||
[
|
||||
'subject',
|
||||
'altnames',
|
||||
'renew-offset',
|
||||
'server-key-type',
|
||||
'challenge',
|
||||
'challenge-xxxx',
|
||||
'challenge-json',
|
||||
'force-save'
|
||||
].forEach(function(k) {
|
||||
myFlags[k] = flagOptions[k];
|
||||
});
|
||||
|
||||
cli.parse(myFlags);
|
||||
cli.main(function(argList, flags) {
|
||||
main(argList, flags, rc, greenlock, mconf);
|
||||
}, args);
|
||||
});
|
||||
|
||||
async function main(_, flags, rc, greenlock, mconf) {
|
||||
if (!flags.subject || !flags.altnames) {
|
||||
console.error(
|
||||
'--subject and --altnames must be provided and should be valid domains'
|
||||
);
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
Flags.mangleFlags(flags, mconf);
|
||||
|
||||
greenlock.update(flags).catch(function(err) {
|
||||
console.error();
|
||||
console.error('error:', err.message);
|
||||
console.error();
|
||||
}) .then(function() {
|
||||
return greenlock
|
||||
._config({ servername: flags.subject })
|
||||
.then(function(site) {
|
||||
if (!site) {
|
||||
console.info();
|
||||
console.info('No config found for ');
|
||||
console.info();
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
console.info();
|
||||
console.info("Updated config!");
|
||||
console.info();
|
||||
|
||||
Object.keys(site).forEach(function(k) {
|
||||
if ('defaults' === k) {
|
||||
console.info(k + ':');
|
||||
Object.keys(site.defaults).forEach(function(key) {
|
||||
var value = JSON.stringify(site.defaults[key]);
|
||||
console.info('\t' + key + ':' + value);
|
||||
});
|
||||
} else {
|
||||
console.info(k + ': ' + JSON.stringify(site[k]));
|
||||
}
|
||||
});
|
||||
console.info();
|
||||
});
|
||||
});
|
||||
}
|
22
greenlock.js
22
greenlock.js
|
@ -128,9 +128,13 @@ G.create = function(gconf) {
|
|||
});
|
||||
})
|
||||
.catch(function(err) {
|
||||
if ('load_plugin' !== err.context) {
|
||||
console.error('Fatal error during greenlock init:');
|
||||
console.error(err);
|
||||
console.error(err.message);
|
||||
}
|
||||
if (!gconf._bin_mode) {
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
return p;
|
||||
};
|
||||
|
@ -247,8 +251,14 @@ G.create = function(gconf) {
|
|||
.split('.')
|
||||
.slice(1)
|
||||
.join('.');
|
||||
if (args.wildname.split('.').length < 3) {
|
||||
// No '*.com'
|
||||
args.wildname = '';
|
||||
}
|
||||
if (
|
||||
args.servernames ||
|
||||
//TODO I think we need to block altnames as well, but I don't want to break anything
|
||||
//args.altnames ||
|
||||
args.subject ||
|
||||
args.renewBefore ||
|
||||
args.issueBefore ||
|
||||
|
@ -279,12 +289,16 @@ G.create = function(gconf) {
|
|||
if (site.store && site.challenges) {
|
||||
return site;
|
||||
}
|
||||
var dconf = site;
|
||||
if (gconf._bin_mode) {
|
||||
dconf = site.defaults = {};
|
||||
}
|
||||
return manager.defaults().then(function(mconf) {
|
||||
if (!site.store) {
|
||||
site.store = mconf.store;
|
||||
dconf.store = mconf.store;
|
||||
}
|
||||
if (!site.challenges) {
|
||||
site.challenges = mconf.challenges;
|
||||
dconf.challenges = mconf.challenges;
|
||||
}
|
||||
return site;
|
||||
});
|
||||
|
@ -483,6 +497,7 @@ function normalizeManager(gconf) {
|
|||
// wrap this to be safe for greenlock-manager-fs
|
||||
m = require(gconf.manager).create(gconf);
|
||||
} catch (e) {
|
||||
console.error('Error loading manager:');
|
||||
console.error(e.code);
|
||||
console.error(e.message);
|
||||
}
|
||||
|
@ -592,6 +607,7 @@ function mergeDefaults(MCONF, gconf) {
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
// just to test that it loads
|
||||
P._loadSync(MCONF.store.module);
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ P._loadHelper = function(modname) {
|
|||
console.error("Could not load '%s'", modname);
|
||||
console.error('Did you install it?');
|
||||
console.error('\tnpm install --save %s', modname);
|
||||
e.context = 'load_plugin';
|
||||
throw e;
|
||||
|
||||
// Fun experiment, bad idea
|
||||
|
@ -193,6 +194,7 @@ P._loadSync = function(modname) {
|
|||
console.error("Could not load '%s'", modname);
|
||||
console.error('Did you install it?');
|
||||
console.error('\tnpm install --save %s', modname);
|
||||
e.context = 'load_plugin';
|
||||
throw e;
|
||||
}
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue