mirror of https://github.com/certd/certd
🔱: [acme] sync upgrade with 3 commits [trident-sync]
Add https-01 challenge test server support Inject CoreDNS into resolv.conf while testing, remove interceptor hackpull/29/head
parent
d22a25d260
commit
18865f0931
|
@ -99,6 +99,10 @@ commands:
|
||||||
command: sudo coredns -p 53 -conf /etc/coredns/Corefile
|
command: sudo coredns -p 53 -conf /etc/coredns/Corefile
|
||||||
background: true
|
background: true
|
||||||
|
|
||||||
|
- run:
|
||||||
|
name: Use CoreDNS in resolv.conf
|
||||||
|
command: echo "nameserver 127.0.0.1" | sudo tee /etc/resolv.conf
|
||||||
|
|
||||||
test:
|
test:
|
||||||
steps:
|
steps:
|
||||||
- run: yarn --color
|
- run: yarn --color
|
||||||
|
@ -111,7 +115,6 @@ commands:
|
||||||
environment:
|
environment:
|
||||||
ACME_DOMAIN_NAME: test.example.com
|
ACME_DOMAIN_NAME: test.example.com
|
||||||
ACME_CHALLTESTSRV_URL: http://127.0.0.1:8055
|
ACME_CHALLTESTSRV_URL: http://127.0.0.1:8055
|
||||||
ACME_DNS_RESOLVER: 127.0.0.1
|
|
||||||
ACME_TLSALPN_PORT: 5001
|
ACME_TLSALPN_PORT: 5001
|
||||||
ACME_HTTP_PORT: 5002
|
ACME_HTTP_PORT: 5002
|
||||||
ACME_HTTPS_PORT: 5003
|
ACME_HTTPS_PORT: 5003
|
||||||
|
|
|
@ -18,7 +18,7 @@ instance.defaults.headers.common['User-Agent'] = `node-${pkg.name}/${pkg.version
|
||||||
/* Default ACME settings */
|
/* Default ACME settings */
|
||||||
instance.defaults.acmeSettings = {
|
instance.defaults.acmeSettings = {
|
||||||
httpChallengePort: 80,
|
httpChallengePort: 80,
|
||||||
bypassCustomDnsResolver: false
|
httpsChallengePort: 443
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,15 +4,19 @@
|
||||||
|
|
||||||
const dns = require('dns').promises;
|
const dns = require('dns').promises;
|
||||||
const { randomUUID: uuid } = require('crypto');
|
const { randomUUID: uuid } = require('crypto');
|
||||||
|
const https = require('https');
|
||||||
const { assert } = require('chai');
|
const { assert } = require('chai');
|
||||||
const cts = require('./challtestsrv');
|
const cts = require('./challtestsrv');
|
||||||
const axios = require('./../src/axios');
|
const axios = require('./../src/axios');
|
||||||
|
|
||||||
const domainName = process.env.ACME_DOMAIN_NAME || 'example.com';
|
const domainName = process.env.ACME_DOMAIN_NAME || 'example.com';
|
||||||
const httpPort = axios.defaults.acmeSettings.httpChallengePort || 80;
|
const httpPort = axios.defaults.acmeSettings.httpChallengePort || 80;
|
||||||
|
const httpsPort = axios.defaults.acmeSettings.httpsChallengePort || 443;
|
||||||
|
|
||||||
|
|
||||||
describe('pebble', () => {
|
describe('pebble', () => {
|
||||||
|
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
||||||
|
|
||||||
const testAHost = `${uuid()}.${domainName}`;
|
const testAHost = `${uuid()}.${domainName}`;
|
||||||
const testARecords = ['1.1.1.1', '2.2.2.2'];
|
const testARecords = ['1.1.1.1', '2.2.2.2'];
|
||||||
const testCnameHost = `${uuid()}.${domainName}`;
|
const testCnameHost = `${uuid()}.${domainName}`;
|
||||||
|
@ -21,6 +25,11 @@ describe('pebble', () => {
|
||||||
const testHttp01ChallengeHost = `${uuid()}.${domainName}`;
|
const testHttp01ChallengeHost = `${uuid()}.${domainName}`;
|
||||||
const testHttp01ChallengeToken = uuid();
|
const testHttp01ChallengeToken = uuid();
|
||||||
const testHttp01ChallengeContent = uuid();
|
const testHttp01ChallengeContent = uuid();
|
||||||
|
|
||||||
|
const testHttps01ChallengeHost = `${uuid()}.${domainName}`;
|
||||||
|
const testHttps01ChallengeToken = uuid();
|
||||||
|
const testHttps01ChallengeContent = uuid();
|
||||||
|
|
||||||
const testDns01ChallengeHost = `_acme-challenge.${uuid()}.${domainName}.`;
|
const testDns01ChallengeHost = `_acme-challenge.${uuid()}.${domainName}.`;
|
||||||
const testDns01ChallengeValue = uuid();
|
const testDns01ChallengeValue = uuid();
|
||||||
|
|
||||||
|
@ -79,39 +88,93 @@ describe('pebble', () => {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Challenge response
|
* HTTP-01 challenge response
|
||||||
*/
|
*/
|
||||||
|
|
||||||
describe('challenges', () => {
|
describe('http-01', () => {
|
||||||
it('should not locate http-01 challenge response', async () => {
|
it('should not locate challenge response', async () => {
|
||||||
const resp = await axios.get(`http://${testHttp01ChallengeHost}:${httpPort}/.well-known/acme-challenge/${testHttp01ChallengeToken}`);
|
const resp = await axios.get(`http://${testHttp01ChallengeHost}:${httpPort}/.well-known/acme-challenge/${testHttp01ChallengeToken}`);
|
||||||
|
|
||||||
assert.isString(resp.data);
|
assert.isString(resp.data);
|
||||||
assert.notEqual(resp.data, testHttp01ChallengeContent);
|
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);
|
const resp = await cts.addHttp01ChallengeResponse(testHttp01ChallengeToken, testHttp01ChallengeContent);
|
||||||
assert.isTrue(resp);
|
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}`);
|
const resp = await axios.get(`http://${testHttp01ChallengeHost}:${httpPort}/.well-known/acme-challenge/${testHttp01ChallengeToken}`);
|
||||||
|
|
||||||
assert.isString(resp.data);
|
assert.isString(resp.data);
|
||||||
assert.strictEqual(resp.data, testHttp01ChallengeContent);
|
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));
|
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);
|
const resp = await cts.addDns01ChallengeResponse(testDns01ChallengeHost, testDns01ChallengeValue);
|
||||||
assert.isTrue(resp);
|
assert.isTrue(resp);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should locate dns-01 challenge response', async () => {
|
it('should locate challenge response', async () => {
|
||||||
const resp = await dns.resolveTxt(testDns01ChallengeHost);
|
const resp = await dns.resolveTxt(testDns01ChallengeHost);
|
||||||
|
|
||||||
assert.isArray(resp);
|
assert.isArray(resp);
|
||||||
|
|
|
@ -26,8 +26,6 @@ describe('http', () => {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
before(() => {
|
before(() => {
|
||||||
axios.defaults.acmeSettings.bypassCustomDnsResolver = true;
|
|
||||||
|
|
||||||
const defaultUaOpts = { reqheaders: { 'User-Agent': defaultUserAgent } };
|
const defaultUaOpts = { reqheaders: { 'User-Agent': defaultUserAgent } };
|
||||||
const customUaOpts = { reqheaders: { 'User-Agent': customUserAgent } };
|
const customUaOpts = { reqheaders: { 'User-Agent': customUserAgent } };
|
||||||
|
|
||||||
|
@ -43,7 +41,6 @@ describe('http', () => {
|
||||||
|
|
||||||
after(() => {
|
after(() => {
|
||||||
axios.defaults.headers.common['User-Agent'] = defaultUserAgent;
|
axios.defaults.headers.common['User-Agent'] = defaultUserAgent;
|
||||||
axios.defaults.acmeSettings.bypassCustomDnsResolver = false;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -50,11 +50,20 @@ async function addHttp01ChallengeResponse(token, content) {
|
||||||
return request('add-http01', { 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) {
|
async function addDns01ChallengeResponse(host, value) {
|
||||||
return request('set-txt', { host, value });
|
return request('set-txt', { host, value });
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.addHttp01ChallengeResponse = addHttp01ChallengeResponse;
|
exports.addHttp01ChallengeResponse = addHttp01ChallengeResponse;
|
||||||
|
exports.addHttps01ChallengeResponse = addHttps01ChallengeResponse;
|
||||||
exports.addDns01ChallengeResponse = addDns01ChallengeResponse;
|
exports.addDns01ChallengeResponse = addDns01ChallengeResponse;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,7 @@
|
||||||
* Setup testing
|
* Setup testing
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const url = require('url');
|
|
||||||
const net = require('net');
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const dns = require('dns').promises;
|
|
||||||
const chai = require('chai');
|
const chai = require('chai');
|
||||||
const chaiAsPromised = require('chai-as-promised');
|
const chaiAsPromised = require('chai-as-promised');
|
||||||
const axios = require('./../src/axios');
|
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) {
|
if (process.env.ACME_HTTP_PORT) {
|
||||||
axios.defaults.acmeSettings.httpChallengePort = 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
|
* 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_KID = kid;
|
||||||
process.env.ACME_EAB_HMAC_KEY = hmacKey;
|
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;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue