Skip to content

Restore isolated Docker network for managed containers#6208

Open
dknecht wants to merge 1 commit intomainfrom
codex/fix-security-vulnerability-in-docker-bridge
Open

Restore isolated Docker network for managed containers#6208
dknecht wants to merge 1 commit intomainfrom
codex/fix-security-vulnerability-in-docker-bridge

Conversation

@dknecht
Copy link
Member

@dknecht dknecht commented Feb 27, 2026

Motivation

  • A recent change attached workerd-managed containers to Docker’s default bridge, removing the isolation boundary and allowing lateral access to other containers on the host.
  • The intent of this PR is to restore the prior isolation by ensuring workerd-managed containers join a dedicated workerd-network bridge instead of the shared default.

Description

  • Reintroduced WORKERD_NETWORK_NAME and createWorkerdNetwork() to create a dedicated workerd-network bridge (with IPv6 enabled according to daemon capability) when missing.
  • Changed getDockerBridgeIPAMConfig() to inspect /networks/workerd-network and create it on-demand if it does not exist, returning gateway and subnet from that network.
  • Call createWorkerdNetwork() before container creation and set HostConfig.NetworkMode to WORKERD_NETWORK_NAME so managed containers are attached to the dedicated network.
  • Added an atomic guard workerdNetworkCreated and corresponding header declarations to avoid races when creating the network.

Testing

  • Attempted to build the modified target with bazel build //src/workerd/server:container-client, but the Bazel bootstrap download was blocked in this environment (Forbidden), so the build could not be completed.
  • No automated tests were executed here due to the environment bootstrap/network restrictions; the change is intentionally minimal to preserve existing behavior while restoring network isolation.

Codex Task

Copy link
Contributor

@ask-bonk ask-bonk bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm Bonk, and I've done a quick review of your PR.

This PR restores network isolation for workerd-managed Docker containers by creating a dedicated workerd-network bridge instead of using the shared default bridge.

Issues (ranked by severity)

  1. [High] Atomic guard is broken across concurrent coroutinesworkerdNetworkCreated uses exchange(true, acquire) as a "run once" guard, matching the pattern of other guards in this file. However, createWorkerdNetwork() is called from two sites: once from getDockerBridgeIPAMConfig() (on 404) and once from createContainer(). Because the guard fires on the first call, the second call site becomes a no-op regardless of whether the first actually succeeded. If the first call comes from getDockerBridgeIPAMConfig() and the network already exists (409), this is fine. But if the first call fails (non-201, non-409) and the KJ_ON_SCOPE_FAILURE resets the flag, a concurrent coroutine that already read true from the exchange will have silently skipped creation. The real issue is simpler though — see issue #2.

  2. [High] createWorkerdNetwork() is called redundantly from getDockerBridgeIPAMConfig()getDockerBridgeIPAMConfig() already handles a 404 by calling createWorkerdNetwork() then re-inspecting. Then createContainer() also calls createWorkerdNetwork() just before building the host config. Because the atomic guard makes the second call a no-op this is merely wasteful, but it makes the control flow confusing and fragile. The call in createContainer() should be removed — getDockerBridgeIPAMConfig() (already called earlier in the start() flow) handles on-demand creation.

  3. [Medium] Missing space in error message — The string literal concatenation on line 449 produces "Failed to create workerd-network.Status: ..." (no space after the period). C++ concatenates adjacent string literals.

  4. [Low] IPv6 warning is noisy for the common case — Most Docker installations have IPv6 disabled by default. Logging a WARNING on every network creation will generate noise. Consider downgrading to INFO or only warning once.


if (response.statusCode != 201 && response.statusCode != 409) {
JSG_FAIL_REQUIRE(Error,
"Failed to create workerd-network."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space: adjacent string literals are concatenated by the compiler, producing "Failed to create workerd-network.Status: ". Add a space (or newline) after the period.

Suggested change
"Failed to create workerd-network."
"Failed to create workerd-network. "

jsonEnv.set(envSize + i, defaultEnv[i]);
}

co_await createWorkerdNetwork();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createWorkerdNetwork() is already called on-demand inside getDockerBridgeIPAMConfig() when the network doesn't exist (404 → create → re-inspect). Since getDockerBridgeIPAMConfig() is called earlier in the start() flow (via ensureSidecarStarted / ensureEgressListenerStarted), this explicit call is redundant. If the intent is to guarantee the network exists even when those paths haven't run, this should at least have a comment explaining why.

If it is truly just a safety net, consider removing it to keep the control flow clear:

Suggested change
co_await createWorkerdNetwork();

jsonRoot.setEnableIpv6(ipv6Enabled);
if (!ipv6Enabled) {
KJ_LOG(WARNING,
"Docker has IPv6 disabled. Connections to Workers via IPv6 won't work. If you want to enable IPv6, remove the workerd-network after re-enabling.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most Docker installations have IPv6 disabled by default, so this WARNING will fire on virtually every network creation. Consider downgrading to INFO, or gating on a debug flag, to avoid log noise in the common case.

@ask-bonk
Copy link
Contributor

ask-bonk bot commented Feb 27, 2026

Review posted successfully on PR #6208. Here's a summary of what I flagged:

  1. [High] Atomic guard + dual call sitescreateWorkerdNetwork() is called from both getDockerBridgeIPAMConfig() (on 404) and createContainer(). The atomic guard makes the second a no-op, but this creates confusing control flow and a subtle race window if the first call fails while a concurrent coroutine already passed the guard.

  2. [High] Redundant createWorkerdNetwork() call in createContainer() — Since getDockerBridgeIPAMConfig() already handles on-demand network creation, the explicit call in createContainer() is redundant and should be removed. Posted a suggestion to delete it.

  3. [Medium] Missing space in error message"Failed to create workerd-network.""Status: " concatenates without a space. Posted a suggestion fix.

  4. [Low] Noisy IPv6 warningKJ_LOG(WARNING, ...) fires on every network creation when IPv6 is disabled (the common default). Suggested downgrading to INFO.

github run

@anonrig anonrig requested a review from gabivlj February 27, 2026 21:25
@gabivlj
Copy link
Contributor

gabivlj commented Feb 27, 2026

Hey, workerd-network was created to add a ipv6 network for the interceptOutboundHttp feature in local development through wrangler dev. It was deemed not necessary for the feature anymore, as default docker bridge suffices for networking.

@gabivlj
Copy link
Contributor

gabivlj commented Feb 27, 2026

Hey, workerd-network was created to add a ipv6 network for the interceptOutboundHttp feature in local development through wrangler dev. It was deemed not necessary for the feature anymore, as default docker bridge suffices for networking.

In other words, we are not really worried about container network isolation in local development.

@codecov-commenter
Copy link

Codecov Report

❌ Patch coverage is 0% with 36 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.56%. Comparing base (a84fcaa) to head (42ca848).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
src/workerd/server/container-client.c++ 0.00% 36 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6208      +/-   ##
==========================================
- Coverage   70.58%   70.56%   -0.03%     
==========================================
  Files         413      413              
  Lines      109992   110024      +32     
  Branches    18120    18125       +5     
==========================================
  Hits        77640    77640              
- Misses      21544    21575      +31     
- Partials    10808    10809       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants