Skip to content

Containers local dev sidecar TPROXY rules intercept Docker bridge traffic #6793

@iGmainC

Description

@iGmainC

What happened?

In local development for Cloudflare Containers with outbound traffic interception enabled, the proxy-everything sidecar installs TPROXY rules in the mangle PREROUTING chain before excluding Docker bridge-to-bridge traffic.

This allows Docker bridge internal traffic, including local runtime / sidecar / container control-plane traffic, to enter the transparent proxy path. In a real local app this showed up as container readiness checks being aborted or timing out, and outbound interception callbacks becoming unreliable.

This report is intentionally separate from #6790. That issue describes a broader Sandbox terminal readiness timeout. This issue is narrower and focuses only on the local Containers sidecar networking rule ordering.

Minimal reproduction

Repository: https://github.com/iGmainC/workerd-container-sidecar-tproxy-repro

The repro contains only:

  • one Worker entrypoint
  • one Container Durable Object
  • one outboundByHost virtual host
  • one Node HTTP server in the container
  • one script that inspects the local sidecar iptables rules

Run:

bun install
bun run dev

In another terminal:

curl -sS http://127.0.0.1:5173/egress
bun run inspect:sidecar

The /egress endpoint starts the container and makes it call:

http://sidecar-tproxy-repro.internal/echo

That virtual host is handled by EgressReproContainer.outboundByHost.

Observed behavior

On affected local workerd builds, the sidecar mangle PREROUTING chain lacks a Docker bridge bypass before the TPROXY / DIVERT rules. The relevant rule order looks like:

-P PREROUTING ACCEPT
-A PREROUTING -p tcp -m socket -j DIVERT
-A PREROUTING -p tcp -j DOCKER_PROXY_ANYTHING_TPROXY
-A PREROUTING -p udp -m udp --dport 53 -j DOCKER_PROXY_DNS_TPROXY

In a larger local app using the same Containers outbound interception feature, this led to logs like:

Error checking if container is ready: The operation was aborted
[ERROR] e = kj/timer.c++:30: overloaded: operation timed out
[ERROR] Container failed to start

Expected behavior

The documented feature is that outbound requests from the container can be handled by Worker handlers using outboundByHost / outbound interception:

https://developers.cloudflare.com/containers/platform-details/outbound-traffic/

The local sidecar should not break local Docker bridge control-plane traffic while implementing that interception. Docker bridge-to-bridge traffic should be excluded before the generic TPROXY / DIVERT rules.

Local mitigation verified

Inserting a Docker bridge CIDR bypass before the sidecar proxy rules fixes the local behavior:

iptables -t mangle -I PREROUTING 1 \
  -s 172.17.0.0/16 \
  -d 172.17.0.0/16 \
  -j RETURN

With the rule installed first, the sidecar rule order becomes:

-P PREROUTING ACCEPT
-A PREROUTING -s 172.17.0.0/16 -d 172.17.0.0/16 -j RETURN
-A PREROUTING -p tcp -m socket -j DIVERT
-A PREROUTING -p tcp -j DOCKER_PROXY_ANYTHING_TPROXY
-A PREROUTING -p udp -m udp --dport 53 -j DOCKER_PROXY_DNS_TPROXY

The exact bridge CIDR may differ by Docker configuration. The runtime already knows the Docker bridge subnet when creating the sidecar, so this can be derived rather than hard-coded.

With this mitigation applied after sidecar start / recovery, local container startup and outbound interception callbacks complete normally.

Why this looks like a bug rather than expected behavior

The Containers docs describe local outbound interception as a way to route container outbound HTTP/HTTPS through Worker handlers. They do not document any requirement for application code to manually repair sidecar iptables rules or to avoid Docker bridge internal traffic.

The Containers FAQ also says user containers do not support arbitrary iptables manipulation, which reinforces that this should be handled by the local runtime / sidecar implementation rather than by application code.

Environment

OS: Linux
Docker: local Docker bridge network
wrangler: 4.95.0
@cloudflare/vite-plugin: 1.39.0
@cloudflare/containers: 0.3.4
Feature used: Containers outbound traffic interception / outboundByHost
Local dev command: vite --host 127.0.0.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions