Skip to content

Commit

Permalink
feat: Custom https cert (#4475)
Browse files Browse the repository at this point in the history
* feat: add flags to provide custom https cert

* fixup! feat: add flags to provide custom https cert

* fixup! feat: add flags to provide custom https cert

* fixup! feat: add flags to provide custom https cert

* fixup! feat: add flags to provide custom https cert

* fixup! feat: add flags to provide custom https cert

* fixup! feat: add flags to provide custom https cert

---------

Co-authored-by: Peter Bacon Darwin <[email protected]>
  • Loading branch information
paulrostorp and petebacondarwin authored Feb 16, 2024
1 parent ed4bf22 commit 86d94ff
Show file tree
Hide file tree
Showing 14 changed files with 189 additions and 57 deletions.
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
5 changes: 4 additions & 1 deletion packages/wrangler/src/api/startDevWorker/ProxyController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ export class ProxyController extends EventEmitter {
const cert =
this.latestConfig.dev?.server?.secure ||
this.latestConfig.dev?.inspector?.secure
? getHttpsOptions()
? getHttpsOptions(
this.latestConfig.dev.server?.httpsKeyPath,
this.latestConfig.dev.server?.httpsCertPath
)
: 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 @@ -447,6 +457,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 @@ -577,6 +589,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 @@ export interface ConfigBundle {
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 @@ async function buildMiniflareOptions(

let httpsOptions: { httpsKey: string; httpsCert: string } | undefined;
if (config.localProtocol === "https") {
const cert = await getHttpsOptions();
const cert = await getHttpsOptions(
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 @@ export async function startPreviewServer({
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(
localProtocol,
customHttpsKeyPath,
customHttpsCertPath
);
const proxy = {
server,
terminator: createHttpTerminator({
Expand Down Expand Up @@ -199,12 +207,16 @@ export function usePreviewServer({
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 @@ export function usePreviewServer({
*/
useEffect(() => {
if (proxy === undefined) {
createProxyServer(localProtocol)
createProxyServer(localProtocol, httpsKeyPath, httpsCertPath)
.then((server) => {
setProxy({
server,
Expand All @@ -231,7 +243,7 @@ export function usePreviewServer({
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 @@ const HTTP1_HEADERS = new Set([
]);

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)
)
: 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 @@ const ONE_DAY_IN_MS = 86400000;
*
* 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(
"Must specify both certificate path and key path to use a Custom Certificate."
);
}
if (!fs.existsSync(customHttpsKeyPath)) {
throw new UserError(
"Missing Custom Certificate Key at " + customHttpsKeyPath
);
}
if (!fs.existsSync(customHttpsCertPath)) {
throw new UserError(
"Missing Custom Certificate File at " + customHttpsCertPath
);
}
if (hasCertificateExpired(customHttpsKeyPath, customHttpsCertPath)) {
throw new UserError("Custom Certificate is invalid");
}
logger.log("Using custom certificate at ", customHttpsKeyPath);

return {
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

0 comments on commit 86d94ff

Please sign in to comment.