acme: if account key was revoked, generate new account key

the plan was to migrate only specific keys but this allows us the
flexibility to revoke keys after the release (since we have not
gotten response from DO about access to old 1-click images so far).
This commit is contained in:
Girish Ramakrishnan
2021-11-16 22:56:35 -08:00
parent b7c5c99301
commit c4db0d746d
4 changed files with 29 additions and 46 deletions
+27 -6
View File
@@ -9,6 +9,7 @@ exports = module.exports = {
};
const assert = require('assert'),
blobs = require('./blobs.js'),
BoxError = require('./boxerror.js'),
crypto = require('crypto'),
debug = require('debug')('box:cert/acme2'),
@@ -31,7 +32,7 @@ const CA_PROD_DIRECTORY_URL = 'https://acme-v02.api.letsencrypt.org/directory',
function Acme2(options) {
assert.strictEqual(typeof options, 'object');
this.accountKeyPem = options.accountKeyPem; // Buffer
this.accountKeyPem = null; // Buffer .
this.email = options.email;
this.keyId = null;
this.caDirectory = options.prod ? CA_PROD_DIRECTORY_URL : CA_STAGING_DIRECTORY_URL;
@@ -133,18 +134,38 @@ Acme2.prototype.updateContact = async function (registrationUri) {
debug(`updateContact: contact of user updated to ${this.email}`);
};
Acme2.prototype.registerUser = async function () {
async function generateAccountKey() {
const acmeAccountKey = safe.child_process.execSync('openssl genrsa 4096');
if (!acmeAccountKey) throw new BoxError(BoxError.OPENSSL_ERROR, `Could not generate acme account key: ${safe.error.message}`);
return acmeAccountKey;
}
Acme2.prototype.ensureAccount = async function () {
const payload = {
termsOfServiceAgreed: true
};
debug('registerUser: registering user');
debug('ensureAccount: registering user');
this.accountKeyPem = await blobs.get(blobs.ACME_ACCOUNT_KEY);
if (!this.accountKeyPem) {
debug('ensureAccount: generating new account keys');
this.accountKeyPem = await generateAccountKey();
await blobs.set(blobs.ACME_ACCOUNT_KEY, this.accountKeyPem);
}
let result = await this.sendSignedRequest(this.directory.newAccount, JSON.stringify(payload));
if (result.status === 403 && result.body.type === 'urn:ietf:params:acme:error:unauthorized') {
debug(`ensureAccount: key was revoked. ${result.status} ${JSON.stringify(result.body)}. generating new account key`);
this.accountKeyPem = await generateAccountKey();
await blobs.set(blobs.ACME_ACCOUNT_KEY, this.accountKeyPem);
result = await this.sendSignedRequest(this.directory.newAccount, JSON.stringify(payload));
}
const result = await this.sendSignedRequest(this.directory.newAccount, JSON.stringify(payload));
// 200 if already exists. 201 for new accounts
if (result.status !== 200 && result.status !== 201) throw new BoxError(BoxError.EXTERNAL_ERROR, `Failed to register new account. Expecting 200 or 201, got ${result.status} ${JSON.stringify(result.body)}`);
debug(`registerUser: user registered keyid: ${result.headers.location}`);
debug(`ensureAccount: user registered keyid: ${result.headers.location}`);
this.keyId = result.headers.location;
@@ -464,7 +485,7 @@ Acme2.prototype.acmeFlow = async function (hostname, domain, paths) {
const { certFilePath, keyFilePath, csrFilePath, acmeChallengesDir } = paths;
await this.registerUser();
await this.ensureAccount();
const { order, orderUrl } = await this.newOrder(hostname);
for (let i = 0; i < order.authorizations.length; i++) {
-4
View File
@@ -54,10 +54,6 @@ async function clear() {
}
async function generateSecrets() {
const acmeAccountKey = safe.child_process.execSync('openssl genrsa 4096');
if (!acmeAccountKey) throw new BoxError(BoxError.OPENSSL_ERROR, `Could not generate acme account key: ${safe.error.message}`);
await set(exports.ACME_ACCOUNT_KEY, acmeAccountKey);
debug('generateSecrets: generating dhparams.pem');
// https://security.stackexchange.com/questions/95178/diffie-hellman-parameters-still-calculating-after-24-hours
const dhparams = safe.child_process.execSync('openssl dhparam -dsaparam 2048');
+2 -8
View File
@@ -51,8 +51,7 @@ const acme2 = require('./acme2.js'),
shell = require('./shell.js'),
sysinfo = require('./sysinfo.js'),
users = require('./users.js'),
util = require('util'),
_ = require('underscore');
util = require('util');
const NGINX_APPCONFIG_EJS = fs.readFileSync(__dirname + '/nginxconfig.ejs', { encoding: 'utf8' });
const RESTART_SERVICE_CMD = path.join(__dirname, 'scripts/restartservice.sh');
@@ -82,11 +81,6 @@ async function getAcmeApi(domainObject) {
const [error, owner] = await safe(users.getOwner());
apiOptions.email = (error || !owner) ? 'webmaster@cloudron.io' : owner.email; // can error if not activated yet
const accountKeyPem = await blobs.get(blobs.ACME_ACCOUNT_KEY);
if (!accountKeyPem) throw new BoxError(BoxError.NOT_FOUND, 'acme account key not found');
apiOptions.accountKeyPem = accountKeyPem;
return { acmeApi, apiOptions };
}
@@ -412,7 +406,7 @@ async function ensureCertificate(vhost, domain, auditSource) {
debug(`ensureCertificate: ${vhost} cert does not exist`);
}
debug('ensureCertificate: getting certificate for %s with options %j', vhost, _.omit(apiOptions, 'accountKeyPem'));
debug('ensureCertificate: getting certificate for %s with options %j', vhost, apiOptions);
const acmePaths = getAcmeCertificatePathSync(vhost, domainObject);
let [error] = await safe(acmeApi.getCertificate(vhost, domain, acmePaths, apiOptions));