History

Trust bundled root certificates in workerd outbound TLS

workerd's built-in internet service cannot read the Windows system trust store, so npm installs inside the publish worker failed TLS verification. Declare the internet service explicitly with the runtime's bundled Mozilla roots, surface workerd stderr in startup failures, and fail the bundle when a dependency install warning is present instead of publishing a broken bundle.

Rhys Sullivan committed 4 months ago

Commit bb54fa91 parent 4be58bc2 files · +23 −2Browse files

Unified · Split

2 files
  1. packages/kernel/runtime-workerd-subprocess/src/index.ts +19 −2
  2. packages/plugins/apps/src/pipeline/worker-bundler.ts +4 −0
packages/kernel/runtime-workerd-subprocess/src/index.ts+19−2
@@ -8,6 +8,7 @@ import { createServer as createNetServer, type Server as NetServer } from "node: · 7 unmodified lines
8import type { Readable } from "node:stream";8import type { Readable } from "node:stream";
9import { tmpdir } from "node:os";9import { tmpdir } from "node:os";
10import { dirname, join } from "node:path";10import { dirname, join } from "node:path";
11import { rootCertificates } from "node:tls";
11import { fileURLToPath } from "node:url";12import { fileURLToPath } from "node:url";
12 13
13import {14import {
@@ -303,9 +304,19 @@ const buildConfig = (input: { · 289 unmodified lines
303 return `( name = ${capnpString(name)}, ${field} = embed ${capnpString(name)} )`;304 return `( name = ${capnpString(name)}, ${field} = embed ${capnpString(name)} )`;
304 })305 })
305 .join(",\n ");306 .join(",\n ");
307 // workerd's built-in `internet` service relies on the platform TLS trust
308 // store, which kj/tls cannot read on Windows ("unable to get local issuer
309 // certificate"). Declaring our own internet service with the runtime's
310 // bundled Mozilla roots gives every platform the same trust behavior.
311 const trustedCertificates = rootCertificates.map(capnpString).join(",\n ");
306 const services = [312 const services = [
307 '( name = "main", worker = .mainWorker )',313 '( name = "main", worker = .mainWorker )',
308 '( name = "blocked", worker = .blockedWorker )',314 '( name = "blocked", worker = .blockedWorker )',
315 ...(input.globalOutbound === "internet"
316 ? [
317 `( name = "internet", network = ( allow = ["public"], tlsOptions = ( trustedCertificates = [\n ${trustedCertificates}\n ] ) ) )`,
318 ]
319 : []),
309 ...(input.hostPort === null320 ...(input.hostPort === null
310 ? []321 ? []
311 : [`( name = "host", external = ( address = "127.0.0.1:${input.hostPort}", http = () ) )`]),322 : [`( name = "host", external = ( address = "127.0.0.1:${input.hostPort}", http = () ) )`]),
@@ -504,7 +515,10 @@ export const createWorkerdModuleRunner = ( · 192 unmodified lines
504 });515 });
505 proc.stderr.setEncoding("utf8");516 proc.stderr.setEncoding("utf8");
506 proc.stdout.resume();517 proc.stdout.resume();
507 proc.stderr.resume();518 let stderrTail = "";
519 proc.stderr.on("data", (chunk: string) => {
520 stderrTail = (stderrTail + chunk).slice(-4000);
521 });
508 proc.on("exit", (code, signal) => {522 proc.on("exit", (code, signal) => {
509 lastExitAt = Date.now();523 lastExitAt = Date.now();
510 if (processState?.proc === proc) {524 if (processState?.proc === proc) {
@@ -522,7 +536,10 @@ export const createWorkerdModuleRunner = ( · 11 unmodified lines
522 await waitReady(listenPort, token, startupTimeoutMs);536 await waitReady(listenPort, token, startupTimeoutMs);
523 } catch (cause) {537 } catch (cause) {
524 await disposeState(true);538 await disposeState(true);
525 throw cause;539 const detail = stderrTail.trim();
540 throw detail.length === 0
541 ? cause
542 : new Error(`${normalizeError(cause).message}\nworkerd stderr:\n${detail}`);
526 }543 }
527 })();544 })();
528 545
packages/plugins/apps/src/pipeline/worker-bundler.ts+4−0
@@ -81,6 +81,10 @@ export default { · 80 unmodified lines
81 if (code === null) {81 if (code === null) {
82 return json({ ok: false, message: "worker-bundler did not return JavaScript for " + result.mainModule });82 return json({ ok: false, message: "worker-bundler did not return JavaScript for " + result.mainModule });
83 }83 }
84 const installFailure = (result.warnings ?? []).find((warning) => /failed to install/i.test(String(warning)));
85 if (installFailure) {
86 return json({ ok: false, message: String(installFailure) });
87 }
84 for (const [path, module] of Object.entries(result.modules)) {88 for (const [path, module] of Object.entries(result.modules)) {
85 const source = moduleCode(module) ?? "";89 const source = moduleCode(module) ?? "";
86 if (/\\.node(?:$|[?#])|node-gyp|prebuild-install|node-pre-gyp/.test(path) || /\\.node(?:$|[?#])|node-gyp|prebuild-install|node-pre-gyp/.test(source)) {90 if (/\\.node(?:$|[?#])|node-gyp|prebuild-install|node-pre-gyp/.test(path) || /\\.node(?:$|[?#])|node-gyp|prebuild-install|node-pre-gyp/.test(source)) {