mirror of https://github.com/certd/certd
🔱: [acme] sync upgrade with 10 commits [trident-sync]
Bump v5.2.0 - package.json Bump v5.2.0 yarn -> npm CHANGELOG and tests for #76 Fix tests Update auto.js: wait for all challenge promises before exit Fixes #75 CHANGELOG and tests for #66 Fix lint errors Allow self-signed or invalid certificate when evaluating verifyHttpChallengepull/29/head
parent
18865f0931
commit
08c1f338d5
|
@ -4,9 +4,14 @@ version: 2.1
|
|||
commands:
|
||||
pre:
|
||||
steps:
|
||||
- run:
|
||||
name: Setup environment
|
||||
command: |
|
||||
echo 'export FORCE_COLOR=1' >> $BASH_ENV
|
||||
echo 'export NPM_CONFIG_COLOR="always"' >> $BASH_ENV
|
||||
|
||||
- run: node --version
|
||||
- run: npm --version
|
||||
- run: yarn --version
|
||||
- checkout
|
||||
|
||||
enable-eab:
|
||||
|
@ -105,13 +110,13 @@ commands:
|
|||
|
||||
test:
|
||||
steps:
|
||||
- run: yarn --color
|
||||
- run: yarn run lint --color
|
||||
- run: yarn run lint-types
|
||||
- run: yarn run build-docs
|
||||
- run: npm i
|
||||
- run: npm run lint
|
||||
- run: npm run lint-types
|
||||
- run: npm run build-docs
|
||||
|
||||
- run:
|
||||
command: yarn run test --color
|
||||
command: npm run test
|
||||
environment:
|
||||
ACME_DOMAIN_NAME: test.example.com
|
||||
ACME_CHALLTESTSRV_URL: http://127.0.0.1:8055
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
.vscode/
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
ignore-engines true
|
||||
ignore-optional true
|
|
@ -1,5 +1,10 @@
|
|||
# Changelog
|
||||
|
||||
## v5.2.0 (2024-01-22)
|
||||
|
||||
* `fixed` Allow self-signed or invalid certs when validating `http-01` challenges that redirect to HTTPS - [#65](https://github.com/publishlab/node-acme-client/issues/65)
|
||||
* `fixed` Wait for all challenge promises to settle before rejecting `client.auto()` - [#75](https://github.com/publishlab/node-acme-client/issues/75)
|
||||
|
||||
## v5.1.0 (2024-01-20)
|
||||
|
||||
* `fixed` Upgrade `jsrsasign@11.0.0` - [GHSA-rh63-9qcf-83gf](https://github.com/kjur/jsrsasign/security/advisories/GHSA-rh63-9qcf-83gf)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "acme-client",
|
||||
"description": "Simple and unopinionated ACME client",
|
||||
"author": "nmorsman",
|
||||
"version": "5.1.0",
|
||||
"version": "5.2.0",
|
||||
"main": "src/index.js",
|
||||
"types": "types/index.d.ts",
|
||||
"license": "MIT",
|
||||
|
|
|
@ -165,8 +165,19 @@ module.exports = async function(client, userOpts) {
|
|||
}
|
||||
});
|
||||
|
||||
log('[auto] Waiting for challenge valid status');
|
||||
await Promise.all(challengePromises);
|
||||
|
||||
/**
|
||||
* Wait for all challenge promises to settle
|
||||
*/
|
||||
|
||||
try {
|
||||
log('[auto] Waiting for challenge valid status');
|
||||
await Promise.all(challengePromises);
|
||||
}
|
||||
catch (e) {
|
||||
await Promise.allSettled(challengePromises);
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*/
|
||||
|
||||
const dns = require('dns').promises;
|
||||
const https = require('https');
|
||||
const { log } = require('./logger');
|
||||
const axios = require('./axios');
|
||||
const util = require('./util');
|
||||
|
@ -24,8 +25,11 @@ async function verifyHttpChallenge(authz, challenge, keyAuthorization, suffix =
|
|||
const httpPort = axios.defaults.acmeSettings.httpChallengePort || 80;
|
||||
const challengeUrl = `http://${authz.identifier.value}:${httpPort}${suffix}`;
|
||||
|
||||
/* May redirect to HTTPS with invalid/self-signed cert - https://letsencrypt.org/docs/challenge-types/#http-01-challenge */
|
||||
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
||||
|
||||
log(`Sending HTTP query to ${authz.identifier.value}, suffix: ${suffix}, port: ${httpPort}`);
|
||||
const resp = await axios.get(challengeUrl);
|
||||
const resp = await axios.get(challengeUrl, { httpsAgent });
|
||||
const data = (resp.data || '').replace(/\s+$/, '');
|
||||
|
||||
log(`Query successful, HTTP status code: ${resp.status}`);
|
||||
|
|
|
@ -129,7 +129,7 @@ describe('pebble', () => {
|
|||
});
|
||||
|
||||
it('should add challenge response', async () => {
|
||||
const resp = await cts.addHttps01ChallengeResponse(testHttps01ChallengeToken, testHttps01ChallengeContent, testHttps01ChallengeHost, httpsPort);
|
||||
const resp = await cts.addHttps01ChallengeResponse(testHttps01ChallengeToken, testHttps01ChallengeContent, testHttps01ChallengeHost);
|
||||
assert.isTrue(resp);
|
||||
});
|
||||
|
||||
|
|
|
@ -17,6 +17,10 @@ describe('verify', () => {
|
|||
const testHttp01Challenge = { type: 'http-01', status: 'pending', token: uuid() };
|
||||
const testHttp01Key = uuid();
|
||||
|
||||
const testHttps01Authz = { identifier: { type: 'dns', value: `${uuid()}.${domainName}` } };
|
||||
const testHttps01Challenge = { type: 'http-01', status: 'pending', token: uuid() };
|
||||
const testHttps01Key = uuid();
|
||||
|
||||
const testDns01Authz = { identifier: { type: 'dns', value: `${uuid()}.${domainName}` } };
|
||||
const testDns01Challenge = { type: 'dns-01', status: 'pending', token: uuid() };
|
||||
const testDns01Key = uuid();
|
||||
|
@ -74,6 +78,27 @@ describe('verify', () => {
|
|||
});
|
||||
|
||||
|
||||
/**
|
||||
* https-01
|
||||
*/
|
||||
|
||||
describe('https-01', () => {
|
||||
it('should reject challenge', async () => {
|
||||
await assert.isRejected(verify['http-01'](testHttps01Authz, testHttps01Challenge, testHttps01Key));
|
||||
});
|
||||
|
||||
it('should mock challenge response', async () => {
|
||||
const resp = await cts.addHttps01ChallengeResponse(testHttps01Challenge.token, testHttps01Key, testHttps01Authz.identifier.value);
|
||||
assert.isTrue(resp);
|
||||
});
|
||||
|
||||
it('should verify challenge', async () => {
|
||||
const resp = await verify['http-01'](testHttps01Authz, testHttps01Challenge, testHttps01Key);
|
||||
assert.isTrue(resp);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* dns-01
|
||||
*/
|
||||
|
|
|
@ -32,6 +32,7 @@ if (capEabEnabled && process.env.ACME_EAB_KID && process.env.ACME_EAB_HMAC_KEY)
|
|||
describe('client.auto', () => {
|
||||
const testDomain = `${uuid()}.${domainName}`;
|
||||
const testHttpDomain = `${uuid()}.${domainName}`;
|
||||
const testHttpsDomain = `${uuid()}.${domainName}`;
|
||||
const testDnsDomain = `${uuid()}.${domainName}`;
|
||||
const testWildcardDomain = `${uuid()}.${domainName}`;
|
||||
|
||||
|
@ -178,6 +179,38 @@ describe('client.auto', () => {
|
|||
assert.isString(cert);
|
||||
});
|
||||
|
||||
it('should settle all challenges before rejecting', async () => {
|
||||
const results = [];
|
||||
const [, csr] = await acme.crypto.createCsr({
|
||||
commonName: `${uuid()}.${domainName}`,
|
||||
altNames: [
|
||||
`${uuid()}.${domainName}`,
|
||||
`${uuid()}.${domainName}`,
|
||||
`${uuid()}.${domainName}`,
|
||||
`${uuid()}.${domainName}`
|
||||
]
|
||||
}, await createKeyFn());
|
||||
|
||||
await assert.isRejected(testClient.auto({
|
||||
csr,
|
||||
termsOfServiceAgreed: true,
|
||||
challengeCreateFn: async (...args) => {
|
||||
if ([0, 1, 2].includes(results.length)) {
|
||||
results.push(false);
|
||||
throw new Error('oops');
|
||||
}
|
||||
|
||||
await new Promise((resolve) => { setTimeout(resolve, 500); });
|
||||
results.push(true);
|
||||
return cts.challengeCreateFn(...args);
|
||||
},
|
||||
challengeRemoveFn: cts.challengeRemoveFn
|
||||
}));
|
||||
|
||||
assert.strictEqual(results.length, 5);
|
||||
assert.deepStrictEqual(results, [false, false, false, true, true]);
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Order certificates
|
||||
|
@ -215,6 +248,22 @@ describe('client.auto', () => {
|
|||
assert.isString(cert);
|
||||
});
|
||||
|
||||
it('should order certificate using https-01', async () => {
|
||||
const [, csr] = await acme.crypto.createCsr({
|
||||
commonName: testHttpsDomain
|
||||
}, await createKeyFn());
|
||||
|
||||
const cert = await testClient.auto({
|
||||
csr,
|
||||
termsOfServiceAgreed: true,
|
||||
challengeCreateFn: cts.assertHttpsChallengeCreateFn,
|
||||
challengeRemoveFn: cts.challengeRemoveFn,
|
||||
challengePriority: ['http-01']
|
||||
});
|
||||
|
||||
assert.isString(cert);
|
||||
});
|
||||
|
||||
it('should order certificate using dns-01', async () => {
|
||||
const [, csr] = await acme.crypto.createCsr({
|
||||
commonName: testDnsDomain
|
||||
|
|
|
@ -6,6 +6,7 @@ const { assert } = require('chai');
|
|||
const axios = require('./../src/axios');
|
||||
|
||||
const apiBaseUrl = process.env.ACME_CHALLTESTSRV_URL || null;
|
||||
const httpsPort = axios.defaults.acmeSettings.httpsChallengePort || 443;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -50,11 +51,11 @@ async function addHttp01ChallengeResponse(token, content) {
|
|||
return request('add-http01', { token, content });
|
||||
}
|
||||
|
||||
async function addHttps01ChallengeResponse(token, content, targetHostname, targetPort = 443) {
|
||||
async function addHttps01ChallengeResponse(token, content, targetHostname) {
|
||||
await addHttp01ChallengeResponse(token, content);
|
||||
return request('add-redirect', {
|
||||
path: `/.well-known/acme-challenge/${token}`,
|
||||
targetURL: `https://${targetHostname}:${targetPort}/.well-known/acme-challenge/${token}`
|
||||
targetURL: `https://${targetHostname}:${httpsPort}/.well-known/acme-challenge/${token}`
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -76,6 +77,11 @@ async function assertHttpChallengeCreateFn(authz, challenge, keyAuthorization) {
|
|||
return addHttp01ChallengeResponse(challenge.token, keyAuthorization);
|
||||
}
|
||||
|
||||
async function assertHttpsChallengeCreateFn(authz, challenge, keyAuthorization) {
|
||||
assert.strictEqual(challenge.type, 'http-01');
|
||||
return addHttps01ChallengeResponse(challenge.token, keyAuthorization, authz.identifier.value);
|
||||
}
|
||||
|
||||
async function assertDnsChallengeCreateFn(authz, challenge, keyAuthorization) {
|
||||
assert.strictEqual(challenge.type, 'dns-01');
|
||||
return addDns01ChallengeResponse(`_acme-challenge.${authz.identifier.value}.`, keyAuthorization);
|
||||
|
@ -98,5 +104,6 @@ exports.challengeNoopFn = async () => true;
|
|||
exports.challengeThrowFn = async () => { throw new Error('oops'); };
|
||||
|
||||
exports.assertHttpChallengeCreateFn = assertHttpChallengeCreateFn;
|
||||
exports.assertHttpsChallengeCreateFn = assertHttpsChallengeCreateFn;
|
||||
exports.assertDnsChallengeCreateFn = assertDnsChallengeCreateFn;
|
||||
exports.challengeCreateFn = challengeCreateFn;
|
||||
|
|
Loading…
Reference in New Issue