Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/extra-ca-certs-deploy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---

Fix `extraCACerts` in `trigger.config.ts` having no effect on deployed tasks. The CA file is now included in the deployed image and `NODE_EXTRA_CA_CERTS` points at it, so self-signed or corporate certificates are trusted at runtime.
6 changes: 5 additions & 1 deletion packages/cli-v3/src/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ import {
} from "../cli/common.js";
import { loadConfig } from "../config.js";
import { authenticateForDeploy, userIdForDeploy } from "../deploy/auth.js";
import { buildImage } from "../deploy/buildImage.js";
import { buildImage, resolveExtraCACertsImagePath } from "../deploy/buildImage.js";
import {
checkLogsForErrors,
checkLogsForWarnings,
Expand Down Expand Up @@ -765,6 +765,10 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
externalBuildProjectId: deployment.externalBuildData?.projectId,
projectId: projectClient.id,
projectRef: resolvedConfig.project,
extraCACerts: resolveExtraCACertsImagePath(
resolvedConfig.extraCACerts,
resolvedConfig.workingDir
),
Comment on lines +768 to +771

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Bundled deployments omit the CA setting

With --local-bundle or --from-bundle, extraCACerts never reaches their image builds. Those paths bypass this buildImage call, so deployed tasks cannot trust the configured CA.

Learn more

The deploy command has separate paths for standard builds and builds from bundles. The local-bundle path copies the CA certificate into its output through the new extension, but handleLocalBundleDeploy sends the bundle to a server instead of calling this image build. The from-bundle path calls buildAndFinalizeFromBundle, which calls buildImage without extraCACerts. Both images retain an empty NODE_EXTRA_CA_CERTS setting.

Example: A project sets extraCACerts: "./certs/company.crt" and deploys with --local-bundle. The certificate is copied into /app/certs/company.crt, but Node does not load it and a task connecting to an internal HTTPS service fails certificate verification.

Recommended fix: Store the resolved image CA path in the bundle or build metadata and pass it through both bundle image-building paths. Verify the path matches the copied file for local-bundle and from-bundle builds.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

apiUrl: projectClient.client.apiURL,
apiKey: projectClient.client.accessToken!,
apiClient: projectClient.client,
Expand Down
7 changes: 7 additions & 0 deletions packages/cli-v3/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,13 @@ async function autoDetectDirs(workingDir: string): Promise<string[]> {
}

function validateConfig(config: TriggerConfig, warn = true) {
if (config.extraCACerts) {
// The CA file must be inside the build output so the image can point NODE_EXTRA_CA_CERTS at it
config.build ??= {};
config.build.extensions ??= [];
config.build.extensions.push(additionalFiles({ files: [config.extraCACerts] }));
}

if (config.additionalFiles && config.additionalFiles.length > 0) {
warn &&
prettyWarning(
Expand Down
15 changes: 15 additions & 0 deletions packages/cli-v3/src/deploy/buildImage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
TOOLCHAIN_PACKAGES,
formatCommandForDiagnostics,
generateContainerfile,
resolveExtraCACertsImagePath,
} from "./buildImage.js";

describe("formatCommandForDiagnostics", () => {
Expand Down Expand Up @@ -53,6 +54,20 @@ const images: Array<[BuildRuntime, string, string]> = [
],
];

describe("resolveExtraCACertsImagePath", () => {
it("returns undefined when no CA file is configured", () => {
expect(resolveExtraCACertsImagePath(undefined, "/project")).toBeUndefined();
});

it("maps a project-relative path into /app", () => {
expect(resolveExtraCACertsImagePath("./certs/ca.crt", "/project")).toBe("/app/certs/ca.crt");
});

it("strips leading parent segments the same way additionalFiles does", () => {
expect(resolveExtraCACertsImagePath("../shared/ca.crt", "/project")).toBe("/app/shared/ca.crt");
});
});

describe("generateContainerfile", () => {
it.each(images)("uses the pinned published base images for %s", async (runtime, base, build) => {
const containerfile = await generateContainerfile({
Expand Down
19 changes: 18 additions & 1 deletion packages/cli-v3/src/deploy/buildImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { depot } from "@depot/cli";
import { x } from "tinyexec";
import type { BuildManifest, BuildRuntime } from "@trigger.dev/core/v3/schemas";
import { networkInterfaces } from "os";
import { join } from "path";
import { join, relative, resolve, sep } from "path";
import { safeReadJSONFile } from "../utilities/fileSystem.js";
import { readFileSync } from "fs";

Expand Down Expand Up @@ -55,6 +55,23 @@ export interface BuildImageOptions {
deploymentSpinner?: any; // Replace 'any' with the actual type if known
}

/**
* Where the CA file ends up inside the image. The build copies it into /app using the same layout
* as additionalFiles (path relative to the project root, with leading `..` segments stripped).
*/
export function resolveExtraCACertsImagePath(extraCACerts: string | undefined, workingDir: string) {
if (!extraCACerts) {
return undefined;
}

const relativePath = relative(workingDir, resolve(workingDir, extraCACerts))
.split(sep)
.filter((segment) => segment !== "..")
.join("/");

return `/app/${relativePath}`;
Comment on lines +67 to +72

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Parent-directory CA missing on Windows

On Windows, resolveExtraCACertsImagePath maps ../shared/ca.crt to /app/shared/ca.crt. The file copier retains Windows .. segments, placing the certificate outside the image build context.

Learn more

The additionalFiles extension copies the configured CA file into the bundle. Its file copier calls Node's relative, then splits on posix.sep. On Windows, relative returns backslashes, so ..\\shared\\ca.crt is not split and the copy destination resolves outside the bundle. The new image path strips .. using the platform separator, so it points to a file absent from the image.

Example: On Windows, a project in C:\\project sets extraCACerts: "../shared/ca.crt". The image path becomes /app/shared/ca.crt, while the copy goes to a path outside the build output.

Recommended fix: Make the file copier strip parent segments using the platform path separator, and test that its destination agrees with resolveExtraCACertsImagePath on Windows.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

}

export async function buildImage(options: BuildImageOptions): Promise<BuildImageResults> {
const {
isLocalBuild,
Expand Down