Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

送信トラフィックを処理する

最終更新 Markdown で表示Agent セットアップ

送信ハンドラーを使うと、信頼できるコードで Container からの HTTP トラフィックを傍受して変更できます。

次の用途に使えます。

  • 特定のオリジン宛先を許可または拒否する
  • 認可ヘッダーやトークンを安全に注入する
  • トラフィックを透過的に再ルーティングする
  • 送信トラフィックにカスタムポリシーを追加する(特定の HTTP リクエストを拒否するなど)
  • KV、R2、Durable Objects などの Workers バインディングに接続する

送信トラフィックをブロックする

enableInternet = false を使い、デフォルトでパブリックインターネットへのアクセスをブロックします。

import { Container } from "@cloudflare/containers";

export class MyContainer extends Container {
	enableInternet = false;
}

enableInternetfalse のとき、このページの後半で allowedHosts または送信ハンドラーによって明示的に許可したトラフィックだけが Container から出られます。利用できるのはポート 80443、および DNS だけで、DNS クエリは Cloudflare の DNS サーバーを使います。

ホスト単位でトラフィックを許可または拒否する

Container クラスの allowedHostsdeniedHosts プロパティで、送信トラフィックをフィルターできます。

allowedHosts を設定すると、デフォルト拒否の許可リストになります。リストにないホストや IP は拒否され、一致する宛先だけが outbound または outboundByHost ハンドラーに到達できます。

allowedHostsdeniedHosts は、単純なグロブパターンもサポートします。* は任意の文字シーケンスに一致します。

デフォルトでは Container はインターネットアクセスを許可します。特定のホストや IP を拒否するには deniedHosts を設定します。

import { Container, ContainerProxy } from "@cloudflare/containers";
export { ContainerProxy };

export class MyContainer extends Container {
	// Make sure the container trusts /etc/cloudflare/certs/cloudflare-containers-ca.crt
	interceptHttps = true;
	deniedHosts = ["some-nefarious-website.com", "141.101.64.0/18"];
}

デフォルトでインターネットアクセスを無効にし、特定のホストと IP だけを許可することもできます。

import { Container, ContainerProxy } from "@cloudflare/containers";
export { ContainerProxy };

export class MyContainer extends Container {
	// Make sure the container trusts /etc/cloudflare/certs/cloudflare-containers-ca.crt
	interceptHttps = true;

	// default internet access to off unless overridden by 'allowedHosts' or outbound proxy
	enableInternet = false;

	// overrides enableInternet = false
	allowedHosts = ["allowed.com"];
}

送信ハンドラーを定義する

送信ハンドラーは、Container と同じマシンで動く、プログラム可能な送信プロキシです。すべての Workers バインディングにアクセスできます。

outbound を使い、すべての HTTP および HTTPS トラフィックを傍受します。

import { Container, ContainerProxy } from "@cloudflare/containers";
export { ContainerProxy };

export class MyContainer extends Container {
	interceptHttps = true;
}

MyContainer.outbound = async (request, env, ctx) => {
	if (request.method !== "GET") {
		console.log(`Blocked ${request.method} to ${request.url}`);
		return new Response("Method Not Allowed", { status: 405 });
	}
	return fetch(request);
};

outboundByHost を使い、特定のドメイン名または IP アドレスをプロキシ関数へ対応付けます。

import { Container, ContainerProxy } from "@cloudflare/containers";
export { ContainerProxy };

export class MyContainer extends Container {
	interceptHttps = true;
}

MyContainer.outboundByHost = {
	"my.worker": async (request, env, ctx) => {
		// Run arbitrary Workers logic from this hostname
		return await someWorkersFunction(request.body);
	},
};

Container から http://my.worker への呼び出しはハンドラーを起動します。ハンドラーは Workers ランタイム内、Container サンドボックスの外で実行されます。

deniedHostsallowedHosts は、どの送信ハンドラーよりも先に評価されます。allowedHosts を使う場合は、outbound または outboundByHost が動くように、そのホスト名をリストに含めてください。outboundByHost ハンドラーは、キャッチオールの outbound ハンドラーより優先されます。

認証情報を安全に注入する

送信ハンドラーは Workers ランタイム(Container サンドボックスの外)で動くため、Container 自身には見えないシークレットを保持できます。Container は平文の HTTP リクエストを送り、ハンドラーが認証情報を付けてから上流サービスへ転送します。

export class MyContainer extends Container {
	// Make sure the container trusts /etc/cloudflare/certs/cloudflare-containers-ca.crt
	interceptHttps = true;
}

MyContainer.outboundByHost = {
	"github.com": (request, env, ctx) => {
		const requestWithAuth = new Request(request);
		requestWithAuth.headers.set("x-auth-token", env.SECRET);
		return fetch(requestWithAuth);
	},
};

これは、Container 内で動くコードを完全には信頼できないエージェント型ワークロードで特に役立ちます。このパターンでは次のとおりです。

  • トークンは Container に公開されません。 シークレットは Worker の環境にあり、サンドボックスへ渡されません。
  • Container 内でトークンをローテーションする必要はありません。 Worker の環境でシークレットをローテーションすると、以降のリクエストはすぐに新しい値を使います。
  • ホスト単位およびインスタンス単位のルール。 outboundByHostctx.containerId を組み合わせて、認証情報や権限を特定の Container インスタンスに限定できます。

ここでは、ctx.containerId で KV からインスタンス単位のキーを取得します。

export class MyContainer extends Container {
	// Make sure the container trusts /etc/cloudflare/certs/cloudflare-containers-ca.crt
	interceptHttps = true;
}

MyContainer.outboundByHost = {
	"my-internal-vcs.dev": async (request, env, ctx) => {
		const authKey = await env.KEYS.get(ctx.containerId);

		const requestWithAuth = new Request(request);
		requestWithAuth.headers.set("x-auth-token", authKey);
		return fetch(requestWithAuth);
	},
};

HTTPS トラフィック

デフォルトでは、HTTPS トラフィックは送信ハンドラーで傍受されません。オプトインするには interceptHttps 属性を設定します。

export class MyContainer extends Container {
	// Make sure the container trusts /etc/cloudflare/certs/cloudflare-containers-ca.crt
	interceptHttps = true;
}

MyContainer.outbound = (req, env, ctx) => {
	// All HTTP(S) requests will trigger this hook.
	return fetch(req);
};

これは、信頼できないトラフィックを Container インスタンスから Workers へリダイレクトし、フィルタリングと変更を行う Sandbox のようなサービスで役立ちます。

HTTPS 傍受が有効なとき、Container の起動時に一時的な CA ファイルが /etc/cloudflare/certs/cloudflare-containers-ca.crt に作成されます。CA が注入されるのは、interceptHttps = true を設定し、かつ outbound または outboundByHost ハンドラーを定義した場合だけです。

CA 証明書を信頼する

HTTPS 傍受を動かすには、CA ファイルを信頼する必要があります。CA はエフェメラルでランタイムにだけ存在するため、docker build 中にイメージへ焼き込まないでください。代わりに、ディストリビューションの信頼ストアへコピーし、アプリケーション起動前に Container の entrypoint から信頼ストアを更新します。

ベースイメージに信頼ストア用のツールが含まれていない場合は、先にイメージへディストリビューションの ca-certificates パッケージをインストールします。

import { Container, ContainerProxy } from "@cloudflare/containers";
export { ContainerProxy };

export class MyContainer extends Container {
	interceptHttps = true;
	entrypoint = [
		"sh",
		"-lc",
		[
			"cp /etc/cloudflare/certs/cloudflare-containers-ca.crt /usr/local/share/ca-certificates/cloudflare-containers-ca.crt",
			"update-ca-certificates",
			"exec node server.js",
		].join(" && "),
	];
}
import { Container, ContainerProxy } from "@cloudflare/containers";
export { ContainerProxy };

export class MyContainer extends Container {
	interceptHttps = true;
	entrypoint = [
		"sh",
		"-lc",
		[
			"cp /etc/cloudflare/certs/cloudflare-containers-ca.crt /usr/local/share/ca-certificates/cloudflare-containers-ca.crt",
			"update-ca-certificates",
			"exec node server.js",
		].join(" && "),
	];
}
import { Container, ContainerProxy } from "@cloudflare/containers";
export { ContainerProxy };

export class MyContainer extends Container {
	interceptHttps = true;
	entrypoint = [
		"sh",
		"-lc",
		[
			"cp /etc/cloudflare/certs/cloudflare-containers-ca.crt /etc/pki/ca-trust/source/anchors/cloudflare-containers-ca.crt",
			"update-ca-trust",
			"exec node server.js",
		].join(" && "),
	];
}
import { Container, ContainerProxy } from "@cloudflare/containers";
export { ContainerProxy };

export class MyContainer extends Container {
	interceptHttps = true;
	entrypoint = [
		"sh",
		"-lc",
		[
			"cp /etc/cloudflare/certs/cloudflare-containers-ca.crt /etc/ca-certificates/trust-source/anchors/cloudflare-containers-ca.crt",
			"trust extract-compat",
			"exec node server.js",
		].join(" && "),
	];
}

node server.js は、アプリケーションを起動するコマンドに置き換えてください。

ほとんどのランタイムは、システムのルートストア経由で CA を自動的に信頼します。ランタイムが独自の CA バンドルを使う場合は、NODE_EXTRA_CA_CERTSREQUESTS_CA_BUNDLE などで /etc/cloudflare/certs/cloudflare-containers-ca.crt を直接指定します。

HTTP 以外のトラフィック

送信ハンドラーが傍受するのは HTTP と HTTPS トラフィックだけです。ポート 80443 以外のトラフィックは、outboundoutboundByHost を通りません。

enableInternet = false を設定すると、そのトラフィックは拒否されます。例外は DNS クエリだけですが、宛先は Cloudflare の DNS サーバーに限られます。任意の DNS 宛先を使ったデータ持ち出しを防ぐためです。

実行時にポリシーを変更する

outboundHandlers で名前付きハンドラーを定義し、setOutboundByHost() で実行時に特定のホストへ割り当てます。setOutboundHandler() でハンドラーをグローバルに適用することもできます。

実行時ポリシーは、setOutboundByHosts()setAllowedHosts()setDeniedHosts()allowHost()denyHost()removeAllowedHost()removeDeniedHost() でも管理できます。

これにより、信頼できる Worker が認証情報を保持し、信頼できない Container には公開しません。

export class MyContainer extends Container {
	// Make sure the container trusts /etc/cloudflare/certs/cloudflare-containers-ca.crt
	interceptHttps = true;
}

MyContainer.outboundHandlers = {
	authenticatedGithub: async (request, env, ctx) => {
		const githubToken = env.GITHUB_TOKEN;
		return authenticateGitHttpsRequest(request, githubToken, ctx.containerId);
	},
};

Worker からプログラムでホストにハンドラーを適用します。

async setUpContainer(req, env) {
  const container = await env.MY_CONTAINER.getByName("my-instance");

  // Give the container access to github.com on a specific host during setup
  await container.setOutboundByHost("github.com", "authenticatedGithub");

	// do something with github.com on your container...
}

async removeAccessToGithub(req, env) {
  const container = await env.MY_CONTAINER.getByName("my-instance");

  // Remove access to Github
  await container.removeOutboundByHost("github.com");
}

ハンドラーの優先順位

リクエストは次の順で評価されます。

  1. 最初に deniedHosts を確認します。一致するホストまたは IP は直ちに拒否されます。
  2. 次に allowedHosts を確認します。設定されている場合、リストにないホストまたは IP は拒否されます。一致するホストは送信ハンドラーへ進むか、ハンドラーがなければパブリックインターネットへ送信されます。
  3. setOutboundByHost() で設定したインスタンス単位のルールは、クラス単位の outboundByHost ルールより先に確認されます。
  4. ホスト単位のハンドラーは常にキャッチオールハンドラーより優先されるため、outboundByHostoutbound より先に実行されます。
  5. setOutboundHandler() で設定したインスタンス単位のハンドラーは、クラス単位の outbound ハンドラーより先に確認されます。
  6. どのハンドラーにも一致しない場合でも、allowedHosts に一致したか enableInternet = true なら、リクエストはパブリックインターネットへ送信できます。それ以外は拒否されます。

低レベル API

ctx.container で送信傍受を直接設定するには、特定のホスト名グロブ、IP、または CIDR 範囲に interceptOutboundHttp を使うか、すべてのトラフィックに interceptAllOutboundHttp を使います。どちらも WorkerEntrypoint を受け取ります。

import { WorkerEntrypoint } from "cloudflare:workers";

export class MyOutboundWorker extends WorkerEntrypoint {
	fetch(request) {
		// Inspect, modify, or deny the request before passing it on
		return fetch(request);
	}
}

// Inside your Container DurableObject
this.ctx.container.start({ enableInternet: false });
const worker = this.ctx.exports.MyOutboundWorker({ props: {} });
await this.ctx.container.interceptAllOutboundHttp(worker);

これらのメソッドは、Container の起動前でも起動後でも、接続が開いている最中でも呼び出せます。進行中の TCP 接続は新しいハンドラーを自動的に取り込みます。接続は切断されません。

// Intercept a specific CIDR range
await this.ctx.container.interceptOutboundHttp("203.0.113.0/24", worker);
// Intercept by hostname
this.ctx.container.interceptOutboundHttp("foo.com", worker);

// Update the handler while the container is running
const updated = this.ctx.exports.MyOutboundWorker({
	props: { phase: "post-install" },
});
await this.ctx.container.interceptOutboundHttp("203.0.113.0/24", updated);

HTTPS では、interceptOutboundHttpsinterceptOutboundHttp と同じように動作します。

// Intercept a specific hostname
this.ctx.container.interceptOutboundHttps("foo.com", worker);

// Intercept all traffic
this.ctx.container.interceptOutboundHttps("*", worker);

Container クラスは、上記の関数を使うときにこれらのメソッドを自動的に呼び出します。クラスがカバーしないケースでは、直接呼び出すこともできます。

ローカル開発

wrangler dev は送信傍受をサポートします。Container のネットワーク名前空間内でサイドカープロセスが起動します。一致するトラフィックをローカルの Workerd インスタンスへルーティングする TPROXY ルールを適用し、本番の動作を再現します。

関連リソース

役に立ちましたか?