[PR #207] [MERGED] feat(network): support domain-based egress control #252

Closed
opened 2026-10-29 14:21:29 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/kvcache-ai/AgentENV/pull/207
Author: @LSX-s-Software
Created: 8/22/2026
Status: ✅ Merged
Merged: 8/24/2026
Merged by: @yingdi-shan

Base: main ← Head: feat/domain-based-egress-control


📝 Commits (5)

  • b816db7 feat(network): support domain-based egress control
  • 9b4fbb9 refactor(network): store egress CIDRs as IP networks
  • 8e6790f refactor(network): use bounded host DNS resolver
  • d6d7946 refactor(network): use mutable slot lifecycle access to remove concurrency control
  • 1de7324 fix(network): reject malformed host headers

📊 Changes

16 files changed (+2249 additions, -105 deletions)

View changed files

📝 Cargo.lock (+42 -0)
📝 Cargo.toml (+4 -1)
📝 docs/src/SUMMARY.md (+1 -0)
📝 docs/src/concepts/sandboxes.md (+5 -1)
📝 docs/src/internals/architecture.md (+1 -10)
➕ docs/src/internals/networking.md (+194 -0)
📝 src/api/impls/sandbox.rs (+131 -17)
📝 src/sandbox/firecracker/sandbox.rs (+3 -3)
📝 src/sandbox/mod.rs (+4 -1)
➕ src/sandbox/network/egress_proxy.rs (+1103 -0)
📝 src/sandbox/network/manager.rs (+32 -17)
📝 src/sandbox/network/mod.rs (+6 -1)
📝 src/sandbox/network/policy.rs (+301 -27)
➕ src/sandbox/network/resolver.rs (+274 -0)
📝 src/sandbox/network/slot.rs (+80 -27)
📝 tests/integration/fc.rs (+68 -0)

📄 Description

What

Add E2B-compatible domain-based egress control for sandbox network policies. Domain entries in allowOut are now enforced for new HTTP/HTTPS connections through a namespace-local transparent proxy, with trusted host-side DNS resolution and lifecycle cleanup.

Why

CIDR-only rules cannot express the common requirement of allowing a sandbox to reach a named service while denying all other internet destinations. This change makes domain allowlists usable while preserving existing CIDR behavior, policy replacement semantics, and warm-pool reuse.

No linked issue; this PR follows the existing E2B-compatible network-policy contract.

Scope and non-goals

Included:

  • Domain matching for exact names, *.example.com, and * patterns.
  • Transparent interception on TCP ports 80 and 443, Host/SNI inspection, and host-network-namespace DNS resolution.
  • Explicit validation that domain allowlists include denyOut: ["0.0.0.0/0"].
  • Atomic/staged policy replacement, connection tracking, teardown, and warm-pool cleanup.
  • API behavior updates, architecture/networking documentation, and focused unit/integration coverage.

Excluded:

  • Domain inspection on non-HTTP/HTTPS TCP ports.
  • TLS termination, HTTP rewriting, or a new user-facing configuration knob.
  • Changes to unrelated sandbox lifecycle or storage behavior.

Design and behavior changes

NetworkManager owns a process-wide EgressProxy registry. Each namespace that needs proxy mediation gets its own listener on the fixed namespace-local port 15000; the listener is not exposed as a host port. Namespace NAT redirects only TCP/80 and TCP/443. The proxy recovers the original destination, parses HTTP Host or TLS SNI, resolves candidates in a long-lived host-netns resolver, applies absolute-deny and domain policy checks, then relays the connection without terminating TLS.

Policy updates stage the new proxy policy and commit filter/NAT rules as one namespace update. Existing connections retain the policy selected when accepted; tracked sockets are closed during proxy/slot teardown so namespace resources can be safely released and reused.

Compatibility and operations

  • Public API or generated protocol: Existing allowOut/denyOut fields are extended to accept domain patterns; no generated protocol changes.
  • Configuration or defaults: No new configuration; existing network address and deny settings remain authoritative.
  • Snapshot manifest, artifact layout, or storage format: N/A.
  • Upgrade and rollback: Nodes can be upgraded or rolled back with the existing network-policy API contract; policies without domain entries retain the prior path.
  • Host requirements, permissions, ports, or dependencies: Uses the existing Linux network namespace, iptables, and socket capabilities. Port 15000 is bound only inside each sandbox namespace.

Validation

  • make fmt (already run by the author)
  • make clippy (already run by the author)
  • make test-unit (already run by the author)
  • Relevant Rust integration tests (already run by the author)
  • make -C services test (not applicable; services/ is unchanged)
  • Generated clients/server regenerated with the documented make target (not applicable; generated API code is unchanged)
  • Documentation updated
  • Benchmarks or performance comparison completed (not applicable)

Commands and results:

The author already ran the applicable formatting, lint, unit-test, and integration-test checks before requesting this PR. They are intentionally not repeated here.

Skipped checks and reasons:

Services tests, code generation, and benchmarks were skipped because this change does not modify services, generated clients/server code, or benchmark targets.

Risks and reviewer notes

  • Review packet ordering and conntrack behavior in src/sandbox/network/policy.rs and src/sandbox/network/egress_proxy.rs; domain interception is intentionally limited to TCP/80 and TCP/443.
  • Host-side DNS resolution is a security boundary: guest DNS answers and original destination IPs are not trusted for domain rules, and platform/internal deny ranges remain non-overridable.
  • Proxy lifecycle and policy replacement are shared with warm-pool slot reuse; teardown closes tracked handlers before namespace resources are removed.
  • The implementation adds a small bounded per-connection handler pool and resolver worker; long-lived established streams are not given an idle timeout.

Checklist

  • The PR contains one coherent change and no unrelated formatting or refactoring.
  • New behavior is covered by tests, or I explained why testing is impractical.
  • Logs and examples contain no credentials, tokens, or private registry information.
  • I did not manually edit generated code without updating its source and regenerating it.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/kvcache-ai/AgentENV/pull/207 **Author:** [@LSX-s-Software](https://github.com/LSX-s-Software) **Created:** 8/22/2026 **Status:** ✅ Merged **Merged:** 8/24/2026 **Merged by:** [@yingdi-shan](https://github.com/yingdi-shan) **Base:** `main` ← **Head:** `feat/domain-based-egress-control` --- ### 📝 Commits (5) - [`b816db7`](https://github.com/kvcache-ai/AgentENV/commit/b816db7183532aa54947717b64523432cc18f5f1) feat(network): support domain-based egress control - [`9b4fbb9`](https://github.com/kvcache-ai/AgentENV/commit/9b4fbb9f62facadec6d0b43d2870603ed0848c7a) refactor(network): store egress CIDRs as IP networks - [`8e6790f`](https://github.com/kvcache-ai/AgentENV/commit/8e6790fa3f3d91047d7129d254b741ee12727e9f) refactor(network): use bounded host DNS resolver - [`d6d7946`](https://github.com/kvcache-ai/AgentENV/commit/d6d7946b7f739e8640eabdc615c70101db08df70) refactor(network): use mutable slot lifecycle access to remove concurrency control - [`1de7324`](https://github.com/kvcache-ai/AgentENV/commit/1de732427ad6a6edd5f65fa34192a8f99d04a41f) fix(network): reject malformed host headers ### 📊 Changes **16 files changed** (+2249 additions, -105 deletions) <details> <summary>View changed files</summary> 📝 `Cargo.lock` (+42 -0) 📝 `Cargo.toml` (+4 -1) 📝 `docs/src/SUMMARY.md` (+1 -0) 📝 `docs/src/concepts/sandboxes.md` (+5 -1) 📝 `docs/src/internals/architecture.md` (+1 -10) ➕ `docs/src/internals/networking.md` (+194 -0) 📝 `src/api/impls/sandbox.rs` (+131 -17) 📝 `src/sandbox/firecracker/sandbox.rs` (+3 -3) 📝 `src/sandbox/mod.rs` (+4 -1) ➕ `src/sandbox/network/egress_proxy.rs` (+1103 -0) 📝 `src/sandbox/network/manager.rs` (+32 -17) 📝 `src/sandbox/network/mod.rs` (+6 -1) 📝 `src/sandbox/network/policy.rs` (+301 -27) ➕ `src/sandbox/network/resolver.rs` (+274 -0) 📝 `src/sandbox/network/slot.rs` (+80 -27) 📝 `tests/integration/fc.rs` (+68 -0) </details> ### 📄 Description ## What Add E2B-compatible domain-based egress control for sandbox network policies. Domain entries in `allowOut` are now enforced for new HTTP/HTTPS connections through a namespace-local transparent proxy, with trusted host-side DNS resolution and lifecycle cleanup. ## Why CIDR-only rules cannot express the common requirement of allowing a sandbox to reach a named service while denying all other internet destinations. This change makes domain allowlists usable while preserving existing CIDR behavior, policy replacement semantics, and warm-pool reuse. ## Related issue No linked issue; this PR follows the existing E2B-compatible network-policy contract. ## Scope and non-goals Included: - Domain matching for exact names, `*.example.com`, and `*` patterns. - Transparent interception on TCP ports 80 and 443, Host/SNI inspection, and host-network-namespace DNS resolution. - Explicit validation that domain allowlists include `denyOut: ["0.0.0.0/0"]`. - Atomic/staged policy replacement, connection tracking, teardown, and warm-pool cleanup. - API behavior updates, architecture/networking documentation, and focused unit/integration coverage. Excluded: - Domain inspection on non-HTTP/HTTPS TCP ports. - TLS termination, HTTP rewriting, or a new user-facing configuration knob. - Changes to unrelated sandbox lifecycle or storage behavior. ## Design and behavior changes `NetworkManager` owns a process-wide `EgressProxy` registry. Each namespace that needs proxy mediation gets its own listener on the fixed namespace-local port `15000`; the listener is not exposed as a host port. Namespace NAT redirects only TCP/80 and TCP/443. The proxy recovers the original destination, parses HTTP Host or TLS SNI, resolves candidates in a long-lived host-netns resolver, applies absolute-deny and domain policy checks, then relays the connection without terminating TLS. Policy updates stage the new proxy policy and commit filter/NAT rules as one namespace update. Existing connections retain the policy selected when accepted; tracked sockets are closed during proxy/slot teardown so namespace resources can be safely released and reused. ## Compatibility and operations - Public API or generated protocol: Existing `allowOut`/`denyOut` fields are extended to accept domain patterns; no generated protocol changes. - Configuration or defaults: No new configuration; existing network address and deny settings remain authoritative. - Snapshot manifest, artifact layout, or storage format: N/A. - Upgrade and rollback: Nodes can be upgraded or rolled back with the existing network-policy API contract; policies without domain entries retain the prior path. - Host requirements, permissions, ports, or dependencies: Uses the existing Linux network namespace, iptables, and socket capabilities. Port `15000` is bound only inside each sandbox namespace. ## Validation - [x] `make fmt` (already run by the author) - [x] `make clippy` (already run by the author) - [x] `make test-unit` (already run by the author) - [x] Relevant Rust integration tests (already run by the author) - [ ] `make -C services test` (not applicable; `services/` is unchanged) - [ ] Generated clients/server regenerated with the documented `make` target (not applicable; generated API code is unchanged) - [x] Documentation updated - [ ] Benchmarks or performance comparison completed (not applicable) Commands and results: ```text The author already ran the applicable formatting, lint, unit-test, and integration-test checks before requesting this PR. They are intentionally not repeated here. ``` Skipped checks and reasons: ```text Services tests, code generation, and benchmarks were skipped because this change does not modify services, generated clients/server code, or benchmark targets. ``` ## Risks and reviewer notes - Review packet ordering and conntrack behavior in `src/sandbox/network/policy.rs` and `src/sandbox/network/egress_proxy.rs`; domain interception is intentionally limited to TCP/80 and TCP/443. - Host-side DNS resolution is a security boundary: guest DNS answers and original destination IPs are not trusted for domain rules, and platform/internal deny ranges remain non-overridable. - Proxy lifecycle and policy replacement are shared with warm-pool slot reuse; teardown closes tracked handlers before namespace resources are removed. - The implementation adds a small bounded per-connection handler pool and resolver worker; long-lived established streams are not given an idle timeout. ## Checklist - [x] The PR contains one coherent change and no unrelated formatting or refactoring. - [x] New behavior is covered by tests, or I explained why testing is impractical. - [x] Logs and examples contain no credentials, tokens, or private registry information. - [x] I did not manually edit generated code without updating its source and regenerating it. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-29 14:21:29 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
kvcache-ai/AgentENV#252
No description provided.