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

feat: Custom https cert #4475

Merged
merged 7 commits into from
Feb 16, 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
2 changes: 2 additions & 0 deletions packages/wrangler/src/__tests__/dev.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1249,6 +1249,8 @@ describe("wrangler dev", () => {
--routes, --route Routes to upload [array]
--host Host to forward requests to, defaults to the zone of project [string]
--local-protocol Protocol to listen to requests on, defaults to http. [choices: \\"http\\", \\"https\\"]
--https-key-path Path to a custom certificate key [string]
--https-cert-path Path to a custom certificate [string]
--local-upstream Host to act as origin in local mode, defaults to dev.host or route [string]
--assets Static assets to be served [string]
--site Root folder of static assets for Workers Sites [string]
Expand Down
4 changes: 4 additions & 0 deletions packages/wrangler/src/api/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export interface UnstableDevOptions {
bundle?: boolean; // Set to false to skip internal build steps and directly deploy script
inspectorPort?: number; // Port for devtools to connect to
localProtocol?: "http" | "https"; // Protocol to listen to requests on, defaults to http.
httpsKeyPath?: string;
httpsCertPath?: string;
assets?: string; // Static assets to be served
site?: string; // Root folder of static assets for Workers Sites
siteInclude?: string[]; // Array of .gitignore-style patterns that match file or directory names from the sites directory. Only matched items will be uploaded.
Expand Down Expand Up @@ -174,6 +176,8 @@ export async function unstable_dev(
inspectorPort: options?.inspectorPort ?? 0,
v: undefined,
localProtocol: options?.localProtocol,
httpsKeyPath: options?.httpsKeyPath,
httpsCertPath: options?.httpsCertPath,
assets: options?.assets,
site: options?.site, // Root folder of static assets for Workers Sites
siteInclude: options?.siteInclude, // Array of .gitignore-style patterns that match file or directory names from the sites directory. Only matched items will be uploaded.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@
const cert =
this.latestConfig.dev?.server?.secure ||
this.latestConfig.dev?.inspector?.secure
? getHttpsOptions()
? getHttpsOptions(
this.latestConfig.dev.server?.httpsKeyPath,
this.latestConfig.dev.server?.httpsCertPath

Check warning on line 63 in packages/wrangler/src/api/startDevWorker/ProxyController.ts

View check run for this annotation

Codecov / codecov/patch

packages/wrangler/src/api/startDevWorker/ProxyController.ts#L62-L63

Added lines #L62 - L63 were not covered by tests
)
: undefined;

const proxyWorkerOptions: MiniflareOptions = {
Expand Down
8 changes: 7 additions & 1 deletion packages/wrangler/src/api/startDevWorker/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,13 @@ export interface StartDevWorkerOptions {
liveReload?: boolean;

/** The local address to reach your worker. Applies to remote: true (remote mode) and remote: false (local mode). */
server?: { hostname?: string; port?: number; secure?: boolean }; // hostname: --ip, port: --port, secure: --local-protocol
server?: {
hostname?: string; // --ip
port?: number; // --port
secure?: boolean; // --local-protocol==https
httpsKeyPath?: string;
httpsCertPath?: string;
};
/** Controls what request.url looks like inside the worker. */
urlOverrides?: { hostname?: string; secure?: boolean }; // hostname: --host (remote)/--local-upstream (local), port: doesn't make sense in remote/=== server.port in local, secure: --upstream-protocol
/** A hook for outbound fetch calls from within the worker. */
Expand Down
14 changes: 14 additions & 0 deletions packages/wrangler/src/dev.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,16 @@ export function devOptions(yargs: CommonYargsArgv) {
describe: "Protocol to listen to requests on, defaults to http.",
choices: ["http", "https"] as const,
})
.option("https-key-path", {
describe: "Path to a custom certificate key",
type: "string",
requiresArg: true,
})
.option("https-cert-path", {
describe: "Path to a custom certificate",
type: "string",
requiresArg: true,
})
.options("local-upstream", {
type: "string",
describe:
Expand Down Expand Up @@ -446,6 +456,8 @@ export async function startDev(args: StartDevOptions) {
tsconfig={args.tsconfig ?? configParam.tsconfig}
upstreamProtocol={upstreamProtocol}
localProtocol={args.localProtocol || configParam.dev.local_protocol}
httpsKeyPath={args.httpsKeyPath}
httpsCertPath={args.httpsCertPath}
localUpstream={args.localUpstream ?? host}
localPersistencePath={localPersistencePath}
liveReload={args.liveReload || false}
Expand Down Expand Up @@ -572,6 +584,8 @@ export async function startApiDev(args: StartDevOptions) {
tsconfig: args.tsconfig ?? configParam.tsconfig,
upstreamProtocol: upstreamProtocol,
localProtocol: args.localProtocol ?? configParam.dev.local_protocol,
httpsKeyPath: args.httpsKeyPath,
httpsCertPath: args.httpsCertPath,
localUpstream: args.localUpstream ?? host,
localPersistencePath,
liveReload: args.liveReload ?? false,
Expand Down
10 changes: 10 additions & 0 deletions packages/wrangler/src/dev/dev.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ export type DevProps = {
tsconfig: string | undefined;
upstreamProtocol: "https" | "http";
localProtocol: "https" | "http";
httpsKeyPath: string | undefined;
httpsCertPath: string | undefined;
localUpstream: string | undefined;
localPersistencePath: string | null;
liveReload: boolean;
Expand Down Expand Up @@ -270,6 +272,8 @@ function DevSession(props: DevSessionProps) {
hostname: props.initialIp,
port: props.initialPort,
secure: props.localProtocol === "https",
httpsKeyPath: props.httpsKeyPath,
httpsCertPath: props.httpsCertPath,
},
inspector: {
port: props.inspectorPort,
Expand All @@ -286,6 +290,8 @@ function DevSession(props: DevSessionProps) {
props.initialIp,
props.initialPort,
props.localProtocol,
props.httpsKeyPath,
props.httpsCertPath,
props.localUpstream,
props.inspectorPort,
props.liveReload,
Expand Down Expand Up @@ -445,6 +451,8 @@ function DevSession(props: DevSessionProps) {
crons={props.crons}
queueConsumers={props.queueConsumers}
localProtocol={"http"} // hard-code for userworker, DevEnv-ProxyWorker now uses this prop value
httpsKeyPath={props.httpsKeyPath}
httpsCertPath={props.httpsCertPath}
localUpstream={props.localUpstream}
upstreamProtocol={props.upstreamProtocol}
inspect={props.inspect}
Expand All @@ -464,6 +472,8 @@ function DevSession(props: DevSessionProps) {
port={props.initialPort}
ip={props.initialIp}
localProtocol={props.localProtocol}
httpsKeyPath={props.httpsKeyPath}
httpsCertPath={props.httpsCertPath}
inspectorPort={props.inspectorPort}
// TODO: @threepointone #1167
// liveReload={props.liveReload}
Expand Down
4 changes: 4 additions & 0 deletions packages/wrangler/src/dev/local.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export interface LocalProps {
queueConsumers: Config["queues"]["consumers"];
localProtocol: "http" | "https";
upstreamProtocol: "http" | "https";
httpsKeyPath: string | undefined;
httpsCertPath: string | undefined;
localUpstream: string | undefined;
inspect: boolean;
onReady:
Expand Down Expand Up @@ -93,6 +95,8 @@ export async function localPropsToConfigBundle(
crons: props.crons,
queueConsumers: props.queueConsumers,
localProtocol: props.localProtocol,
httpsKeyPath: props.httpsKeyPath,
httpsCertPath: props.httpsCertPath,
localUpstream: props.localUpstream,
upstreamProtocol: props.upstreamProtocol,
inspect: props.inspect,
Expand Down
7 changes: 6 additions & 1 deletion packages/wrangler/src/dev/miniflare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@
crons: Config["triggers"]["crons"];
queueConsumers: Config["queues"]["consumers"];
localProtocol: "http" | "https";
httpsKeyPath: string | undefined;
httpsCertPath: string | undefined;
localUpstream: string | undefined;
upstreamProtocol: "http" | "https";
inspect: boolean;
Expand Down Expand Up @@ -594,7 +596,10 @@

let httpsOptions: { httpsKey: string; httpsCert: string } | undefined;
if (config.localProtocol === "https") {
const cert = await getHttpsOptions();
const cert = await getHttpsOptions(

Check warning on line 599 in packages/wrangler/src/dev/miniflare.ts

View check run for this annotation

Codecov / codecov/patch

packages/wrangler/src/dev/miniflare.ts#L599

Added line #L599 was not covered by tests
config.httpsKeyPath,
config.httpsCertPath
);
httpsOptions = {
httpsKey: cert.key,
httpsCert: cert.cert,
Expand Down
26 changes: 21 additions & 5 deletions packages/wrangler/src/dev/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,21 +115,29 @@
previewToken,
assetDirectory,
localProtocol,
customHttpsKeyPath,
customHttpsCertPath,
localPort: port,
ip,
onReady,
}: {
previewToken: CfPreviewToken;
assetDirectory: string | undefined;
localProtocol: "https" | "http";
customHttpsKeyPath: string | undefined;
customHttpsCertPath: string | undefined;
localPort: number;
ip: string;
onReady: ((readyIp: string, readyPort: number) => void) | undefined;
}) {
try {
const abortController = new AbortController();

const server = await createProxyServer(localProtocol);
const server = await createProxyServer(

Check warning on line 136 in packages/wrangler/src/dev/proxy.ts

View check run for this annotation

Codecov / codecov/patch

packages/wrangler/src/dev/proxy.ts#L136

Added line #L136 was not covered by tests
localProtocol,
customHttpsKeyPath,
customHttpsCertPath
);
const proxy = {
server,
terminator: createHttpTerminator({
Expand Down Expand Up @@ -199,12 +207,16 @@
previewToken,
assetDirectory,
localProtocol,
httpsKeyPath,
httpsCertPath,
localPort: port,
ip,
}: {
previewToken: CfPreviewToken | undefined;
assetDirectory: string | undefined;
localProtocol: "https" | "http";
httpsKeyPath: string | undefined;
httpsCertPath: string | undefined;
localPort: number;
ip: string;
}) {
Expand All @@ -217,7 +229,7 @@
*/
useEffect(() => {
if (proxy === undefined) {
createProxyServer(localProtocol)
createProxyServer(localProtocol, httpsKeyPath, httpsCertPath)

Check warning on line 232 in packages/wrangler/src/dev/proxy.ts

View check run for this annotation

Codecov / codecov/patch

packages/wrangler/src/dev/proxy.ts#L232

Added line #L232 was not covered by tests
.then((server) => {
setProxy({
server,
Expand All @@ -231,7 +243,7 @@
logger.error("Failed to create proxy server:", err);
});
}
}, [proxy, localProtocol]);
}, [proxy, localProtocol, httpsKeyPath, httpsCertPath]);

/**
* When we're not connected / getting a fresh token on changes,
Expand Down Expand Up @@ -579,11 +591,15 @@
]);

async function createProxyServer(
localProtocol: "https" | "http"
localProtocol: "https" | "http",
customHttpsKeyPath: string | undefined,
customHttpsCertPath: string | undefined
): Promise<HttpServer | HttpsServer> {
const server: HttpServer | HttpsServer =
localProtocol === "https"
? createHttpsServer(await getHttpsOptions())
? createHttpsServer(
await getHttpsOptions(customHttpsKeyPath, customHttpsCertPath)
)

Check warning on line 602 in packages/wrangler/src/dev/proxy.ts

View check run for this annotation

Codecov / codecov/patch

packages/wrangler/src/dev/proxy.ts#L602

Added line #L602 was not covered by tests
: createHttpServer();

return server
Expand Down
4 changes: 4 additions & 0 deletions packages/wrangler/src/dev/remote.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ interface RemoteProps {
port: number;
ip: string;
localProtocol: "https" | "http";
httpsKeyPath: string | undefined;
httpsCertPath: string | undefined;
inspect: boolean;
inspectorPort: number;
accountId: string | undefined;
Expand Down Expand Up @@ -422,6 +424,8 @@ export async function startRemoteServer(props: RemoteProps) {
? undefined
: props.assetPaths?.assetDirectory,
localProtocol: props.localProtocol,
customHttpsKeyPath: props.httpsKeyPath,
customHttpsCertPath: props.httpsCertPath,
localPort: props.port,
ip: props.ip,
onReady: async (ip, port) => {
Expand Down
6 changes: 6 additions & 0 deletions packages/wrangler/src/dev/start-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ export async function startDevServer(
hostname: props.initialIp,
port: props.initialPort,
secure: props.localProtocol === "https",
httpsKeyPath: props.httpsKeyPath,
httpsCertPath: props.httpsCertPath,
},
inspector: {
port: props.inspectorPort,
Expand Down Expand Up @@ -172,6 +174,8 @@ export async function startDevServer(
crons: props.crons,
queueConsumers: props.queueConsumers,
localProtocol: props.localProtocol,
httpsKeyPath: props.httpsKeyPath,
httpsCertPath: props.httpsCertPath,
localUpstream: props.localUpstream,
upstreamProtocol: props.upstreamProtocol,
inspect: true,
Expand Down Expand Up @@ -223,6 +227,8 @@ export async function startDevServer(
port: props.initialPort,
ip: props.initialIp,
localProtocol: props.localProtocol,
httpsKeyPath: props.httpsKeyPath,
httpsCertPath: props.httpsCertPath,
inspectorPort: props.inspectorPort,
inspect: props.inspect,
compatibilityDate: props.compatibilityDate,
Expand Down
34 changes: 32 additions & 2 deletions packages/wrangler/src/https-options.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as fs from "node:fs";
import * as path from "node:path";
import { getAccessibleHosts } from "miniflare";
import { UserError } from "./errors";
import { getGlobalWranglerConfigPath } from "./global-wrangler-config-path";
import { logger } from "./logger";
import type { Attributes, Options } from "selfsigned";
Expand All @@ -16,11 +17,40 @@
*
* The certificates are self-signed and generated locally, and cached in the `CERT_ROOT` directory.
*/
export function getHttpsOptions() {
export function getHttpsOptions(
customHttpsKeyPath?: string,
customHttpsCertPath?: string
) {
if (customHttpsKeyPath !== undefined || customHttpsCertPath !== undefined) {
if (customHttpsKeyPath === undefined || customHttpsCertPath === undefined) {
throw new UserError(

Check warning on line 26 in packages/wrangler/src/https-options.ts

View check run for this annotation

Codecov / codecov/patch

packages/wrangler/src/https-options.ts#L26

Added line #L26 was not covered by tests
"Must specify both certificate path and key path to use a Custom Certificate."
);
}
if (!fs.existsSync(customHttpsKeyPath)) {
throw new UserError(

Check warning on line 31 in packages/wrangler/src/https-options.ts

View check run for this annotation

Codecov / codecov/patch

packages/wrangler/src/https-options.ts#L31

Added line #L31 was not covered by tests
"Missing Custom Certificate Key at " + customHttpsKeyPath
);
}
if (!fs.existsSync(customHttpsCertPath)) {
throw new UserError(

Check warning on line 36 in packages/wrangler/src/https-options.ts

View check run for this annotation

Codecov / codecov/patch

packages/wrangler/src/https-options.ts#L36

Added line #L36 was not covered by tests
"Missing Custom Certificate File at " + customHttpsCertPath
);
}
if (hasCertificateExpired(customHttpsKeyPath, customHttpsCertPath)) {
throw new UserError("Custom Certificate is invalid");

Check warning on line 41 in packages/wrangler/src/https-options.ts

View check run for this annotation

Codecov / codecov/patch

packages/wrangler/src/https-options.ts#L41

Added line #L41 was not covered by tests
}
logger.log("Using custom certificate at ", customHttpsKeyPath);

Check warning on line 43 in packages/wrangler/src/https-options.ts

View check run for this annotation

Codecov / codecov/patch

packages/wrangler/src/https-options.ts#L43

Added line #L43 was not covered by tests

return {

Check warning on line 45 in packages/wrangler/src/https-options.ts

View check run for this annotation

Codecov / codecov/patch

packages/wrangler/src/https-options.ts#L45

Added line #L45 was not covered by tests
key: fs.readFileSync(customHttpsKeyPath, "utf8"),
cert: fs.readFileSync(customHttpsCertPath, "utf8"),
};
}

const certDirectory = path.join(getGlobalWranglerConfigPath(), "local-cert");
const keyPath = path.join(certDirectory, "key.pem");
const certPath = path.join(certDirectory, "cert.pem");

const regenerate =
!fs.existsSync(keyPath) ||
!fs.existsSync(certPath) ||
Expand Down
14 changes: 14 additions & 0 deletions packages/wrangler/src/pages/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,16 @@ export function Options(yargs: CommonYargsArgv) {
describe: "Protocol to listen to requests on, defaults to http.",
choices: ["http", "https"] as const,
},
"https-key-path": {
describe: "Path to a custom certificate key",
type: "string",
requiresArg: true,
},
"https-cert-path": {
describe: "Path to a custom certificate",
type: "string",
requiresArg: true,
},
"persist-to": {
describe:
"Specify directory to use for local persistence (defaults to .wrangler/state)",
Expand Down Expand Up @@ -230,6 +240,8 @@ export const Handler = async ({
service: requestedServices = [],
liveReload,
localProtocol,
httpsKeyPath,
httpsCertPath,
persistTo,
nodeCompat: legacyNodeCompat,
experimentalLocal,
Expand Down Expand Up @@ -635,6 +647,8 @@ export const Handler = async ({
port,
inspectorPort,
localProtocol,
httpsKeyPath,
httpsCertPath,
compatibilityDate,
compatibilityFlags,
nodeCompat: legacyNodeCompat,
Expand Down
Loading
Loading