cloudflare / cloudflare/workerd
Containers local dev sidecar TPROXY rules intercept Docker bridge traffic
- Dominant language
- C++
- Stars
- 8.7k
- Forks
- 739
- Avg merge
- 2d 20h
- Merged PRs (30d)
- 174
Description
### 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:
```sh
bun install
bun run dev
```
In another terminal:
```sh
curl -sS http://127.0.0.1:5173/egress
bun run inspect:sidecar
```
The `/egress` endpoint starts the container and makes it call:
```text
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:
```text
-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:
```text
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:
```sh
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:
```text
-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
```text
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
```
Contributor guide
Assessment
This issue has not been assessed yet.