Repository navigation
Require dns+nft egress mode for OpenSandbox network policies - #74504
Merged
Merged
Conversation
zozo123
requested review from
Lee-W,
ashb,
gopidesupavan and
kaxil
as code owners
October 9, 2026 16:10
kaxil
reviewed
Oct 12, 2026
The OpenSandbox server runs its egress sidecar in "dns" mode by default (EgressConfig.mode, and every example configuration), where it filters name resolution and nothing else. A sandbox created from a default SandboxSpec() could therefore still connect to any numeric address, including a DNS-over-HTTPS resolver, while the backend's read-back passed, because get_egress_policy() returns the same rules in "dns" and "dns+nft" mode. The sidecar's own /policy response names the mode, so the backend can keep the deny-by-default contract SandboxSpec promises instead of leaving it to a server setting nobody is told about.
zozo123
force-pushed
the
docs/opensandbox-egress-nft
branch
from
October 12, 2026 13:38
b6cb55f to
1c2d5fb
Compare
kaxil
approved these changes
Oct 13, 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.
Behaviour change. On an OpenSandbox server with the default
[egress] mode = "dns",OpenSandboxBackendnow refuses a deny-all or allowlistSandboxSpecat create, including the defaultSandboxSpec(). The error names the fix: set[egress] mode = "dns+nft"in the server config, or passSandboxSpec(block_network=False)for a sandbox that may reach the network. The backend is Experimental, so the stability policy allows this in a minor release. The changelog note under theChangelogheader announces it, asstability.rstrequires.Why.
dnsis the server's default (EgressConfig.mode) and is what every example config ships. In that mode the egress sidecar filters name resolution and nothing else, so a "no network" sandbox can still connect to any numeric address._verify_network_policypassed anyway, becauseSandboxSync.get_egress_policy()returns the same rules indnsanddns+nftmode. That breaks theSandboxSpeccontract that a backend which cannot enforce a field must raise rather than silently ignore it.Release context. apache-airflow-providers-common-ai 0.11.0 (RC vote #74459, ends 2026-10-11 14:47 UTC) ships the backend without this check. The caveat is on the RC thread: #74459 (comment). This lands in the next release.
Two questions a reviewer will ask:
opensandbox.api.egressclient? The sidecar'sGET /policyresponse carriesenforcementMode, but the high-levelget_egress_policy()keeps only the rules and drops that field.new_owned_transport(), so this usesconfig.model_copy(update={"transport": None}).with_transport_if_missing().transport. Non-200 responses go through the SDK's ownhandle_api_errorandrequire_parsed, so an HTTP error is reported as one. A missing or unreadable mode also fails closed.allow_egress_to_cidrsis still refused. Its message no longer claims that the mode cannot be read. Supporting CIDRs is possible now, but it needs Modal-style entry validation, so it is left for a follow-up.Measured against OpenSandbox server 0.2.3 with egress sidecar v1.1.7, running on Docker (colima), with
opensandbox==1.1.0. Each sandbox was created throughOpenSandboxBackend.create().What a sandbox can reach with the backend as it is on
main, which is byte-identical to 0.11.0rc1:dns, deny-alldns, allowpypi.orgdns+nft, deny-alldns+nft, allowpypi.org1.1.1.1:443,8.8.8.8:443GET 1.1.1.1:80301from Cloudflare301from CloudflareHEAD pypi.org/simple/200200In another
dns-mode deny-all sandbox created the same way, the system resolver could not look upexample.com. A DNS-over-HTTPS query tohttps://1.1.1.1/dns-queryreturned its address, andGET https://example.com/over that address returned200 OKwith a verified certificate. So the DNS-only filter is easy to get around.With this PR:
dns+nftserver: deny-all andallow_egress_to=["pypi.org"]are accepted, with the same reachability as the table above. The system testexample_sandbox_toolset_opensandbox.pysucceeds in 7.30 s. On 2026-10-08, againstdns+nftwith 0.11.0rc1, the timeout, kill and cleanup edge cases also passed.dnsserver: deny-all is refused after 2.6 s and the allowlist after 2.0 s, each with the error above. No sandbox container is left behind.Validation after review: all 85 OpenSandbox tests pass under Python 3.12 with
opensandbox==1.1.0; targeted mypy passes on source and tests; fast prek checks pass. The fixture now patchesHTTPTransport.handle_request, exercising transport, retries, generated client and parsing. New cases cover proxy environment variables, transient 502/503 responses and closing the owned transport without closing the sandbox's shared transport. All three new cases fail when the transport argument is removed.A missing enforcement mode now advises upgrading the egress sidecar to v1.0.1 or later. Configuration docs describe creation followed by destruction and task failure. The changelog conflict is resolved against current
main.Full manual prek and documentation builds were attempted but stopped during a fresh Python 3.11 CI-image build; they remain unverified locally. Targeted tests and type checks used the existing Python 3.12 CI image.
related: #71676, #74459
Was generative AI tooling used to co-author this PR?
Generated-by: Claude Code (Opus 5.5) and Codex (GPT-6) following the guidelines
{pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.