Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 1c25f30

Browse files
authored
Split certificates from NODE_EXTRA_CA_CERTS (#637)
If `NODE_EXTRA_CA_CERTS` contains multiple certificates, they need to be split before being passed to `workerd`. This change fixes that, and adds a test for `NODE_EXTRA_CA_CERTS` handling in general.
1 parent b8c93f1 commit 1c25f30

File tree

2 files changed

+96
-1
lines changed

2 files changed

+96
-1
lines changed

packages/miniflare/src/plugins/core/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,12 @@ if (process.env.NODE_EXTRA_CA_CERTS !== undefined) {
6565
// https://nodejs.org/api/cli.html#node_extra_ca_certsfile
6666
try {
6767
const extra = readFileSync(process.env.NODE_EXTRA_CA_CERTS, "utf8");
68-
trustedCertificates.push(extra);
68+
// Split bundle into individual certificates and add each individually:
69+
// https://github.com/cloudflare/miniflare/pull/587/files#r1271579671
70+
const pemBegin = "-----BEGIN";
71+
for (const cert of extra.split(pemBegin)) {
72+
if (cert.trim() !== "") trustedCertificates.push(pemBegin + cert);
73+
}
6974
} catch {}
7075
}
7176

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import childProcess from "child_process";
2+
import { once } from "events";
3+
import fs from "fs/promises";
4+
import https from "https";
5+
import { AddressInfo } from "net";
6+
import path from "path";
7+
import { text } from "stream/consumers";
8+
import tls from "tls";
9+
import test from "ava";
10+
import stoppable from "stoppable";
11+
import which from "which";
12+
import { useTmp } from "../../test-shared";
13+
14+
const opensslInstalled = which.sync("openssl", { nothrow: true });
15+
const opensslTest = opensslInstalled ? test : test.skip;
16+
opensslTest("NODE_EXTRA_CA_CERTS: loads certificates", async (t) => {
17+
const tmp = await useTmp(t);
18+
19+
// Generate self-signed certificate
20+
childProcess.execSync(
21+
'openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 30 -out cert.pem -subj "/CN=localhost"',
22+
{ cwd: tmp, stdio: "ignore" }
23+
);
24+
const keyPath = path.join(tmp, "key.pem");
25+
const certPath = path.join(tmp, "cert.pem");
26+
const key = await fs.readFile(keyPath);
27+
const cert = await fs.readFile(certPath);
28+
29+
// Start HTTPS server with self-signed certificate
30+
const responseBody = "secure body";
31+
const server = https.createServer({ key, cert }, (req, res) => {
32+
res.end(responseBody);
33+
});
34+
const stoppableServer = stoppable(server, /* grace */ 0);
35+
const url = await new Promise<string>((resolve) => {
36+
server.listen(0, () => {
37+
t.teardown(() => {
38+
return new Promise((resolve, reject) =>
39+
stoppableServer.stop((err) => (err ? reject(err) : resolve()))
40+
);
41+
});
42+
const port = (server.address() as AddressInfo).port;
43+
resolve(`https://localhost:${port}`);
44+
});
45+
});
46+
47+
// Write NODE_EXTRA_CA_CERTS file, with multiple certificates
48+
// (see https://github.com/cloudflare/miniflare/pull/587/files#r1271579671)
49+
const caCertsPath = path.join(tmp, "bundle.pem");
50+
const caCerts = [...tls.rootCertificates, cert];
51+
await fs.writeFile(caCertsPath, caCerts.join("\n"));
52+
53+
// Start Miniflare with NODE_EXTRA_CA_CERTS environment variable
54+
// (cannot use sync process methods here as that would block HTTPS server)
55+
const result = childProcess.spawn(
56+
process.execPath,
57+
[
58+
"-e",
59+
`
60+
const { Miniflare } = require("miniflare");
61+
const mf = new Miniflare({
62+
verbose: true,
63+
modules: true,
64+
script: \`export default {
65+
fetch() {
66+
return fetch(${JSON.stringify(url)});
67+
}
68+
}\`
69+
});
70+
(async () => {
71+
const res = await mf.dispatchFetch("http://placeholder/");
72+
const text = await res.text();
73+
console.log(text);
74+
await mf.dispose();
75+
})();
76+
`,
77+
],
78+
{
79+
stdio: [/* in */ "ignore", /* out */ "pipe", /* error */ "inherit"],
80+
env: { NODE_EXTRA_CA_CERTS: caCertsPath },
81+
}
82+
);
83+
84+
// Check response matches expected
85+
const exitPromise = once(result, "exit");
86+
const resultText = await text(result.stdout);
87+
await exitPromise;
88+
t.is(result.exitCode, 0);
89+
t.is(resultText.trim(), responseBody);
90+
});

0 commit comments

Comments
 (0)