mirror of
https://github.com/therootcompany/greenlock.js.git
synced 2024-11-16 17:29:00 +00:00
Backwards compatibility madness.
When you Greenlock.create({ }), challenges will be set by default if
not available. This is good... ish.
When you approveDomains() and set opts.challenges, however, those must
be able to override the defaults.
This was just recently broken and the fix seems to be to make the prior
defaults inaccessible, otherwise it becomes really confusing as to why
a set DNS challenge for local, wild, or private domains is not being
preferred to the (failing) http.
All this crap will be cleaned up in v3...
606 lines
25 KiB
JavaScript
606 lines
25 KiB
JavaScript
'use strict';
|
|
/*global Promise*/
|
|
require('./compat.js');
|
|
|
|
var util = require('util');
|
|
function promisifyAll(obj) {
|
|
var aobj = {};
|
|
Object.keys(obj).forEach(function (key) {
|
|
if ('function' === typeof obj[key]) {
|
|
aobj[key] = obj[key];
|
|
aobj[key + 'Async'] = util.promisify(obj[key]);
|
|
}
|
|
});
|
|
return aobj;
|
|
}
|
|
|
|
function _log(debug) {
|
|
if (debug) {
|
|
var args = Array.prototype.slice.call(arguments);
|
|
args.shift();
|
|
args.unshift("[greenlock/lib/core.js]");
|
|
console.log.apply(console, args);
|
|
}
|
|
}
|
|
|
|
module.exports.create = function (gl) {
|
|
var utils = require('./utils');
|
|
var RSA = promisifyAll(require('rsa-compat').RSA);
|
|
var log = gl.log || _log; // allow custom log
|
|
var pendingRegistrations = {};
|
|
|
|
var core = {
|
|
//
|
|
// Helpers
|
|
//
|
|
getAcmeUrlsAsync: function (args) {
|
|
var now = Date.now();
|
|
|
|
// TODO check response header on request for cache time
|
|
if ((now - gl._ipc.acmeUrlsUpdatedAt) < 10 * 60 * 1000) {
|
|
return Promise.resolve(gl._ipc.acmeUrls);
|
|
}
|
|
|
|
// TODO acme-v2/nocompat
|
|
return gl.acme.getAcmeUrlsAsync(args.server).then(function (data) {
|
|
gl._ipc.acmeUrlsUpdatedAt = Date.now();
|
|
gl._ipc.acmeUrls = data;
|
|
|
|
return gl._ipc.acmeUrls;
|
|
});
|
|
}
|
|
|
|
|
|
//
|
|
// The Main Enchilada
|
|
//
|
|
|
|
//
|
|
// Accounts
|
|
//
|
|
, accounts: {
|
|
// Accounts
|
|
registerAsync: function (args) {
|
|
var err;
|
|
var copy = utils.merge(args, gl);
|
|
var disagreeTos;
|
|
args = utils.tplCopy(copy);
|
|
if (!args.account) { args.account = {}; }
|
|
if ('object' === typeof args.account && !args.account.id) { args.account.id = args.accountId || args.email || ''; }
|
|
|
|
disagreeTos = (!args.agreeTos && 'undefined' !== typeof args.agreeTos);
|
|
if (!args.email || disagreeTos || (parseInt(args.rsaKeySize, 10) < 2048)) {
|
|
err = new Error(
|
|
"In order to register an account both 'email' and 'agreeTos' must be present"
|
|
+ " and 'rsaKeySize' must be 2048 or greater."
|
|
);
|
|
err.code = 'E_ARGS';
|
|
return Promise.reject(err);
|
|
}
|
|
|
|
return utils.testEmail(args.email).then(function () {
|
|
if (args.account && args.account.privkey && (args.account.privkey.jwk || args.account.privkey.pem)) {
|
|
// TODO import jwk or pem and return it here
|
|
console.warn("TODO: implement accounts.checkKeypairAsync skipping");
|
|
}
|
|
var accountKeypair;
|
|
var newAccountKeypair = true;
|
|
var promise = gl.store.accounts.checkKeypairAsync(args).then(function (keypair) {
|
|
if (keypair) {
|
|
// TODO keypairs
|
|
newAccountKeypair = false;
|
|
accountKeypair = RSA.import(keypair);
|
|
return;
|
|
}
|
|
|
|
if (args.accountKeypair) {
|
|
// TODO keypairs
|
|
accountKeypair = RSA.import(args.accountKeypair);
|
|
return;
|
|
}
|
|
|
|
var keypairOpts = { bitlen: args.rsaKeySize, exp: 65537, public: true, pem: true };
|
|
// TODO keypairs
|
|
return (args.generateKeypair||RSA.generateKeypairAsync)(keypairOpts).then(function (keypair) {
|
|
keypair.privateKeyPem = RSA.exportPrivatePem(keypair);
|
|
keypair.publicKeyPem = RSA.exportPublicPem(keypair);
|
|
keypair.privateKeyJwk = RSA.exportPrivateJwk(keypair);
|
|
accountKeypair = keypair;
|
|
});
|
|
}).then(function () {
|
|
return accountKeypair;
|
|
});
|
|
|
|
return promise.then(function (keypair) {
|
|
// Note: the ACME urls are always fetched fresh on purpose
|
|
// TODO acme-v2/nocompat
|
|
return core.getAcmeUrlsAsync(args).then(function (urls) {
|
|
args._acmeUrls = urls;
|
|
|
|
// TODO acme-v2/nocompat
|
|
return gl.acme.registerNewAccountAsync({
|
|
email: args.email
|
|
, newRegUrl: args._acmeUrls.newReg
|
|
, newAuthzUrl: args._acmeUrls.newAuthz
|
|
, agreeToTerms: function (tosUrl, agreeCb) {
|
|
if (true === args.agreeTos || tosUrl === args.agreeTos || tosUrl === gl.agreeToTerms) {
|
|
agreeCb(null, tosUrl);
|
|
return;
|
|
}
|
|
|
|
// args.email = email; // already there
|
|
// args.domains = domains // already there
|
|
args.tosUrl = tosUrl;
|
|
gl.agreeToTerms(args, agreeCb);
|
|
}
|
|
, accountKeypair: keypair
|
|
|
|
, debug: gl.debug || args.debug
|
|
}).then(function (receipt) {
|
|
var reg = {
|
|
keypair: keypair
|
|
, receipt: receipt
|
|
, kid: receipt && receipt.key && (receipt.key.kid || receipt.kid)
|
|
, email: args.email
|
|
, newRegUrl: args._acmeUrls.newReg
|
|
, newAuthzUrl: args._acmeUrls.newAuthz
|
|
};
|
|
|
|
var accountKeypairPromise;
|
|
args.keypair = keypair;
|
|
args.receipt = receipt;
|
|
if (newAccountKeypair) {
|
|
accountKeypairPromise = gl.store.accounts.setKeypairAsync(args, keypair);
|
|
}
|
|
return Promise.resolve(accountKeypairPromise).then(function () {
|
|
// TODO move templating of arguments to right here?
|
|
if (!gl.store.accounts.setAsync) { return Promise.resolve({ keypair: keypair }); }
|
|
return gl.store.accounts.setAsync(args, reg).then(function (account) {
|
|
if (account && 'object' !== typeof account) {
|
|
throw new Error("store.accounts.setAsync should either return 'null' or an object with at least a string 'id'");
|
|
}
|
|
if (!account) { account = {}; }
|
|
account.keypair = keypair;
|
|
return account;
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
// Accounts
|
|
// (only used for keypair)
|
|
, getAsync: function (args) {
|
|
var accountPromise = null;
|
|
if (gl.store.accounts.checkAsync) {
|
|
accountPromise = core.accounts.checkAsync(args);
|
|
}
|
|
return Promise.resolve(accountPromise).then(function (account) {
|
|
if (!account) { return core.accounts.registerAsync(args); }
|
|
if (account.keypair) { return account; }
|
|
|
|
if (!args.account) { args.account = {}; }
|
|
if ('object' === typeof args.account && !args.account.id) { args.account.id = args.accountId || args.email || ''; }
|
|
var copy = utils.merge(args, gl);
|
|
args = utils.tplCopy(copy);
|
|
return gl.store.accounts.checkKeypairAsync(args).then(function (keypair) {
|
|
if (keypair) { return { keypair: keypair }; }
|
|
return core.accounts.registerAsync(args);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Accounts
|
|
, checkAsync: function (args) {
|
|
var requiredArgs = ['accountId', 'email', 'domains', 'domain'];
|
|
if (!(args.account && (args.account.id || args.account.kid))
|
|
&& !requiredArgs.some(function (key) { return -1 !== Object.keys(args).indexOf(key); })) {
|
|
return Promise.reject(new Error(
|
|
"In order to register or retrieve an account one of '" + requiredArgs.join("', '") + "' must be present"
|
|
));
|
|
}
|
|
|
|
var copy = utils.merge(args, gl);
|
|
args = utils.tplCopy(copy);
|
|
if (!args.account) { args.account = {}; }
|
|
if ('object' === typeof args.account && !args.account.id) { args.account.id = args.accountId || args.email || ''; }
|
|
|
|
// we can re-register the same account until we're blue in the face and it's all the same
|
|
// of course, we can also skip the lookup if we do store the account, but whatever
|
|
if (!gl.store.accounts.checkAsync) { return Promise.resolve(null); }
|
|
return gl.store.accounts.checkAsync(args).then(function (account) {
|
|
|
|
if (!account) {
|
|
return null;
|
|
}
|
|
|
|
args.account = account;
|
|
args.accountId = account.id;
|
|
|
|
return account;
|
|
});
|
|
}
|
|
}
|
|
|
|
, certificates: {
|
|
// Certificates
|
|
registerAsync: function (args) {
|
|
var err;
|
|
var challengeDefaults = gl['_challengeOpts_' + (args.challengeType || gl.challengeType)] || {};
|
|
var copy = utils.merge(args, challengeDefaults || {});
|
|
copy = utils.merge(copy, gl);
|
|
if (!copy.subject) { copy.subject = copy.domains[0]; }
|
|
if (!copy.domain) { copy.domain = copy.domains[0]; }
|
|
args = utils.tplCopy(copy);
|
|
|
|
if (!Array.isArray(args.domains)) {
|
|
return Promise.reject(new Error('args.domains should be an array of domains'));
|
|
}
|
|
//if (-1 === args.domains.indexOf(args.subject)) // TODO relax the constraint once acme-v2 handles subject?
|
|
if (args.subject !== args.domains[0]) {
|
|
console.warn("The certificate's subject (primary domain) should be first in the list of opts.domains");
|
|
console.warn('\topts.subject: (set by you approveDomains(), falling back to opts.domain) ' + args.subject);
|
|
console.warn('\topts.domain: (set by SNICallback()) ' + args.domain);
|
|
console.warn('\topts.domains: (set by you in approveDomains()) ' + args.domains.join(','));
|
|
console.warn("Updating your code will prevent weird, random, hard-to-repro bugs during renewals");
|
|
console.warn("(also this will be required in the next major version of greenlock)");
|
|
//return Promise.reject(new Error('certificate subject (primary domain) must be the first in opts.domains'));
|
|
}
|
|
if (!(args.domains.length && args.domains.every(utils.isValidDomain))) {
|
|
// NOTE: this library can't assume to handle the http loopback
|
|
// (or dns-01 validation may be used)
|
|
// so we do not check dns records or attempt a loopback here
|
|
err = new Error("invalid domain name(s): '(" + args.subject + ') ' + args.domains.join(',') + "'");
|
|
err.code = "INVALID_DOMAIN";
|
|
return Promise.reject(err);
|
|
}
|
|
|
|
// If a previous request to (re)register a certificate is already underway we need
|
|
// to return the same promise created before rather than registering things twice.
|
|
// I'm not 100% sure how to properly handle the case where someone registers domain
|
|
// lists with some but not all elements common, nor am I sure that's even a case that
|
|
// is allowed to happen anyway. But for now we act like the list is completely the
|
|
// same if any elements are the same.
|
|
var promise;
|
|
args.domains.some(function (name) {
|
|
if (pendingRegistrations.hasOwnProperty(name)) {
|
|
promise = pendingRegistrations[name];
|
|
return true;
|
|
}
|
|
});
|
|
if (promise) {
|
|
return promise;
|
|
}
|
|
|
|
promise = core.certificates._runRegistration(args);
|
|
|
|
// Now that the registration is actually underway we need to make sure any subsequent
|
|
// registration attempts return the same promise until it is completed (but not after
|
|
// it is completed).
|
|
args.domains.forEach(function (name) {
|
|
pendingRegistrations[name] = promise;
|
|
});
|
|
function clearPending() {
|
|
args.domains.forEach(function (name) {
|
|
delete pendingRegistrations[name];
|
|
});
|
|
}
|
|
promise.then(clearPending, clearPending);
|
|
|
|
return promise;
|
|
}
|
|
, _runRegistration: function (args) {
|
|
// TODO renewal cb
|
|
// accountId and or email
|
|
return core.accounts.getAsync(args).then(function (account) {
|
|
args.account = account;
|
|
|
|
|
|
if (args.certificate && args.certificate.privkey && (args.certificate.privkey.jwk || args.certificate.privkey.pem)) {
|
|
// TODO import jwk or pem and return it here
|
|
console.warn("TODO: implement certificates.checkKeypairAsync skipping");
|
|
}
|
|
var domainKeypair;
|
|
var newDomainKeypair = true;
|
|
// This has been done in the getAsync already, so we skip it here
|
|
// if approveDomains doesn't set subject, we set it here
|
|
//args.subject = args.subject || args.domains[0];
|
|
var promise = gl.store.certificates.checkKeypairAsync(args).then(function (keypair) {
|
|
if (keypair) {
|
|
domainKeypair = RSA.import(keypair);
|
|
newDomainKeypair = false;
|
|
return;
|
|
}
|
|
|
|
if (args.domainKeypair) {
|
|
domainKeypair = RSA.import(args.domainKeypair);
|
|
return;
|
|
}
|
|
|
|
var keypairOpts = { bitlen: args.rsaKeySize, exp: 65537, public: true, pem: true };
|
|
return (args.generateKeypair||RSA.generateKeypairAsync)(keypairOpts).then(function (keypair) {
|
|
keypair.privateKeyPem = RSA.exportPrivatePem(keypair);
|
|
keypair.publicKeyPem = RSA.exportPublicPem(keypair);
|
|
keypair.privateKeyJwk = RSA.exportPrivateJwk(keypair);
|
|
domainKeypair = keypair;
|
|
});
|
|
}).then(function () {
|
|
return domainKeypair;
|
|
});
|
|
|
|
return promise.then(function (domainKeypair) {
|
|
args.domainKeypair = domainKeypair;
|
|
//args.registration = domainKey;
|
|
|
|
// Note: the ACME urls are always fetched fresh on purpose
|
|
// TODO is this the right place for this?
|
|
return core.getAcmeUrlsAsync(args).then(function (urls) {
|
|
args._acmeUrls = urls;
|
|
|
|
var certReq = {
|
|
debug: args.debug || gl.debug
|
|
|
|
, newAuthzUrl: args._acmeUrls.newAuthz
|
|
, newCertUrl: args._acmeUrls.newCert
|
|
|
|
, accountKeypair: RSA.import(account.keypair)
|
|
, domainKeypair: domainKeypair
|
|
, subject: args.subject // TODO handle this in acme-v2
|
|
, domains: args.domains
|
|
, challengeTypes: Object.keys(args.challenges)
|
|
};
|
|
|
|
//
|
|
// IMPORTANT
|
|
//
|
|
// setChallenge and removeChallenge are handed defaults
|
|
// instead of args because getChallenge does not have
|
|
// access to args
|
|
// (args is per-request, defaults is per instance)
|
|
//
|
|
// Each of these fires individually for each domain,
|
|
// even though the certificate on the whole may have many domains
|
|
//
|
|
certReq.setChallenge = function (challenge, done) {
|
|
log(args.debug, "setChallenge called for '" + challenge.altname + "'");
|
|
// NOTE: First arg takes precedence
|
|
var copy = utils.merge({ domains: [challenge.altname] }, args);
|
|
copy = utils.merge(copy, gl);
|
|
utils.tplCopy(copy);
|
|
copy.challenge = challenge;
|
|
|
|
if (1 === copy.challenges[challenge.type].set.length) {
|
|
copy.challenges[challenge.type].set(copy).then(function (result) {
|
|
done(null, result);
|
|
}).catch(done);
|
|
} else if (2 === copy.challenges[challenge.type].set.length) {
|
|
copy.challenges[challenge.type].set(copy, done);
|
|
} else {
|
|
Object.keys(challenge).forEach(function (key) {
|
|
done[key] = challenge[key];
|
|
});
|
|
// regression bugfix for le-challenge-cloudflare
|
|
// (_acme-challege => _greenlock-dryrun-XXXX)
|
|
copy.acmePrefix = (challenge.dnsHost||'').replace(/\.*/, '') || copy.acmePrefix;
|
|
copy.challenges[challenge.type].set(copy, challenge.altname, challenge.token, challenge.keyAuthorization, done);
|
|
}
|
|
};
|
|
certReq.removeChallenge = function (challenge, done) {
|
|
log(args.debug, "removeChallenge called for '" + challenge.altname + "'");
|
|
var copy = utils.merge({ domains: [challenge.altname] }, args);
|
|
copy = utils.merge(copy, gl);
|
|
utils.tplCopy(copy);
|
|
copy.challenge = challenge;
|
|
|
|
if (1 === copy.challenges[challenge.type].remove.length) {
|
|
copy.challenges[challenge.type].remove(copy).then(function (result) {
|
|
done(null, result);
|
|
}).catch(done);
|
|
} else if (2 === copy.challenges[challenge.type].remove.length) {
|
|
copy.challenges[challenge.type].remove(copy, done);
|
|
} else {
|
|
Object.keys(challenge).forEach(function (key) {
|
|
done[key] = challenge[key];
|
|
});
|
|
copy.challenges[challenge.type].remove(copy, challenge.altname, challenge.token, done);
|
|
}
|
|
};
|
|
certReq.getZones = function (challenge) {
|
|
var copy = utils.merge({ dnsHosts: args.domains.map(function (x) { return 'xxxx.' + x; }) }, args);
|
|
copy = utils.merge(copy, gl);
|
|
utils.tplCopy(copy);
|
|
copy.challenge = challenge;
|
|
|
|
if (!copy.challenges[challenge.type] || ('function' !== typeof copy.challenges[challenge.type].zones)) {
|
|
// may not be available, that's fine.
|
|
return Promise.resolve([]);
|
|
}
|
|
|
|
return copy.challenges[challenge.type].zones(copy);
|
|
};
|
|
|
|
log(args.debug, 'calling greenlock.acme.getCertificateAsync', certReq.subject, certReq.domains);
|
|
|
|
// TODO acme-v2/nocompat
|
|
return gl.acme.getCertificateAsync(certReq).then(utils.attachCertInfo);
|
|
});
|
|
}).then(function (results) {
|
|
//var requested = {};
|
|
//var issued = {};
|
|
// { cert, chain, privkey /*TODO, subject, altnames, issuedAt, expiresAt */ }
|
|
|
|
// args.certs.privkey = RSA.exportPrivatePem(options.domainKeypair);
|
|
args.certs = results;
|
|
// args.pems is deprecated
|
|
args.pems = results;
|
|
// This has been done in the getAsync already, so we skip it here
|
|
// if approveDomains doesn't set subject, we set it here
|
|
//args.subject = args.subject || args.domains[0];
|
|
var promise;
|
|
if (newDomainKeypair) {
|
|
args.keypair = domainKeypair;
|
|
promise = gl.store.certificates.setKeypairAsync(args, domainKeypair);
|
|
}
|
|
return Promise.resolve(promise).then(function () {
|
|
return gl.store.certificates.setAsync(args).then(function () {
|
|
return results;
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
// Certificates
|
|
, renewAsync: function (args, certs) {
|
|
var renewableAt = core.certificates._getRenewableAt(args, certs);
|
|
var err;
|
|
//var halfLife = (certs.expiresAt - certs.issuedAt) / 2;
|
|
//var renewable = (Date.now() - certs.issuedAt) > halfLife;
|
|
|
|
log(args.debug, "(Renew) Expires At", new Date(certs.expiresAt).toISOString());
|
|
log(args.debug, "(Renew) Renewable At", new Date(renewableAt).toISOString());
|
|
|
|
if (!args.duplicate && Date.now() < renewableAt) {
|
|
err = new Error(
|
|
"[ERROR] Certificate issued at '"
|
|
+ new Date(certs.issuedAt).toISOString() + "' and expires at '"
|
|
+ new Date(certs.expiresAt).toISOString() + "'. Ignoring renewal attempt until '"
|
|
+ new Date(renewableAt).toISOString() + "'. Set { duplicate: true } to force."
|
|
);
|
|
err.code = 'E_NOT_RENEWABLE';
|
|
return Promise.reject(err);
|
|
}
|
|
|
|
// Either the cert has entered its renewal period
|
|
// or we're forcing a refresh via 'dupliate: true'
|
|
log(args.debug, "Renewing!");
|
|
|
|
if (!args.domains || !args.domains.length) {
|
|
args.domains = args.servernames || [certs.subject].concat(certs.altnames);
|
|
}
|
|
|
|
return core.certificates.registerAsync(args);
|
|
}
|
|
// Certificates
|
|
, _isRenewable: function (args, certs) {
|
|
var renewableAt = core.certificates._getRenewableAt(args, certs);
|
|
|
|
log(args.debug, "Check Expires At", new Date(certs.expiresAt).toISOString());
|
|
log(args.debug, "Check Renewable At", new Date(renewableAt).toISOString());
|
|
|
|
if (args.duplicate || Date.now() >= renewableAt) {
|
|
log(args.debug, "certificates are renewable");
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
, _getRenewableAt: function (args, certs) {
|
|
return certs.expiresAt - (args.renewWithin || gl.renewWithin);
|
|
}
|
|
, checkAsync: function (args) {
|
|
var copy = utils.merge(args, gl);
|
|
// if approveDomains doesn't set subject, we set it here
|
|
if (!(copy.domains && copy.domains.length)) { copy.domains = [ copy.subject || copy.domain ].filter(Boolean); }
|
|
if (!copy.subject) { copy.subject = copy.domains[0]; }
|
|
if (!copy.domain) { copy.domain = copy.domains[0]; }
|
|
args = utils.tplCopy(copy);
|
|
|
|
// returns pems
|
|
return gl.store.certificates.checkAsync(args).then(function (cert) {
|
|
if (!cert) { log(args.debug, 'checkAsync failed to find certificates'); return null; }
|
|
|
|
cert = utils.attachCertInfo(cert);
|
|
if (utils.certHasDomain(cert, args.domain)) {
|
|
log(args.debug, 'checkAsync found existing certificates');
|
|
|
|
if (cert.privkey) {
|
|
return cert;
|
|
} else {
|
|
return gl.store.certificates.checkKeypairAsync(args).then(function (keypair) {
|
|
cert.privkey = keypair.privateKeyPem || RSA.exportPrivatePem(keypair);
|
|
return cert;
|
|
});
|
|
}
|
|
}
|
|
log(args.debug, 'checkAsync found mismatched / incomplete certificates');
|
|
});
|
|
}
|
|
// Certificates
|
|
, getAsync: function (args) {
|
|
var copy = utils.merge(args, gl);
|
|
// if approveDomains doesn't set subject, we set it here
|
|
if (!(copy.domains && copy.domains.length)) { copy.domains = [ copy.subject || copy.domain ].filter(Boolean); }
|
|
if (!copy.subject) { copy.subject = copy.domains[0]; }
|
|
if (!copy.domain) { copy.domain = copy.domains[0]; }
|
|
args = utils.tplCopy(copy);
|
|
|
|
if (args.certificate && args.certificate.privkey && args.certificate.cert && args.certificate.chain) {
|
|
// TODO skip fetching a certificate if it's fetched during approveDomains
|
|
console.warn("TODO: implement certificates.checkAsync skipping");
|
|
}
|
|
return core.certificates.checkAsync(args).then(function (certs) {
|
|
if (certs) { certs = utils.attachCertInfo(certs); }
|
|
if (!certs || !utils.certHasDomain(certs, args.domain)) {
|
|
// There is no cert available
|
|
if (false !== args.securityUpdates && !args._communityMemberAdded) {
|
|
try {
|
|
// We will notify all greenlock users of mandatory and security updates
|
|
// We'll keep track of versions and os so we can make sure things work well
|
|
// { name, version, email, domains, action, communityMember, telemetry }
|
|
require('./community').add({
|
|
name: args._communityPackage
|
|
, version: args._communityPackageVersion
|
|
, email: args.email
|
|
, domains: args.domains || args.servernames
|
|
, action: 'reg'
|
|
, communityMember: args.communityMember
|
|
, telemetry: args.telemetry
|
|
});
|
|
} catch(e) { /* ignore */ }
|
|
args._communityMemberAdded = true;
|
|
}
|
|
return core.certificates.registerAsync(args);
|
|
}
|
|
|
|
if (core.certificates._isRenewable(args, certs)) {
|
|
// it's time to renew the available cert
|
|
if (false !== args.securityUpdates && !args._communityMemberAdded) {
|
|
try {
|
|
// We will notify all greenlock users of mandatory and security updates
|
|
// We'll keep track of versions and os so we can make sure things work well
|
|
// { name, version, email, domains, action, communityMember, telemetry }
|
|
require('./community').add({
|
|
name: args._communityPackage
|
|
, version: args._communityPackageVersion
|
|
, email: args.email
|
|
, domains: args.domains || args.servernames
|
|
, action: 'renew'
|
|
, communityMember: args.communityMember
|
|
, telemetry: args.telemetry
|
|
});
|
|
} catch(e) { /* ignore */ }
|
|
args._communityMemberAdded = true;
|
|
}
|
|
certs.renewing = core.certificates.renewAsync(args, certs);
|
|
if (args.waitForRenewal) {
|
|
return certs.renewing;
|
|
}
|
|
}
|
|
|
|
// return existing unexpired (although potentially stale) certificates when available
|
|
// there will be an additional .renewing property if the certs are being asynchronously renewed
|
|
return certs;
|
|
}).then(function (results) {
|
|
// returns pems
|
|
return results;
|
|
});
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
return core;
|
|
};
|