Reconcile network isolation with network mode#5794
Open
JAORMX wants to merge 2 commits into
Open
Conversation
Network isolation is a bridge-topology construct: it builds a per-workload internal-only network plus DNS and egress/ingress Squid sidecars, and only works when the container lives on a Docker-managed bridge. Since isolation became the default (v0.30.1), running a server with --network host silently built those sidecars attached only to the internal network with no route out, so all DNS and outbound traffic broke on the next re-create. --network none hit the same path. Isolation cannot be enforced for host/none/custom modes, so drop it there instead of building broken sidecars. How loudly depends on provenance: warn when isolation was on only by default (restores the pre-v0.30.1 behaviour host-mode servers relied on), fail fast when the user explicitly passed --isolate-network=true (a genuinely contradictory request). "none" is already maximally confined, so its drop is silent. The rule is enforced at three layers so no path can report a workload started with non-functional isolation: the docker client (structural backstop for every caller), the config builder (owns the warn-vs-error decision, the only place that knows whether the flag was explicit), and config load (self-heals legacy persisted configs on restart, which bypass the builder). Docker/Podman only; the Kubernetes runtime ignores both fields. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #5794 +/- ##
==========================================
+ Coverage 70.76% 70.85% +0.08%
==========================================
Files 685 685
Lines 69435 69484 +49
==========================================
+ Hits 49139 49234 +95
+ Misses 16701 16660 -41
+ Partials 3595 3590 -5 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
aponcedeleonch
previously approved these changes
Jul 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Network isolation is a bridge-topology construct:
--isolate-networkbuilds aper-workload internal-only network plus DNS (dnsmasq) and egress/ingress
(Squid) sidecars, and only works when the MCP container lives on a
Docker-managed bridge. Since isolation became the default in v0.30.1 (#5583),
running a server with
--network hostsilently built those sidecars attachedonly to the internal (no-route-out) network, so all DNS and outbound traffic
broke on the next re-create — with no error at deploy time.
--network nonehit the same path. A host-networked container also can't be forced through the
sidecars at all (it shares the host netns), so the isolation was both broken
and illusory.
Because isolation cannot be enforced for
host/none/custom modes, thisdrops it there instead of building broken sidecars — and never reports a
workload as successfully started with non-functional isolation.
host/custom, isolation on only by default → drop + warn (restores thepre-v0.30.1 behaviour host-mode servers relied on).
host/custom with an explicit--isolate-network=true→ fail fast withan error naming both exits (a genuinely contradictory request).
none→ drop silently (debug); it is already maximally confined, soisolation is merely redundant, not a security reduction.
client (
DeployWorkload, structural backstop for every caller includingrestart), the config builder (owns the warn-vs-error decision — the only
place that knows whether the flag was explicit), and config load (
ReadJSON,self-heals legacy persisted configs so
thv list/inspect stop reportingisolation that isn't enforced).
networking.IsBridgeModepredicate + shared log-message constants sothe rule lives in one place.
docs/arch/05-runconfig-and-permissions.mdand disclosure on the
--network/--isolate-networkflag help.Scope is Docker/Podman only — the Kubernetes runtime ignores both fields, so
the operator is unaffected.
Closes #5775
Type of change
Test plan
task test)task lint-fix)New coverage: flag→builder explicitness wiring (
cmd/thv/app/run_flags_test.go),docker-layer assertion that no DNS/egress/ingress sidecars and no
HTTP_PROXYare created for host/none (
pkg/container/docker/client_deploy_test.go),builder reconciliation matrix and
ReadJSONdegrade (pkg/runner/*_test.go),and the upgrade-path degrade (
pkg/workloads/upgrade/applier_test.go).Changes
pkg/container/docker/client.goisolate && IsBridgeMode; effective-isolation used for MCP/ingress/labelpkg/runner/config_builder.goreconcileNetworkIsolation(fail-fast vs degrade) +WithNetworkIsolationExplicitoptionpkg/runner/config.goReadJSONdegrade for legacy persisted configspkg/networking/utilities.goIsBridgeModepredicate + log-message constantscmd/thv/app/run_flags.gocmd.Flags().Changed("isolate-network");--networkhelp disclosurepkg/workloads/upgrade/applier.godocs/arch/05-runconfig-and-permissions.md,docs/cli/thv_run.mdDoes this introduce a user-facing change?
Yes.
thv run --network hostno longer silently loses outbound connectivity:network isolation (on by default) is now dropped for host/none networking with
a warning, restoring the pre-v0.30.1 behaviour. Explicitly combining
--isolate-network=truewith--network hostnow fails fast with anexplanatory error instead of starting a broken workload.
Implementation plan
Approved implementation plan
Reasoned from mechanics rather than patching the symptom:
DNS/Squid sidecars; every enforcement mechanism requires the container on a
Docker-managed bridge.
hostshares the host netns(bypasses all Docker network controls),
noneis loopback-only.--network hostdoesn't mean the user waived isolation — it meansisolation can't be enforced. So the default is really a bridge-mode
default; for non-bridge modes it must be dropped, loudly only when dropping it
reduces security.
--isolate-networksource""/defaulthost/customhost/customtruenoneThree defense-in-depth layers: docker-client structural gate (unbypassable,
covers restart) + creation-time policy (owns error-vs-warn via the explicit
bit) + load-time degrade (legacy persisted configs / display honesty).
Reviewed by a spec/standards/security/architecture/UX/DevEx/duplication/
library-reuse/QA panel; findings folded in (predicate + message consolidation,
message precision, flag-help disclosure, doc cross-refs, and the added tests).
Special notes for reviewers
DeployWorkloadre-derives theeffective isolation from the network mode, so the load-time degrade only
affects
list/inspect display accuracy, not the actual security boundary.network-mode field in the API).
degrade today; a fresh explicit conflict errors before it can persist).
--network hostoutbound (platform-dependent;host mode isn't supported on VM-based Docker).
🤖 Generated with Claude Code