Skip to content
Draft
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
7 changes: 7 additions & 0 deletions .changeset/vpc-networks-binding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"wrangler": minor
"miniflare": minor
"@cloudflare/workers-utils": minor
---

Add `vpc_networks` binding support for routing Worker traffic through a Cloudflare Tunnel
4 changes: 4 additions & 0 deletions packages/miniflare/src/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
VERSION_METADATA_PLUGIN,
VERSION_METADATA_PLUGIN_NAME,
} from "./version-metadata";
import { VPC_NETWORKS_PLUGIN, VPC_NETWORKS_PLUGIN_NAME } from "./vpc-networks";
import { VPC_SERVICES_PLUGIN, VPC_SERVICES_PLUGIN_NAME } from "./vpc-services";
import {
WORKER_LOADER_PLUGIN,
Expand Down Expand Up @@ -64,6 +65,7 @@ export const PLUGINS = {
[DISPATCH_NAMESPACE_PLUGIN_NAME]: DISPATCH_NAMESPACE_PLUGIN,
[IMAGES_PLUGIN_NAME]: IMAGES_PLUGIN,
[VECTORIZE_PLUGIN_NAME]: VECTORIZE_PLUGIN,
[VPC_NETWORKS_PLUGIN_NAME]: VPC_NETWORKS_PLUGIN,
[VPC_SERVICES_PLUGIN_NAME]: VPC_SERVICES_PLUGIN,
[MTLS_PLUGIN_NAME]: MTLS_PLUGIN,
[HELLO_WORLD_PLUGIN_NAME]: HELLO_WORLD_PLUGIN,
Expand Down Expand Up @@ -128,6 +130,7 @@ export type WorkerOptions = z.input<typeof CORE_PLUGIN.options> &
z.input<typeof DISPATCH_NAMESPACE_PLUGIN.options> &
z.input<typeof IMAGES_PLUGIN.options> &
z.input<typeof VECTORIZE_PLUGIN.options> &
z.input<typeof VPC_NETWORKS_PLUGIN.options> &
z.input<typeof VPC_SERVICES_PLUGIN.options> &
z.input<typeof MTLS_PLUGIN.options> &
z.input<typeof HELLO_WORLD_PLUGIN.options> &
Expand Down Expand Up @@ -207,6 +210,7 @@ export * from "./browser-rendering";
export * from "./dispatch-namespace";
export * from "./images";
export * from "./vectorize";
export * from "./vpc-networks";
export * from "./vpc-services";
export * from "./mtls";
export * from "./hello-world";
Expand Down
75 changes: 75 additions & 0 deletions packages/miniflare/src/plugins/vpc-networks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { z } from "zod";
import {
getUserBindingServiceName,
Plugin,
ProxyNodeBinding,
remoteProxyClientWorker,
RemoteProxyConnectionString,
} from "../shared";

const VpcNetworksSchema = z.object({
tunnel_id: z.string(),
remoteProxyConnectionString: z
.custom<RemoteProxyConnectionString>()
.optional(),
});

export const VpcNetworksOptionsSchema = z.object({
vpcNetworks: z.record(VpcNetworksSchema).optional(),
});

export const VPC_NETWORKS_PLUGIN_NAME = "vpc-networks";

export const VPC_NETWORKS_PLUGIN: Plugin<typeof VpcNetworksOptionsSchema> = {
options: VpcNetworksOptionsSchema,
async getBindings(options) {
if (!options.vpcNetworks) {
return [];
}

return Object.entries(options.vpcNetworks).map(
([name, { tunnel_id, remoteProxyConnectionString }]) => {
return {
name,

service: {
name: getUserBindingServiceName(
VPC_NETWORKS_PLUGIN_NAME,
tunnel_id,
remoteProxyConnectionString
),
},
};
}
);
},
getNodeBindings(options: z.infer<typeof VpcNetworksOptionsSchema>) {
if (!options.vpcNetworks) {
return {};
}
return Object.fromEntries(
Object.keys(options.vpcNetworks).map((name) => [
name,
new ProxyNodeBinding(),
])
);
},
async getServices({ options }) {
if (!options.vpcNetworks) {
return [];
}

return Object.entries(options.vpcNetworks).map(
([name, { tunnel_id, remoteProxyConnectionString }]) => {
return {
name: getUserBindingServiceName(
VPC_NETWORKS_PLUGIN_NAME,
tunnel_id,
remoteProxyConnectionString
),
worker: remoteProxyClientWorker(remoteProxyConnectionString, name),
};
}
);
},
};
38 changes: 20 additions & 18 deletions packages/workers-utils/src/config/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import type { Json } from "../types";
* This could be the top-level default environment, or a specific named environment.
*/
export interface Environment
extends EnvironmentInheritable, EnvironmentNonInheritable {}
extends EnvironmentInheritable,
EnvironmentNonInheritable {}

type SimpleRoute = string;
export type ZoneIdRoute = {
Expand Down Expand Up @@ -1113,23 +1114,6 @@ export interface EnvironmentNonInheritable {
}
| undefined;

/**
* Binding to Cloudflare Stream
*
* NOTE: This field is not automatically inherited from the top level environment,
* and so must be specified in every named environment.
*
* @default {}
* @nonInheritable
*/
stream:
| {
binding: string;
/** Whether the Stream binding should be remote or not in local development */
remote?: boolean;
}
| undefined;

/**
* Binding to the Worker Version's metadata
*/
Expand Down Expand Up @@ -1352,6 +1336,24 @@ export interface EnvironmentNonInheritable {
/** Whether the VPC service is remote or not */
remote?: boolean;
}[];

/**
* Specifies VPC networks that are bound to this Worker environment.
*
* NOTE: This field is not automatically inherited from the top level environment,
* and so must be specified in every named environment.
*
* @default []
* @nonInheritable
*/
vpc_networks: {
/** The binding name used to refer to the VPC network in the Worker. */
binding: string;
/** The tunnel ID of the Cloudflare Tunnel to route traffic through. */
tunnel_id: string;
/** Whether the VPC network is remote or not */
remote?: boolean;
}[];
}

/**
Expand Down
69 changes: 53 additions & 16 deletions packages/workers-utils/src/config/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ export type ConfigBindingFieldName =
| "browser"
| "ai"
| "images"
| "stream"
| "media"
| "version_metadata"
| "unsafe"
Expand All @@ -103,7 +102,8 @@ export type ConfigBindingFieldName =
| "assets"
| "unsafe_hello_world"
| "worker_loaders"
| "vpc_services";
| "vpc_services"
| "vpc_networks";

/**
* @deprecated new code should use getBindingTypeFriendlyName() instead
Expand All @@ -125,7 +125,6 @@ export const friendlyBindingNames: Record<ConfigBindingFieldName, string> = {
browser: "Browser",
ai: "AI",
images: "Images",
stream: "Stream",
media: "Media",
version_metadata: "Worker Version Metadata",
unsafe: "Unsafe Metadata",
Expand All @@ -141,6 +140,7 @@ export const friendlyBindingNames: Record<ConfigBindingFieldName, string> = {
unsafe_hello_world: "Hello World",
worker_loaders: "Worker Loader",
vpc_services: "VPC Service",
vpc_networks: "VPC Network",
} as const;

/**
Expand All @@ -159,7 +159,6 @@ const bindingTypeFriendlyNames: Record<Binding["type"], string> = {
browser: "Browser",
ai: "AI",
images: "Images",
stream: "Stream",
version_metadata: "Worker Version Metadata",
data_blob: "Data Blob",
durable_object_namespace: "Durable Object",
Expand All @@ -181,6 +180,7 @@ const bindingTypeFriendlyNames: Record<Binding["type"], string> = {
ratelimit: "Rate Limit",
worker_loader: "Worker Loader",
vpc_service: "VPC Service",
vpc_network: "VPC Network",
media: "Media",
assets: "Assets",
inherit: "Inherited",
Expand Down Expand Up @@ -702,7 +702,7 @@ function normalizeAndValidateDev(
inspector_ip,
local_protocol = localProtocolArg ?? "http",
// In remote mode upstream_protocol must be https, otherwise it defaults to local_protocol.
upstream_protocol = (upstreamProtocolArg ?? remoteArg)
upstream_protocol = upstreamProtocolArg ?? remoteArg
? "https"
: local_protocol,
host,
Expand Down Expand Up @@ -1805,16 +1805,6 @@ function normalizeAndValidateEnvironment(
validateNamedSimpleBinding(envName),
undefined
),
stream: notInheritable(
diagnostics,
topLevelEnv,
rawConfig,
rawEnv,
envName,
"stream",
validateNamedSimpleBinding(envName),
undefined
),
media: notInheritable(
diagnostics,
topLevelEnv,
Expand Down Expand Up @@ -1885,6 +1875,16 @@ function normalizeAndValidateEnvironment(
validateBindingArray(envName, validateVpcServiceBinding),
[]
),
vpc_networks: notInheritable(
diagnostics,
topLevelEnv,
rawConfig,
rawEnv,
envName,
"vpc_networks",
validateBindingArray(envName, validateVpcNetworkBinding),
[]
),
version_metadata: notInheritable(
diagnostics,
topLevelEnv,
Expand Down Expand Up @@ -2930,7 +2930,7 @@ const validateUnsafeBinding: ValidatorFn = (diagnostics, field, value) => {
"pipeline",
"worker_loader",
"vpc_service",
"stream",
"vpc_network",
"media",
];

Expand Down Expand Up @@ -3994,6 +3994,43 @@ const validateVpcServiceBinding: ValidatorFn = (diagnostics, field, value) => {
return isValid;
};

const validateVpcNetworkBinding: ValidatorFn = (diagnostics, field, value) => {
if (typeof value !== "object" || value === null) {
diagnostics.errors.push(
`"vpc_networks" bindings should be objects, but got ${JSON.stringify(
value
)}`
);
return false;
}
let isValid = true;
// VPC network bindings must have a binding and a tunnel_id.
if (!isRequiredProperty(value, "binding", "string")) {
diagnostics.errors.push(
`"${field}" bindings should have a string "binding" field but got ${JSON.stringify(
value
)}.`
);
isValid = false;
}
if (!isRequiredProperty(value, "tunnel_id", "string")) {
diagnostics.errors.push(
`"${field}" bindings must have a string "tunnel_id" field but got ${JSON.stringify(
value
)}.`
);
isValid = false;
}

validateAdditionalProperties(diagnostics, field, Object.keys(value), [
"binding",
"tunnel_id",
"remote",
]);

return isValid;
};

/**
* Check that bindings whose names might conflict, don't.
*
Expand Down
18 changes: 11 additions & 7 deletions packages/workers-utils/src/map-worker-metadata-bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,6 @@ export function mapWorkerMetadataBindings(
};
}
break;
case "stream":
{
configObj.stream = {
binding: binding.name,
};
}
break;
case "media":
{
configObj.media = {
Expand Down Expand Up @@ -350,6 +343,17 @@ export function mapWorkerMetadataBindings(
];
}
break;
case "vpc_network":
{
configObj.vpc_networks = [
...(configObj.vpc_networks ?? []),
{
binding: binding.name,
tunnel_id: binding.tunnel_id,
},
];
}
break;
default: {
configObj.unsafe = {
bindings: [...(configObj.unsafe?.bindings ?? []), binding],
Expand Down
Loading
Loading