7 changed files with 312 additions and 53 deletions
@ -0,0 +1,134 @@ |
|||
'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'); |
|||
|
|||
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(); |
|||
|
|||
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 |
|||
] |
|||
}); |
|||
|
|||
// ignore certonly and extraneous arguments
|
|||
async function main(_, options) { |
|||
if (!options.subject || !options.altnames) { |
|||
console.error( |
|||
'--subject and --altnames must be provided and should be valid domains' |
|||
); |
|||
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]; |
|||
} |
|||
}); |
|||
|
|||
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) { |
|||
console.error(); |
|||
console.error('error:', err.message); |
|||
console.error(); |
|||
}); |
|||
} |
|||
|
|||
cli.main(main, process.argv.slice(3)); |
|||
}); |
@ -0,0 +1,113 @@ |
|||
'use strict'; |
|||
|
|||
// TODO how to handle path differences when run from npx vs when required by greenlock?
|
|||
|
|||
var promisify = require('util').promisify; |
|||
var fs = require('fs'); |
|||
var readFile = promisify(fs.readFile); |
|||
var writeFile = promisify(fs.writeFile); |
|||
var chmodFile = promisify(fs.chmod); |
|||
var path = require('path'); |
|||
|
|||
function saveFile(rcpath, data, enc) { |
|||
// because this may have a database url or some such
|
|||
return writeFile(rcpath, data, enc).then(function() { |
|||
return chmodFile(rcpath, parseInt('0600', 8)); |
|||
}); |
|||
} |
|||
|
|||
module.exports = async function(pkgpath, manager, rc) { |
|||
// TODO when run from package
|
|||
// Run from the package root (assumed) or exit
|
|||
var pkgdir = path.dirname(pkgpath); |
|||
var rcpath = path.join(pkgdir, '.greenlockrc'); |
|||
var created = false; |
|||
|
|||
try { |
|||
require(pkgpath); |
|||
} catch (e) { |
|||
console.error( |
|||
'npx greenlock must be run from the package root (where package.json is)' |
|||
); |
|||
process.exit(1); |
|||
} |
|||
|
|||
if (manager) { |
|||
if ('.' === manager[0]) { |
|||
manager = path.resolve(pkgdir, manager); |
|||
} |
|||
try { |
|||
require(manager); |
|||
} catch (e) { |
|||
console.error('npx greenlock must be run from the package root'); |
|||
process.exit(1); |
|||
} |
|||
} |
|||
|
|||
var _data = await readFile(rcpath, 'utf8').catch(function(err) { |
|||
if ('ENOENT' !== err.code) { |
|||
throw err; |
|||
} |
|||
console.info('Creating ' + rcpath); |
|||
created = true; |
|||
var data = '{}'; |
|||
return saveFile(rcpath, data, 'utf8').then(function() { |
|||
return data; |
|||
}); |
|||
}); |
|||
|
|||
var changed; |
|||
var _rc; |
|||
try { |
|||
_rc = JSON.parse(_data); |
|||
} catch (e) { |
|||
console.error("couldn't parse " + rcpath, _data); |
|||
console.error('(perhaps you should just delete it and try again?)'); |
|||
process.exit(1); |
|||
} |
|||
|
|||
if (manager) { |
|||
if (!_rc.manager) { |
|||
_rc.manager = manager; |
|||
} |
|||
if (_rc.manager !== manager) { |
|||
console.info('Switching manager:'); |
|||
var older = _rc.manager; |
|||
var newer = manager; |
|||
if ('/' === older[0]) { |
|||
older = path.relative(pkgdir, older); |
|||
} |
|||
if ('/' === newer[0]) { |
|||
newer = path.relative(pkgdir, newer); |
|||
} |
|||
console.info('\told: ' + older); |
|||
console.info('\tnew: ' + newer); |
|||
changed = true; |
|||
} |
|||
} |
|||
|
|||
if (rc) { |
|||
changed = true; |
|||
Object.keys(rc).forEach(function(k) { |
|||
_rc[k] = rc; |
|||
}); |
|||
} |
|||
|
|||
if (!_rc.manager) { |
|||
changed = true; |
|||
_rc.manager = 'greenlock-manager-fs'; |
|||
console.info('Using default manager ' + _rc.manager); |
|||
} |
|||
|
|||
if (!changed) { |
|||
return _rc; |
|||
} |
|||
|
|||
var data = JSON.stringify(_rc, null, 2); |
|||
if (created) { |
|||
console.info('Wrote ' + rcpath); |
|||
} |
|||
return saveFile(rcpath, data, 'utf8').then(function() { |
|||
return _rc; |
|||
}); |
|||
}; |
Loading…
Reference in new issue