greenlock.js/bin/lib/flags.js

395 lines
12 KiB
JavaScript

'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');
// These are ALL options
// The individual CLI files each select a subset of them
Flags.flags = function(mconf, myOpts) {
// Current Manager Config
if (!mconf) {
mconf = {};
}
// Extra Override Options
if (!myOpts) {
myOpts = {};
}
return {
all: [
false,
'search all site configs rather than by --subject or --servernames',
'boolean'
],
'agree-to-terms': [
false,
"agree to the Let's Encrypts Subscriber Agreement and Greenlock Terms of Use",
'boolean'
],
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'
],
cluster: [false, 'initialize with cluster mode on', 'boolean', false],
'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
],
'customer-email': [
false,
"the email address of the owner of the domain or site (not necessarily the Let's Encrypt or ACME subscriber)",
'string'
],
'subscriber-email': [
false,
"the email address of the Let's Encrypt or ACME Account subscriber (not necessarily the domain owner)",
'string'
],
'maintainer-email': [
false,
'the maintainance contact for security and critical bug notices',
'string'
],
'account-key-type': [
false,
"either 'P-256' (ECDSA) or 'RSA-2048' - although other values are technically supported, they don't make sense and won't work with many services (More bits != More security)",
'string',
mconf.accountKeyType
],
'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
],
store: [
false,
'the module name or file path of the store module to use',
'string'
//mconf.store.module
],
'store-xxxx': [
false,
'an option for the chosen store module, such as --store-apikey or --store-bucket',
'bag'
],
manager: [
false,
'the module name or file path of the manager module to use',
'string',
'greenlock-manager-fs'
],
'manager-xxxx': [
false,
'an option for the chosen manager module, such as --manager-apikey or --manager-dburl',
'bag'
],
challenge: [
false,
'the module name or 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',
'{}'
],
'challenge-http-01': [
false,
'the module name or file path of the HTTP-01 to add',
'string'
//(mconf.challenges['http-01'] || {}).module
],
'challenge-http-01-xxxx': [
false,
'an option for the chosen challenge module, such as --challenge-http-01-apikey or --challenge-http-01-bucket',
'bag'
],
'challenge-dns-01': [
false,
'the module name or file path of the DNS-01 to add',
'string'
//(mconf.challenges['dns-01'] || {}).module
],
'challenge-dns-01-xxxx': [
false,
'an option for the chosen challenge module, such as --challenge-dns-01-apikey or --challenge-dns-01-bucket',
'bag'
],
'challenge-tls-alpn-01': [
false,
'the module name or file path of the DNS-01 to add',
'string'
//(mconf.challenges['tls-alpn-01'] || {}).module
],
'challenge-tls-alpn-01-xxxx': [
false,
'an option for the chosen challenge module, such as --challenge-tls-alpn-01-apikey or --challenge-tls-alpn-01-bucket',
'bag'
],
'force-save': [
false,
"save all options for this site, even if it's the same as the defaults",
'boolean',
myOpts.forceSave || false
]
};
};
Flags.init = async function(myOpts) {
var rc = await GreenlockRc(pkgpath);
rc._bin_mode = true;
var Greenlock = require('../../');
// this is a copy, so it's safe to modify
rc.packageRoot = path.dirname(pkgpath);
var greenlock = Greenlock.create(rc);
var mconf = await greenlock.manager.defaults();
var flagOptions = Flags.flags(mconf, myOpts);
return {
flagOptions,
rc,
greenlock,
mconf
};
};
Flags.mangleFlags = function(flags, mconf, sconf, extras) {
if (extras) {
if (extras.forceSave) {
flags.forceSave = true;
}
}
//console.log('debug a:', flags);
if ('altnames' in flags) {
flags.altnames = (flags.altnames || '').split(/[,\s]+/).filter(Boolean);
}
if ('servernames' in flags) {
flags.servernames = (flags.servernames || '')
.split(/[,\s]+/)
.filter(Boolean);
}
var store;
if (flags.store) {
store = flags.storeOpts;
store.module = flags.store;
flags.store = store;
} else {
delete flags.store;
}
delete flags.storeOpts;
// If this is additive, make an object to hold all values
var isAdditive = [
['http-01', 'Http01'],
['dns-01', 'Dns01'],
['tls-alpn-01', 'TlsAlpn01']
].some(function(types) {
var typCamel = types[1];
var modname = 'challenge' + typCamel;
if (flags[modname]) {
if (!flags.challenges) {
flags.challenges = {};
}
return true;
}
});
if (isAdditive && sconf) {
// copy over the old
var schallenges = sconf.challenges || {};
Object.keys(schallenges).forEach(function(k) {
if (!flags.challenges[k]) {
flags.challenges[k] = schallenges[k];
}
});
}
var typ;
var challenge;
if (flags.challenge) {
// this varient of the flag is exclusive
flags.challenges = {};
isAdditive = false;
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';
}
var modname = 'challenge';
var optsname = 'challengeOpts';
challenge = flags[optsname];
// JSON may already have module name
if (challenge.module) {
if (flags[modname] && challenge.module !== flags[modname]) {
console.error(
'module names do not match:',
JSON.stringify(challenge.module),
JSON.stringify(flags[modname])
);
process.exit(1);
}
} else {
challenge.module = flags[modname];
}
flags.challenges[typ] = challenge;
var chall = mconf.challenges[typ];
if (chall && 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.challenge;
delete flags.challengeOpts;
// Add each of the values, including the existing
[
['http-01', 'Http01'],
['dns-01', 'Dns01'],
['tls-alpn-01', 'TlsAlpn01']
].forEach(function(types) {
var typ = types[0];
var typCamel = types[1];
var modname = 'challenge' + typCamel;
var optsname = 'challenge' + typCamel + 'Opts';
var chall = mconf.challenges[typ];
var challenge = flags[optsname];
// this variant of the flag is additive
if (isAdditive && chall && flags.forceSave) {
if (flags.challenges && !flags.challenges[typ]) {
flags.challenges[typ] = chall;
}
}
if (!flags[modname]) {
delete flags[modname];
delete flags[optsname];
return;
}
// JSON may already have module name
if (challenge.module) {
if (flags[modname] && challenge.module !== flags[modname]) {
console.error(
'module names do not match:',
JSON.stringify(challenge.module),
JSON.stringify(flags[modname])
);
process.exit(1);
}
} else {
challenge.module = flags[modname];
}
if (flags[modname]) {
if (!flags.challenges) {
flags.challenges = {};
}
flags.challenges[typ] = challenge;
}
// Check to see if this is already what's set in the defaults
if (chall && challenge.module === chall.module) {
var keys = Object.keys(challenge);
// Check if all of the options are also the same
var same =
!keys.length ||
keys.every(function(k) {
return chall[k] === challenge[k];
});
if (same && !flags.forceSave) {
// If it's already the global, don't make it the per-site
delete flags[modname];
delete flags[optsname];
}
}
delete flags[modname];
delete flags[optsname];
});
[
['accountKeyType', [/256/, /384/, /EC/], 'EC-P256'],
['serverKeyType', [/RSA/], 'RSA-2048']
].forEach(function(k) {
var key = k[0];
var vals = k[1];
var val = flags[key];
if (val) {
if (
!vals.some(function(v) {
return v.test(val);
})
) {
flags[key] = k[2];
console.warn(
key,
"does not allow the value '",
val,
"' using the default '",
k[2],
"' instead."
);
}
}
});
Object.keys(flags).forEach(function(k) {
if (flags[k] === mconf[k] && !flags.forceSave) {
delete flags[k];
}
});
//console.log('debug z:', flags);
delete flags.forceSave;
};