The certificate bed orders the same certificate from a second authority, the catalogue's own
Issue 020 could not tell a Pebble interop detail from a fault of the proxy's. The bed now raises step-ca as the catalogue pins it and orders again through the same proxy and the same challenge path (novox/hq 04-ISSUES/020).
This commit is contained in:
@@ -41,6 +41,14 @@ const ACME = "/var/lib/acme";
|
|||||||
*/
|
*/
|
||||||
const AUTHORITY = "ghcr.io/letsencrypt/pebble:2.5.0";
|
const AUTHORITY = "ghcr.io/letsencrypt/pebble:2.5.0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The second implementation, for the same order (novox/hq 04-ISSUES/020): the certificate
|
||||||
|
* authority the catalogue itself runs, pinned as the catalogue pins it. If the order, the challenge
|
||||||
|
* and the handshake agree here as well as against Pebble, the one thing 020 could not rule out — a
|
||||||
|
* Pebble interop detail — is ruled out; and if they disagree, which side differs is in view.
|
||||||
|
*/
|
||||||
|
const SECOND_AUTHORITY = "smallstep/step-ca@sha256:a2b17872915c193259b75a5474c398326f41bd199f0842093e52cf4182bc8270";
|
||||||
|
|
||||||
let instanceId = "";
|
let instanceId = "";
|
||||||
|
|
||||||
function shellQuote(s: string): string {
|
function shellQuote(s: string): string {
|
||||||
@@ -185,6 +193,59 @@ test("a public name is served with a certificate the mesh did not issue", {
|
|||||||
assert.match(issuer, new RegExp(NAME), `the certificate is not for the name asked for:\n${issuer}`);
|
assert.match(issuer, new RegExp(NAME), `the certificate is not for the name asked for:\n${issuer}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("the same order against a second authority: the catalogue's own certificate authority", {
|
||||||
|
skip, timeout: 900_000,
|
||||||
|
}, async () => {
|
||||||
|
// The proxy that served the first test goes; its cache with it, or the certificate Pebble issued
|
||||||
|
// would be served again and nothing would have been ordered here.
|
||||||
|
await must(`pkill -f mesh-route-proxy || true; sleep 1; mkdir -p ${ACME}/cache2`);
|
||||||
|
|
||||||
|
// The catalogue's authority, as the catalogue runs it: ACME on, listening on its own port, a
|
||||||
|
// root and an intermediate made at first start. It resolves the name through the machine's
|
||||||
|
// resolver, which reads the hosts entry the first test wrote.
|
||||||
|
await must(
|
||||||
|
`docker run -d --name stepca --network host ` +
|
||||||
|
`-e DOCKER_STEPCA_INIT_NAME="Lab CA" -e DOCKER_STEPCA_INIT_DNS_NAMES=localhost,127.0.0.1 ` +
|
||||||
|
`-e DOCKER_STEPCA_INIT_ACME=true -e DOCKER_STEPCA_INIT_REMOTE_MANAGEMENT=false ` +
|
||||||
|
`-e DOCKER_STEPCA_INIT_PASSWORD=lab-only-password ${SECOND_AUTHORITY}`,
|
||||||
|
);
|
||||||
|
let ready = false;
|
||||||
|
for (let i = 0; i < 90 && !ready; i++) {
|
||||||
|
({ ok: ready } = await on(`docker exec stepca test -s /home/step/certs/root_ca.crt && curl -sk https://127.0.0.1:9000/health -o /dev/null`));
|
||||||
|
if (!ready) await new Promise((r) => setTimeout(r, 2000));
|
||||||
|
}
|
||||||
|
assert.ok(ready, `the second authority never came up:\n${(await on(`docker logs stepca 2>&1 | tail -30`)).out}`);
|
||||||
|
await must(`docker exec stepca cat /home/step/certs/root_ca.crt > ${ACME}/stepca-root.pem`);
|
||||||
|
|
||||||
|
await must(
|
||||||
|
`ROUTES=${ACME}/routes.json LISTEN=:80 TLS_LISTEN=:443 ` +
|
||||||
|
`ACME_CACHE=${ACME}/cache2 ` +
|
||||||
|
`ACME_DIRECTORY=https://127.0.0.1:9000/acme/acme/directory ` +
|
||||||
|
`ACME_CA_BUNDLE=${ACME}/stepca-root.pem ` +
|
||||||
|
`nohup /usr/local/bin/mesh-route-proxy >${ACME}/proxy2.log 2>&1 & sleep 3`,
|
||||||
|
);
|
||||||
|
|
||||||
|
let served = { out: "", ok: false };
|
||||||
|
for (let i = 0; i < 40 && !served.ok; i++) {
|
||||||
|
served = await on(`curl -sf --cacert ${ACME}/stepca-root.pem https://${NAME}/ `);
|
||||||
|
if (!served.ok) await new Promise((r) => setTimeout(r, 2000));
|
||||||
|
}
|
||||||
|
if (!served.ok) {
|
||||||
|
const proxyLog = (await on(`cat ${ACME}/proxy2.log`)).out;
|
||||||
|
const authority = (await on(`docker logs stepca 2>&1 | tail -40`)).out;
|
||||||
|
assert.fail(
|
||||||
|
`the name was never served over TLS from the second authority: ${served.out}\n\n` +
|
||||||
|
`── the proxy tried:\n${proxyLog}\n── the authority heard:\n${authority}\n`);
|
||||||
|
}
|
||||||
|
assert.match(served.out, /hello/);
|
||||||
|
const issuer = await must(
|
||||||
|
`echo | openssl s_client -connect ${NAME}:443 -servername ${NAME} 2>/dev/null ` +
|
||||||
|
`| openssl x509 -noout -issuer -subject`,
|
||||||
|
);
|
||||||
|
assert.match(issuer, /Lab CA/, `the certificate was not issued by the second authority:\n${issuer}`);
|
||||||
|
assert.match(issuer, new RegExp(NAME), `the certificate is not for the name asked for:\n${issuer}`);
|
||||||
|
});
|
||||||
|
|
||||||
test("no certificate is ordered for a name the mesh does not route", {
|
test("no certificate is ordered for a name the mesh does not route", {
|
||||||
skip, timeout: 300_000,
|
skip, timeout: 300_000,
|
||||||
}, async () => {
|
}, async () => {
|
||||||
@@ -193,6 +254,6 @@ test("no certificate is ordered for a name the mesh does not route", {
|
|||||||
const { out } = await on(
|
const { out } = await on(
|
||||||
`echo | openssl s_client -connect 127.0.0.1:443 -servername nobody-asked-for-this.example 2>&1 | head -20`,
|
`echo | openssl s_client -connect 127.0.0.1:443 -servername nobody-asked-for-this.example 2>&1 | head -20`,
|
||||||
);
|
);
|
||||||
assert.doesNotMatch(out, /Pebble/i,
|
assert.doesNotMatch(out, /Pebble|Lab CA/i,
|
||||||
`a certificate was obtained for a name nothing routes here:\n${out}`);
|
`a certificate was obtained for a name nothing routes here:\n${out}`);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user