From 18865f0931fcd75bbffffc89a23b745c042b2573 Mon Sep 17 00:00:00 2001 From: GitHub Actions Bot Date: Sun, 21 Jan 2024 19:24:13 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=B1:=20[acme]=20sync=20upgrade=20with?= =?UTF-8?q?=203=20commits=20[trident-sync]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add https-01 challenge test server support Inject CoreDNS into resolv.conf while testing, remove interceptor hack --- .../core/acme-client/.circleci/config.yml | 5 +- packages/core/acme-client/src/axios.js | 2 +- .../core/acme-client/test/00-pebble.spec.js | 79 +++++++++++++++++-- .../core/acme-client/test/10-http.spec.js | 3 - .../core/acme-client/test/challtestsrv.js | 9 +++ packages/core/acme-client/test/setup.js | 56 ++----------- 6 files changed, 90 insertions(+), 64 deletions(-) diff --git a/packages/core/acme-client/.circleci/config.yml b/packages/core/acme-client/.circleci/config.yml index 7c32a0d3..8865b201 100644 --- a/packages/core/acme-client/.circleci/config.yml +++ b/packages/core/acme-client/.circleci/config.yml @@ -99,6 +99,10 @@ commands: command: sudo coredns -p 53 -conf /etc/coredns/Corefile background: true + - run: + name: Use CoreDNS in resolv.conf + command: echo "nameserver 127.0.0.1" | sudo tee /etc/resolv.conf + test: steps: - run: yarn --color @@ -111,7 +115,6 @@ commands: environment: ACME_DOMAIN_NAME: test.example.com ACME_CHALLTESTSRV_URL: http://127.0.0.1:8055 - ACME_DNS_RESOLVER: 127.0.0.1 ACME_TLSALPN_PORT: 5001 ACME_HTTP_PORT: 5002 ACME_HTTPS_PORT: 5003 diff --git a/packages/core/acme-client/src/axios.js b/packages/core/acme-client/src/axios.js index f0cbdef6..c66d3258 100644 --- a/packages/core/acme-client/src/axios.js +++ b/packages/core/acme-client/src/axios.js @@ -18,7 +18,7 @@ instance.defaults.headers.common['User-Agent'] = `node-${pkg.name}/${pkg.version /* Default ACME settings */ instance.defaults.acmeSettings = { httpChallengePort: 80, - bypassCustomDnsResolver: false + httpsChallengePort: 443 }; diff --git a/packages/core/acme-client/test/00-pebble.spec.js b/packages/core/acme-client/test/00-pebble.spec.js index 805421b9..ce99d7a8 100644 --- a/packages/core/acme-client/test/00-pebble.spec.js +++ b/packages/core/acme-client/test/00-pebble.spec.js @@ -4,15 +4,19 @@ const dns = require('dns').promises; const { randomUUID: uuid } = require('crypto'); +const https = require('https'); const { assert } = require('chai'); const cts = require('./challtestsrv'); const axios = require('./../src/axios'); const domainName = process.env.ACME_DOMAIN_NAME || 'example.com'; const httpPort = axios.defaults.acmeSettings.httpChallengePort || 80; +const httpsPort = axios.defaults.acmeSettings.httpsChallengePort || 443; describe('pebble', () => { + const httpsAgent = new https.Agent({ rejectUnauthorized: false }); + const testAHost = `${uuid()}.${domainName}`; const testARecords = ['1.1.1.1', '2.2.2.2']; const testCnameHost = `${uuid()}.${domainName}`; @@ -21,6 +25,11 @@ describe('pebble', () => { const testHttp01ChallengeHost = `${uuid()}.${domainName}`; const testHttp01ChallengeToken = uuid(); const testHttp01ChallengeContent = uuid(); + + const testHttps01ChallengeHost = `${uuid()}.${domainName}`; + const testHttps01ChallengeToken = uuid(); + const testHttps01ChallengeContent = uuid(); + const testDns01ChallengeHost = `_acme-challenge.${uuid()}.${domainName}.`; const testDns01ChallengeValue = uuid(); @@ -79,39 +88,93 @@ describe('pebble', () => { /** - * Challenge response + * HTTP-01 challenge response */ - describe('challenges', () => { - it('should not locate http-01 challenge response', async () => { + describe('http-01', () => { + it('should not locate challenge response', async () => { const resp = await axios.get(`http://${testHttp01ChallengeHost}:${httpPort}/.well-known/acme-challenge/${testHttp01ChallengeToken}`); assert.isString(resp.data); assert.notEqual(resp.data, testHttp01ChallengeContent); }); - it('should add http-01 challenge response', async () => { + it('should add challenge response', async () => { const resp = await cts.addHttp01ChallengeResponse(testHttp01ChallengeToken, testHttp01ChallengeContent); assert.isTrue(resp); }); - it('should locate http-01 challenge response', async () => { + it('should locate challenge response', async () => { const resp = await axios.get(`http://${testHttp01ChallengeHost}:${httpPort}/.well-known/acme-challenge/${testHttp01ChallengeToken}`); assert.isString(resp.data); assert.strictEqual(resp.data, testHttp01ChallengeContent); }); + }); - it('should not locate dns-01 challenge response', async () => { + + /** + * HTTPS-01 challenge response + */ + + describe('https-01', () => { + it('should not locate challenge response', async () => { + const r1 = await axios.get(`http://${testHttps01ChallengeHost}:${httpPort}/.well-known/acme-challenge/${testHttps01ChallengeToken}`, { httpsAgent }); + const r2 = await axios.get(`https://${testHttps01ChallengeHost}:${httpsPort}/.well-known/acme-challenge/${testHttps01ChallengeToken}`, { httpsAgent }); + + [r1, r2].forEach((resp) => { + assert.isString(resp.data); + assert.notEqual(resp.data, testHttps01ChallengeContent); + }); + }); + + it('should add challenge response', async () => { + const resp = await cts.addHttps01ChallengeResponse(testHttps01ChallengeToken, testHttps01ChallengeContent, testHttps01ChallengeHost, httpsPort); + assert.isTrue(resp); + }); + + it('should 302 with self-signed cert', async () => { + /* Assert HTTP 302 */ + const resp = await axios.get(`http://${testHttps01ChallengeHost}:${httpPort}/.well-known/acme-challenge/${testHttps01ChallengeToken}`, { + maxRedirects: 0, + validateStatus: null + }); + + assert.strictEqual(resp.status, 302); + assert.strictEqual(resp.headers.location, `https://${testHttps01ChallengeHost}:${httpsPort}/.well-known/acme-challenge/${testHttps01ChallengeToken}`); + + /* Self-signed cert test */ + await assert.isRejected(axios.get(`https://${testHttps01ChallengeHost}:${httpsPort}/.well-known/acme-challenge/${testHttps01ChallengeToken}`)); + await assert.isFulfilled(axios.get(`https://${testHttps01ChallengeHost}:${httpsPort}/.well-known/acme-challenge/${testHttps01ChallengeToken}`, { httpsAgent })); + }); + + it('should locate challenge response', async () => { + const r1 = await axios.get(`http://${testHttps01ChallengeHost}:${httpPort}/.well-known/acme-challenge/${testHttps01ChallengeToken}`, { httpsAgent }); + const r2 = await axios.get(`https://${testHttps01ChallengeHost}:${httpsPort}/.well-known/acme-challenge/${testHttps01ChallengeToken}`, { httpsAgent }); + + [r1, r2].forEach((resp) => { + assert.isString(resp.data); + assert.strictEqual(resp.data, testHttps01ChallengeContent); + }); + }); + }); + + + /** + * DNS-01 challenge response + */ + + describe('dns-01', () => { + it('should not locate challenge response', async () => { await assert.isRejected(dns.resolveTxt(testDns01ChallengeHost)); }); - it('should add dns-01 challenge response', async () => { + it('should add challenge response', async () => { const resp = await cts.addDns01ChallengeResponse(testDns01ChallengeHost, testDns01ChallengeValue); assert.isTrue(resp); }); - it('should locate dns-01 challenge response', async () => { + it('should locate challenge response', async () => { const resp = await dns.resolveTxt(testDns01ChallengeHost); assert.isArray(resp); diff --git a/packages/core/acme-client/test/10-http.spec.js b/packages/core/acme-client/test/10-http.spec.js index 22cd7b24..a2a54de4 100644 --- a/packages/core/acme-client/test/10-http.spec.js +++ b/packages/core/acme-client/test/10-http.spec.js @@ -26,8 +26,6 @@ describe('http', () => { */ before(() => { - axios.defaults.acmeSettings.bypassCustomDnsResolver = true; - const defaultUaOpts = { reqheaders: { 'User-Agent': defaultUserAgent } }; const customUaOpts = { reqheaders: { 'User-Agent': customUserAgent } }; @@ -43,7 +41,6 @@ describe('http', () => { after(() => { axios.defaults.headers.common['User-Agent'] = defaultUserAgent; - axios.defaults.acmeSettings.bypassCustomDnsResolver = false; }); diff --git a/packages/core/acme-client/test/challtestsrv.js b/packages/core/acme-client/test/challtestsrv.js index 1a372959..a823c304 100644 --- a/packages/core/acme-client/test/challtestsrv.js +++ b/packages/core/acme-client/test/challtestsrv.js @@ -50,11 +50,20 @@ async function addHttp01ChallengeResponse(token, content) { return request('add-http01', { token, content }); } +async function addHttps01ChallengeResponse(token, content, targetHostname, targetPort = 443) { + await addHttp01ChallengeResponse(token, content); + return request('add-redirect', { + path: `/.well-known/acme-challenge/${token}`, + targetURL: `https://${targetHostname}:${targetPort}/.well-known/acme-challenge/${token}` + }); +} + async function addDns01ChallengeResponse(host, value) { return request('set-txt', { host, value }); } exports.addHttp01ChallengeResponse = addHttp01ChallengeResponse; +exports.addHttps01ChallengeResponse = addHttps01ChallengeResponse; exports.addDns01ChallengeResponse = addDns01ChallengeResponse; diff --git a/packages/core/acme-client/test/setup.js b/packages/core/acme-client/test/setup.js index e6f18647..a09f2801 100644 --- a/packages/core/acme-client/test/setup.js +++ b/packages/core/acme-client/test/setup.js @@ -2,10 +2,7 @@ * Setup testing */ -const url = require('url'); -const net = require('net'); const fs = require('fs'); -const dns = require('dns').promises; const chai = require('chai'); const chaiAsPromised = require('chai-as-promised'); const axios = require('./../src/axios'); @@ -19,13 +16,17 @@ chai.use(chaiAsPromised); /** - * HTTP challenge port + * Challenge test server ports */ if (process.env.ACME_HTTP_PORT) { axios.defaults.acmeSettings.httpChallengePort = process.env.ACME_HTTP_PORT; } +if (process.env.ACME_HTTPS_PORT) { + axios.defaults.acmeSettings.httpsChallengePort = process.env.ACME_HTTPS_PORT; +} + /** * External account binding @@ -38,50 +39,3 @@ if (('ACME_CAP_EAB_ENABLED' in process.env) && (process.env.ACME_CAP_EAB_ENABLED process.env.ACME_EAB_KID = kid; process.env.ACME_EAB_HMAC_KEY = hmacKey; } - - -/** - * Custom DNS resolver - */ - -if (process.env.ACME_DNS_RESOLVER) { - dns.setServers([process.env.ACME_DNS_RESOLVER]); - - - /** - * Axios DNS resolver - */ - - axios.interceptors.request.use(async (config) => { - const urlObj = url.parse(config.url); - - /* Bypass */ - if (axios.defaults.acmeSettings.bypassCustomDnsResolver === true) { - return config; - } - - /* Skip IP addresses and localhost */ - if (net.isIP(urlObj.hostname) || (urlObj.hostname === 'localhost')) { - return config; - } - - /* Lookup hostname */ - const result = await dns.resolve4(urlObj.hostname); - - if (!result.length) { - throw new Error(`Unable to lookup address: ${urlObj.hostname}`); - } - - /* Place hostname in header */ - config.headers = config.headers || {}; - config.headers.Host = urlObj.hostname; - - /* Inject address into URL */ - delete urlObj.host; - urlObj.hostname = result[0]; - config.url = url.format(urlObj); - - /* Done */ - return config; - }); -}