Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support setting custom HTTPS certification paths from environment variables #6479

Merged
merged 3 commits into from
Aug 14, 2024
Merged
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
11 changes: 11 additions & 0 deletions .changeset/tall-lions-explode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"wrangler": minor
---

feat: allow HTTPS custom certificate paths to be provided by a environment variables

As well as providing paths to custom HTTPS certificate files, it is now possible to use WRANGLER_HTTPS_KEY_PATH and WRANGLER_HTTPS_CERT_PATH environment variables.

Specifying the file paths at the command line overrides specifying in environment variables.

Fixes #5997
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,14 @@ describe("normalizeAndValidateConfig()", () => {
);

// supress Hyperdrive beta warnings
process.env.NO_HYPERDRIVE_WARNING = "true";
vi.stubEnv("NO_HYPERDRIVE_WARNING", "true");

// sanity checks
expect(pagesRawConfig.env).not.toBeUndefined();
expect(pagesRawConfig.env?.preview).not.toBeUndefined();
expect(pagesRawConfig.env?.production).not.toBeUndefined();
});

afterEach(() => {
process.env.NO_HYPERDRIVE_WARNING = undefined;
});

describe("named environments", () => {
it("should return config corresponding to the top-level environment, if no named environment is provided", () => {
const { config, diagnostics } = normalizeAndValidateConfig(
Expand Down
20 changes: 8 additions & 12 deletions packages/wrangler/src/__tests__/configuration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3346,20 +3346,16 @@ describe("normalizeAndValidateConfig()", () => {
});

it("should not provide an unsafe warning when the environment variable is specified", () => {
try {
process.env.WRANGLER_DISABLE_EXPERIMENTAL_WARNING = "1";
vi.stubEnv("WRANGLER_DISABLE_EXPERIMENTAL_WARNING", "1");

const { diagnostics } = normalizeAndValidateConfig(
{ unsafe: { bindings: [] } } as unknown as RawConfig,
undefined,
{ env: undefined }
);
const { diagnostics } = normalizeAndValidateConfig(
{ unsafe: { bindings: [] } } as unknown as RawConfig,
undefined,
{ env: undefined }
);

expect(diagnostics.hasWarnings()).toBe(false);
expect(diagnostics.hasErrors()).toBe(false);
} finally {
delete process.env.WRANGLER_DISABLE_EXPERIMENTAL_WARNING;
}
expect(diagnostics.hasWarnings()).toBe(false);
expect(diagnostics.hasErrors()).toBe(false);
});
});

Expand Down
3 changes: 1 addition & 2 deletions packages/wrangler/src/__tests__/d1/migrate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,7 @@ Your database may not be available to serve requests during the migration, conti
});

it("should try to read D1 config from wrangler.toml when logged in", async () => {
// no need to clear this env var as it's implicitly cleared by mockApiToken in afterEach
process.env.CLOUDFLARE_API_TOKEN = "api-token";
vi.stubEnv("CLOUDFLARE_API_TOKEN", "api-token");
reinitialiseAuthTokens();
setIsTTY(false);
writeWranglerToml();
Expand Down
70 changes: 18 additions & 52 deletions packages/wrangler/src/__tests__/deploy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -442,17 +442,8 @@ describe("deploy", () => {
});

describe("non-TTY", () => {
const ENV_COPY = process.env;

afterEach(() => {
process.env = ENV_COPY;
});

it("should not throw an error in non-TTY if 'CLOUDFLARE_API_TOKEN' & 'account_id' are in scope", async () => {
process.env = {
...process.env,
CLOUDFLARE_API_TOKEN: "123456789",
};
vi.stubEnv("CLOUDFLARE_API_TOKEN", "123456789");
setIsTTY(false);
writeWranglerToml({
account_id: "some-account-id",
Expand Down Expand Up @@ -480,11 +471,8 @@ describe("deploy", () => {
});

it("should not throw an error if 'CLOUDFLARE_ACCOUNT_ID' & 'CLOUDFLARE_API_TOKEN' are in scope", async () => {
process.env = {
...process.env,
CLOUDFLARE_API_TOKEN: "hunter2",
CLOUDFLARE_ACCOUNT_ID: "some-account-id",
};
vi.stubEnv("CLOUDFLARE_API_TOKEN", "hunter2");
vi.stubEnv("CLOUDFLARE_ACCOUNT_ID", "some-account-id");
setIsTTY(false);
writeWranglerToml();
writeWorkerSource();
Expand Down Expand Up @@ -512,11 +500,8 @@ describe("deploy", () => {

it("should throw an error in non-TTY & there is more than one account associated with API token", async () => {
setIsTTY(false);
process.env = {
...process.env,
CLOUDFLARE_API_TOKEN: "hunter2",
CLOUDFLARE_ACCOUNT_ID: undefined,
};
vi.stubEnv("CLOUDFLARE_API_TOKEN", "hunter2");
vi.stubEnv("CLOUDFLARE_ACCOUNT_ID", "");
writeWranglerToml({
account_id: undefined,
});
Expand Down Expand Up @@ -544,11 +529,8 @@ describe("deploy", () => {
writeWranglerToml({
account_id: undefined,
});
process.env = {
...process.env,
CLOUDFLARE_API_TOKEN: undefined,
CLOUDFLARE_ACCOUNT_ID: "badwolf",
};
vi.stubEnv("CLOUDFLARE_API_TOKEN", "");
vi.stubEnv("CLOUDFLARE_ACCOUNT_ID", "badwolf");
writeWorkerSource();
mockSubDomainRequest();
mockUploadWorkerRequest();
Expand All @@ -571,11 +553,8 @@ describe("deploy", () => {
writeWranglerToml({
account_id: undefined,
});
process.env = {
...process.env,
CLOUDFLARE_API_TOKEN: "picard",
CLOUDFLARE_ACCOUNT_ID: undefined,
};
vi.stubEnv("CLOUDFLARE_API_TOKEN", "picard");
vi.stubEnv("CLOUDFLARE_ACCOUNT_ID", "");
writeWorkerSource();
mockSubDomainRequest();
mockUploadWorkerRequest();
Expand Down Expand Up @@ -8870,14 +8849,10 @@ addEventListener('fetch', event => {});`
});

describe("inject process.env.NODE_ENV", () => {
let actualProcessEnvNodeEnv: string | undefined;
beforeEach(() => {
actualProcessEnvNodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = "some-node-env";
});
afterEach(() => {
process.env.NODE_ENV = actualProcessEnvNodeEnv;
vi.stubEnv("NODE_ENV", "some-node-env");
});

it("should replace `process.env.NODE_ENV` in scripts", async () => {
writeWranglerToml();
fs.writeFileSync(
Expand Down Expand Up @@ -9317,7 +9292,7 @@ export default{
};
export class SomeClass {};`
);
process.env.CLOUDFLARE_ACCOUNT_ID = "";
vi.stubEnv("CLOUDFLARE_ACCOUNT_ID", "");
await runWrangler("deploy index.js --dry-run");
expect(std).toMatchInlineSnapshot(`
Object {
Expand Down Expand Up @@ -10959,11 +10934,8 @@ export default{

describe("--keep-vars", () => {
it("should send keepVars when keep-vars is passed in", async () => {
process.env = {
...process.env,
CLOUDFLARE_API_TOKEN: "hunter2",
CLOUDFLARE_ACCOUNT_ID: "some-account-id",
};
vi.stubEnv("CLOUDFLARE_API_TOKEN", "hunter2");
vi.stubEnv("CLOUDFLARE_ACCOUNT_ID", "some-account-id");
setIsTTY(false);
writeWranglerToml();
writeWorkerSource();
Expand All @@ -10990,11 +10962,8 @@ export default{
});

it("should not send keepVars by default", async () => {
process.env = {
...process.env,
CLOUDFLARE_API_TOKEN: "hunter2",
CLOUDFLARE_ACCOUNT_ID: "some-account-id",
};
vi.stubEnv("CLOUDFLARE_API_TOKEN", "hunter2");
vi.stubEnv("CLOUDFLARE_ACCOUNT_ID", "some-account-id");
setIsTTY(false);
writeWranglerToml();
writeWorkerSource();
Expand All @@ -11021,11 +10990,8 @@ export default{
});

it("should send keepVars when `keep_vars = true`", async () => {
process.env = {
...process.env,
CLOUDFLARE_API_TOKEN: "hunter2",
CLOUDFLARE_ACCOUNT_ID: "some-account-id",
};
vi.stubEnv("CLOUDFLARE_API_TOKEN", "hunter2");
vi.stubEnv("CLOUDFLARE_ACCOUNT_ID", "some-account-id");
setIsTTY(false);
writeWranglerToml({
keep_vars: true,
Expand Down
3 changes: 2 additions & 1 deletion packages/wrangler/src/__tests__/dev.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -754,8 +754,9 @@ describe("wrangler dev", () => {
});

// We won't overwrite existing process.env keys with .env values (to
// allow .env overrides to specified on then shell), so make sure this
// allow .env overrides to be specified on the shell), so make sure this
// key definitely doesn't exist.
vi.stubEnv("CUSTOM_BUILD_VAR", "");
delete process.env.CUSTOM_BUILD_VAR;
});

Expand Down
14 changes: 4 additions & 10 deletions packages/wrangler/src/__tests__/helpers/mock-account-id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,18 @@ import { reinitialiseAuthTokens } from "../../user";
export function mockApiToken({
apiToken = "some-api-token",
}: { apiToken?: string | null } = {}) {
const ORIGINAL_CLOUDFLARE_API_TOKEN = process.env.CLOUDFLARE_API_TOKEN;
beforeEach(() => {
if (apiToken === null) {
// stubEnv doesn't support removing env vars
// So we fake it by initially stubbing it with an empty string and then deleting it
vi.stubEnv("CLOUDFLARE_API_TOKEN", "");
delete process.env.CLOUDFLARE_API_TOKEN;
} else {
process.env.CLOUDFLARE_API_TOKEN = apiToken;
vi.stubEnv("CLOUDFLARE_API_TOKEN", apiToken);
}
// Now we have updated the environment, we must reinitialize the user auth state.
reinitialiseAuthTokens();
});
afterEach(() => {
if (ORIGINAL_CLOUDFLARE_API_TOKEN === undefined) {
// `process.env`'s assigned property values are coerced to strings
delete process.env.CLOUDFLARE_API_TOKEN;
} else {
process.env.CLOUDFLARE_API_TOKEN = ORIGINAL_CLOUDFLARE_API_TOKEN;
}
});
}

/**
Expand Down
69 changes: 69 additions & 0 deletions packages/wrangler/src/__tests__/https-options.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,75 @@ describe("getHttpsOptions()", () => {
`);
expect(std.err).toMatchInlineSnapshot(`""`);
});

it("should read the certs from the paths if provided", async () => {
fs.mkdirSync("./certs");
await fs.promises.writeFile("./certs/test.key", "xxxxx");
await fs.promises.writeFile("./certs/test.pem", "yyyyy");
const options = getHttpsOptions("./certs/test.key", "./certs/test.pem");
expect(options.key).toEqual("xxxxx");
expect(options.cert).toEqual("yyyyy");
});

it("should error if only one of the two paths is provided", async () => {
expect(() =>
getHttpsOptions("./certs/test.key", undefined)
).toThrowErrorMatchingInlineSnapshot(
`[Error: Must specify both certificate path and key path to use a Custom Certificate.]`
);
expect(() =>
getHttpsOptions(undefined, "./certs/test.pem")
).toThrowErrorMatchingInlineSnapshot(
`[Error: Must specify both certificate path and key path to use a Custom Certificate.]`
);
});

it("should error if the key file does not exist", async () => {
fs.mkdirSync("./certs");
await fs.promises.writeFile("./certs/test.pem", "yyyyy");
expect(() =>
getHttpsOptions("./certs/test.key", "./certs/test.pem")
).toThrowErrorMatchingInlineSnapshot(
`[Error: Missing Custom Certificate Key at ./certs/test.key]`
);
});

it("should error if the cert file does not exist", async () => {
fs.mkdirSync("./certs");
await fs.promises.writeFile("./certs/test.key", "xxxxx");
expect(() =>
getHttpsOptions("./certs/test.key", "./certs/test.pem")
).toThrowErrorMatchingInlineSnapshot(
`[Error: Missing Custom Certificate File at ./certs/test.pem]`
);
});

it("should read the certs from the paths in env vars", async () => {
fs.mkdirSync("./certs");
await fs.promises.writeFile("./certs/test.key", "xxxxx");
await fs.promises.writeFile("./certs/test.pem", "yyyyy");
vi.stubEnv("WRANGLER_HTTPS_KEY_PATH", "./certs/test.key");
vi.stubEnv("WRANGLER_HTTPS_CERT_PATH", "./certs/test.pem");
const options = getHttpsOptions();
expect(options.key).toEqual("xxxxx");
expect(options.cert).toEqual("yyyyy");
});

it("should read the certs from the param paths rather than paths in env vars", async () => {
fs.mkdirSync("./certs");
await fs.promises.writeFile("./certs/test-param.key", "xxxxx-param");
await fs.promises.writeFile("./certs/test-param.pem", "yyyyy-param");
await fs.promises.writeFile("./certs/test-env.key", "xxxxx-env");
await fs.promises.writeFile("./certs/test-env.pem", "yyyyy-env");
vi.stubEnv("WRANGLER_HTTPS_KEY_PATH", "./certs/test-env.key");
vi.stubEnv("WRANGLER_HTTPS_CERT_PATH", "./certs/test-env.pem");
const options = getHttpsOptions(
"./certs/test-param.key",
"./certs/test-param.pem"
);
expect(options.key).toEqual("xxxxx-param");
expect(options.cert).toEqual("yyyyy-param");
});
});

async function mockWriteFileSyncThrow(matcher: RegExp) {
Expand Down
11 changes: 1 addition & 10 deletions packages/wrangler/src/__tests__/init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,17 +103,8 @@ describe("init", () => {
});

describe("with custom C3 command", () => {
const ORIGINAL_ENV = process.env;

beforeEach(() => {
process.env = {
...ORIGINAL_ENV,
WRANGLER_C3_COMMAND: "run create-cloudflare",
};
});

afterEach(() => {
process.env = ORIGINAL_ENV;
vi.stubEnv("WRANGLER_C3_COMMAND", "run create-cloudflare");
});

test("shows deprecation message and delegates to C3", async () => {
Expand Down
13 changes: 5 additions & 8 deletions packages/wrangler/src/__tests__/logger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,7 @@ describe("logger", () => {

describe("loggerLevelFromEnvVar=error", () => {
beforeEach(() => {
process.env.WRANGLER_LOG = "error";
});
afterEach(() => {
process.env.WRANGLER_LOG = undefined;
vi.stubEnv("WRANGLER_LOG", "error");
});

it("should render messages that are at or above the log level set in the env var", () => {
Expand All @@ -146,10 +143,10 @@ describe("logger", () => {

describe("loggerLevelFromEnvVar case-insensitive", () => {
beforeEach(() => {
process.env.WRANGLER_LOG = "wARn";
vi.stubEnv("WRANGLER_LOG", "wARn");
});
afterEach(() => {
process.env.WRANGLER_LOG = undefined;
vi.stubEnv("WRANGLER_LOG", "");
});

it("should render messages that are at or above the log level set in the env var", () => {
Expand All @@ -176,10 +173,10 @@ describe("logger", () => {

describe("loggerLevelFromEnvVar falls back to log on invalid level", () => {
beforeEach(() => {
process.env.WRANGLER_LOG = "everything";
vi.stubEnv("WRANGLER_LOG", "everything");
});
afterEach(() => {
process.env.WRANGLER_LOG = undefined;
vi.stubEnv("WRANGLER_LOG", "");
});

it("should render messages that are at or above the log level set in the env var", () => {
Expand Down
Loading
Loading