Skip to content

feat(docker): add pinned egress planning seam - #131

Merged
senamakel merged 190 commits into
tinybox-27-metadata-ip-aliasfrom
tinybox-27-pinned-egress-plan
Oct 23, 2026
Merged

senamakel merged 190 commits into
tinybox-27-metadata-ip-aliasfrom
tinybox-27-pinned-egress-plan

Conversation

@senamakel

Copy link
Copy Markdown
Member

Add a reusable Docker crate planner that resolves each distinct normalized hostname once and returns immutable host/port/IP targets for a future proxy or firewall. The planner rejects the whole plan if any answer is a loopback or cloud metadata address, including IPv4-mapped metadata answers. Safe private addresses remain allowed under the current policy contract.

Fake resolver tests prove one lookup per normalized hostname even when a resolver would return loopback on a later call, retain the first pinned answer, reject mixed safe/forbidden answers, and permit private addresses.

This is planning only: it does not install firewall rules, filter traffic, or enable NetworkPolicy::Allowlist in DockerSandbox::create, which continues to refuse allowlists as unsupported. It does not claim allowlist enforcement or complete DNS-rebinding protection.

Stacked on TinyBox PR #130 (tinybox-27-metadata-ip-alias).

Refs #27
Part of tinyhumansai/openhuman#7328

senamakel and others added 3 commits October 23, 2026 14:15
Expose an egress module that resolves and pins allowlist targets so sandbox
network access can be planned against stable addresses. The new types and
plan_egress_allowlist are re-exported from the crate root for callers.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@tinysweeper

tinysweeper Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Tiny Sweeper review

Findings

No active actionable findings.

Advisory notes

  • high: Destroy refuses allowlist boxes that have no controller installed (crates/tinybox-docker/src/sandbox/mod.rs:956)
    Problem: `destroy` now returns `ConstraintNotEnforced` before touching Docker when a stored box's spec uses `NetworkPolicy::Allowlist` and `self.egress_controller.is_none()`. Boxes with tha
    Suggested fix:
    No concrete fix was supplied.
  • medium: Check the live-docker gate before binding the host gateway listener (crates/tinybox-docker/tests/live_docker.rs:527)
    Problem: The `live_docker_enabled()?` gate runs *after* `docker_bridge_gateway()?` and `TcpListener::bind((gateway, 0))`. So a developer who has not set `TINYBOX_LIVE_DOCKER` still spawns `
    Suggested fix:
        if !live_docker_enabled()? {
            return Ok(());
        }
    
        const ALLOWED_PORT: &str = "18373";
        const BLOCKED_PORT: &str = "18374";
    
  • medium: Exercise the off-Linux refusal on a non-Linux runner (crates/tinybox-docker/src/egress_enforcement_tests.rs:1399)
    Problem: Every test that asserts the controller refuses a non-Linux target or an unverified daemon (`native_controller_refuses_non_linux_before_host_commands`, `missing_forward_hook_refuses
    Suggested fix:
    No concrete fix was supplied.
  • medium: Describe the egress enforcement the diff actually ships ((pull request description))
    Problem: The body says:
    Suggested fix:
    Add an opt-in native Linux Docker egress controller and wire it into the Docker sandbox. `DockerSandbox::with_egress_controller` installs a `NativeLinuxDockerEgressController` that verifies a local rootful Linux Engine and its `iptables`/`ip6tables` `DOCKER-USER` hooks, resolves each distinct normalized hostname once through an injected `EgressResolver`, and installs per-box default-drop IPv4 and IPv6 chains before `docker run`. Pinned IP/port pairs are accepted, approved names arrive as `--add-host` entries with `--dns 127.0.0.1` so Docker's embedded resolver cannot forward arbitrary queries, and `destroy` removes the chains and network. The planner rejects loopback and cloud metadata answers, including IPv4-mapped metadata, and refuses pinned destinations that fall inside Docker's assigned IPAM subnets.
    
    The sandbox still refuses allowlists when no controller is installed. `Host` gains `HostExecutionLocation` (with `Local`, `Remote`, `Unknown`) so the controller can refuse hosts that do not attest to being local. Planning alone does not claim full DNS-rebinding protection, and the real firewall proof runs only in the disposable hosted Ubuntu Docker job.
    
    Stacked on TinyBox PR #130 (`tinybox-27-metadata-ip-alias`).
    
    Refs tinyhumansai/tinybox#27
    Part of tinyhumansai/openhuman#7328
    
  • medium: Remove the committed rustc ICE log from the repository root ((pull request description))
    Problem: This 606-line file is a compiler internal-error dump (`delayed bug: no resolution for an import`, `rustc version: 1.99.1`, `platform: x86_64-unknown-linux-gnu`) committed at the re
    Suggested fix:
    No concrete fix was supplied.

Verdict: Incomplete · Priority: medium
Coverage: Reviewed 6 of 25 files; not reviewed: Cargo.lock; not reviewed (large PR): crates/tinybox-c…, crates/tinybox-c…, and 16 additional unreviewed files.

Diagnostics and supporting details

Full summary

Pull request purpose (title): feat(docker): add pinned egress planning seam. Tiny Sweeper completed its review; deterministic results follow. No latest-push delta is available.

Unreviewed paths

  • Cargo.lock
  • crates/tinybox-core/src/lib.rs
  • crates/tinybox-core/src/runtime/mod.rs
  • crates/tinybox-docker/src/egress_enforcement/README.md
  • crates/tinybox-docker/src/egress_tests.rs
  • crates/tinybox-docker/src/lib.rs
  • crates/tinybox-docker/src/sandbox/args.rs
  • crates/tinybox-docker/src/sandbox/mod_tests.rs
  • crates/tinybox-host/src/limited/jailed.rs
  • crates/tinybox-host/src/limited/jailed_tests.rs
  • crates/tinybox-host/src/limited/mod.rs
  • crates/tinybox-host/src/limited/mod_tests.rs
  • crates/tinybox-host/src/local/mod.rs
  • crates/tinybox-host/src/local/mod_tests.rs
  • crates/tinybox-ssh/src/host/mod.rs
  • crates/tinybox-ssh/src/host/mod_tests.rs
  • docs/specs/contract-1-19.md
  • docs/specs/tinybox-runtime.md
  • rustc-ice-2026-10-23T18_31_29-185076.txt

Partial review reasons

  • critique: crates/tinybox-core/src/lib.rs — not selected by the large-review budget
  • security: crates/tinybox-core/src/lib.rs — not selected by the large-review budget
  • critique: crates/tinybox-core/src/runtime/mod.rs — not selected by the large-review budget
  • security: crates/tinybox-core/src/runtime/mod.rs — not selected by the large-review budget
  • critique: crates/tinybox-docker/src/egress_enforcement/README.md — not selected by the large-review budget
  • critique: crates/tinybox-docker/src/egress_tests.rs — not selected by the large-review budget
  • security: crates/tinybox-docker/src/egress_tests.rs — not selected by the large-review budget
  • critique: crates/tinybox-docker/src/lib.rs — not selected by the large-review budget
  • security: crates/tinybox-docker/src/lib.rs — not selected by the large-review budget
  • critique: crates/tinybox-docker/src/sandbox/args.rs — not selected by the large-review budget
  • security: crates/tinybox-docker/src/sandbox/args.rs — not selected by the large-review budget
  • critique: crates/tinybox-docker/src/sandbox/mod_tests.rs — not selected by the large-review budget
  • security: crates/tinybox-docker/src/sandbox/mod_tests.rs — not selected by the large-review budget
  • critique: crates/tinybox-host/src/limited/jailed.rs — not selected by the large-review budget
  • security: crates/tinybox-host/src/limited/jailed.rs — not selected by the large-review budget
  • critique: crates/tinybox-host/src/limited/jailed_tests.rs — not selected by the large-review budget
  • security: crates/tinybox-host/src/limited/jailed_tests.rs — not selected by the large-review budget
  • critique: crates/tinybox-host/src/limited/mod.rs — not selected by the large-review budget
  • security: crates/tinybox-host/src/limited/mod.rs — not selected by the large-review budget
  • critique: crates/tinybox-host/src/limited/mod_tests.rs — not selected by the large-review budget
  • security: crates/tinybox-host/src/limited/mod_tests.rs — not selected by the large-review budget
  • critique: crates/tinybox-host/src/local/mod.rs — not selected by the large-review budget
  • security: crates/tinybox-host/src/local/mod.rs — not selected by the large-review budget
  • critique: crates/tinybox-host/src/local/mod_tests.rs — not selected by the large-review budget
  • security: crates/tinybox-host/src/local/mod_tests.rs — not selected by the large-review budget
  • critique: crates/tinybox-ssh/src/host/mod.rs — not selected by the large-review budget
  • security: crates/tinybox-ssh/src/host/mod.rs — not selected by the large-review budget
  • critique: crates/tinybox-ssh/src/host/mod_tests.rs — not selected by the large-review budget
  • security: crates/tinybox-ssh/src/host/mod_tests.rs — not selected by the large-review budget
  • critique: docs/specs/contract-1-19.md — not selected by the large-review budget
  • critique: docs/specs/tinybox-runtime.md — not selected by the large-review budget
  • critique: rustc-ice-2026-10-23T18_31_29-185076.txt — not selected by the large-review budget

Review snapshot

Change surface Files Review signal Count
Production 12 Active findings 0
Tests 8 Noted findings 5
Documentation 3 Resolved findings 0
Configuration 1 Pending checks/questions 50

Completeness: Incomplete
Test assessment: Test coverage is assessed from changed tests and lane evidence; execution is not claimed without trusted check data.

Features

  • Added — Pinned egress allowlist planner: Resolves each distinct normalized hostname once, pins every answer, and rejects the whole plan for empty answers, loopback, metadata, or IPv6 link-local targets; exported from the crate root. (crates/tinybox-docker/src/egress.rs, crates/tinybox-docker/src/lib.rs)
  • Added — Native Linux Docker egress controller: Installs per-box default-drop IPv4/IPv6 firewall chains in `DOCKER-USER`/`INPUT`, permits only pinned TCP IP/port destinations, pins container `/etc/hosts` mappings with a loopback DNS upstream, and gates cleanup on verified owner/scope/attempt labels and chain markers. (crates/tinybox-docker/src/egress_enforcement.rs, crates/tinybox-docker/src/egress_enforcement/README.md)
  • Modified — DockerSandbox egress lifecycle wiring: Opt-in via `with_egress_controller`; allowlist boxes validate the host, prepare the filter before `docker run`, roll back and retain the box record on failed cleanup, and clean up on destroy. Without a controller, allowlists remain refused. (crates/tinybox-docker/src/sandbox/mod.rs impl DockerSandbox {, crates/tinybox-docker/src/sandbox/mod.rs impl Sandbox for DockerSandbox {)
  • Modified — Allowlist-aware run arguments: When a prepared network exists, `docker run` uses that network, disables upstream DNS, and adds every pinned hostname/address; otherwise existing network flags apply. (crates/tinybox-docker/src/sandbox/args.rs pub(super) fn run()
  • Modified — Spec and contract documentation update: Contract docs now describe pin-at-filter-install semantics and per-backend allowlist support; the runtime spec records the controller's requirements and refusals. (docs/specs/contract-1-19.md, docs/specs/tinybox-runtime.md)
  • Added — Unrelated repository artifacts: Adds a rustc internal-compiler-error log at the repo root and an `#[allow(deprecated)]` change in the jailed host; neither is explained by the stated planning-only scope. (rustc-ice-2026-10-23T18_31_29-185076.txt, crates/tinybox-host/src/limited/jailed.rs fn async_pipe(file: std::fs::File) -> Result<AsyncPipe> {)

Tests

  • unit — Covers one lookup per normalized hostname, first-answer retention, IP-literal pinning without resolving, resolver errors as `Error::Backend`, whole-plan rejection for loopback/metadata including IPv4-mapped and legacy forms, mixed and empty answers, private addresses, and root-dot distinctness.: The tests lane answered with no behavioral defect found; the assertions pin specific addresses and error variants. (crates/tinybox-docker/src/egress_tests.rs)
  • integration — A live Docker test stands up a controlled listener and asserts pinned destinations are reachable, other raw TCP and DNS are blocked, the host-gateway listener is untouched, and a published port still replies.: The tests lane answered; execution results are not asserted here. The README states the real firewall proof runs only in the disposable hosted Ubuntu Docker job. (crates/tinybox-docker/tests/live_docker.rs async fn live_network_policy_controls_host_listener_connectivity() -> Result<()>, crates/tinybox-docker/src/egress_enforcement/README.md)

Before merge

  • Complete the critique review for crates/tinybox-core/src/runtime/mod.rs, crates/tinybox-docker/src/lib.rs, rustc-ice-2026-10-23T18_31_29-185076.txt, crates/tinybox-docker/src/sandbox/mod_tests.rs, crates/tinybox-docker/src/egress_tests.rs, crates/tinybox-docker/src/egress_enforcement/README.md, docs/specs/contract-1-19.md, crates/tinybox-docker/src/sandbox/args.rs, crates/tinybox-host/src/limited/jailed.rs, crates/tinybox-host/src/limited/jailed_tests.rs, crates/tinybox-host/src/limited/mod.rs, crates/tinybox-host/src/limited/mod_tests.rs, crates/tinybox-host/src/local/mod.rs, crates/tinybox-host/src/local/mod_tests.rs, crates/tinybox-ssh/src/host/mod.rs, crates/tinybox-ssh/src/host/mod_tests.rs, crates/tinybox-core/src/lib.rs, docs/specs/tinybox-runtime.md.
  • Complete the security review for crates/tinybox-core/src/runtime/mod.rs, crates/tinybox-docker/src/lib.rs, crates/tinybox-docker/src/sandbox/mod_tests.rs, crates/tinybox-docker/src/egress_tests.rs, crates/tinybox-docker/src/sandbox/args.rs, crates/tinybox-host/src/limited/jailed.rs, crates/tinybox-host/src/limited/jailed_tests.rs, crates/tinybox-host/src/limited/mod.rs, crates/tinybox-host/src/limited/mod_tests.rs, crates/tinybox-host/src/local/mod.rs, crates/tinybox-host/src/local/mod_tests.rs, crates/tinybox-ssh/src/host/mod.rs, crates/tinybox-ssh/src/host/mod_tests.rs, crates/tinybox-core/src/lib.rs.
  • Review uncovered paths: crates/tinybox-core/src/lib.rs, crates/tinybox-core/src/runtime/mod.rs, crates/tinybox-docker/src/egress_enforcement/README.md, crates/tinybox-docker/src/egress_tests.rs, crates/tinybox-docker/src/lib.rs, crates/tinybox-docker/src/sandbox/args.rs, crates/tinybox-docker/src/sandbox/mod_tests.rs, crates/tinybox-host/src/limited/jailed.rs, crates/tinybox-host/src/limited/jailed_tests.rs, crates/tinybox-host/src/limited/mod.rs, crates/tinybox-host/src/limited/mod_tests.rs, crates/tinybox-host/src/local/mod.rs, crates/tinybox-host/src/local/mod_tests.rs, crates/tinybox-ssh/src/host/mod.rs, crates/tinybox-ssh/src/host/mod_tests.rs, docs/specs/contract-1-19.md, docs/specs/tinybox-runtime.md, rustc-ice-2026-10-23T18_31_29-185076.txt.
Agent review details

critique

  • Conclusion: Neutral
  • Scope reviewed: incomplete; unanswered files: crates/tinybox-core/src/runtime/mod.rs, crates/tinybox-docker/src/lib.rs, rustc-ice-2026-10-23T18_31_29-185076.txt, crates/tinybox-docker/src/sandbox/mod_tests.rs, crates/tinybox-docker/src/egress_tests.rs, crates/tinybox-docker/src/egress_enforcement/README.md, docs/specs/contract-1-19.md, crates/tinybox-docker/src/sandbox/args.rs, crates/tinybox-host/src/limited/jailed.rs, crates/tinybox-host/src/limited/jailed_tests.rs, crates/tinybox-host/src/limited/mod.rs, crates/tinybox-host/src/limited/mod_tests.rs, crates/tinybox-host/src/local/mod.rs, crates/tinybox-host/src/local/mod_tests.rs, crates/tinybox-ssh/src/host/mod.rs, crates/tinybox-ssh/src/host/mod_tests.rs, crates/tinybox-core/src/lib.rs, docs/specs/tinybox-runtime.md
  • Lane summary: Reviewed 6 of 24 files — 18 not reviewed. Reviewed 6 files; 3 findings. 18 files were omitted: not selected by the large-review budget.

security

  • Conclusion: Neutral
  • Scope reviewed: incomplete; unanswered files: crates/tinybox-core/src/runtime/mod.rs, crates/tinybox-docker/src/lib.rs, crates/tinybox-docker/src/sandbox/mod_tests.rs, crates/tinybox-docker/src/egress_tests.rs, crates/tinybox-docker/src/sandbox/args.rs, crates/tinybox-host/src/limited/jailed.rs, crates/tinybox-host/src/limited/jailed_tests.rs, crates/tinybox-host/src/limited/mod.rs, crates/tinybox-host/src/limited/mod_tests.rs, crates/tinybox-host/src/local/mod.rs, crates/tinybox-host/src/local/mod_tests.rs, crates/tinybox-ssh/src/host/mod.rs, crates/tinybox-ssh/src/host/mod_tests.rs, crates/tinybox-core/src/lib.rs
  • Lane summary: Reviewed 6 of 20 files — 14 not reviewed. Reviewed 6 files; 1 finding. 14 files were omitted: not selected by the large-review budget. 4 files were not security-reviewed: crates/tinybox-docker/src/egress_enforcement/README.md (prose or tabular data), docs/specs/contract-1-19.md (prose or tabular data), docs/specs/tinybox-runtime.md (prose or tabular data), rustc-ice-2026-10-23T18_31_29-185076.txt (prose or tabular data).

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The egress-allowlist controller arrives with substantial fake-host unit coverage plus a live-fire test, and the tests generally pin real argv decisions and rollback paths rather than asserting on mocks. The one gap worth acting on: the build is not gated on Linux, so `NativeLinuxDockerEgressController`'s off-Linux refusal test can never run on a Linux CI runner, and on macOS/Windows the default-drop and header setup paths never exercise the `#[cfg(target_os = "linux")]` branches. I found no defect that changes behaviour for a reachable input beyond that coverage gap.

commits

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The commits lane is answered and found no sensitive content in committed files.
  • Lane summary: No sensitive content in committed files.

description

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The description says this PR is planning only and does not install firewall rules or enable `NetworkPolicy::Allowlist` in `DockerSandbox::create`, but the diff adds an ~1200-line iptables/ip6tables enforcement controller, wires it into `DockerSandbox` via `with_egress_controller`, and changes `create`/`destroy` to run it. It also drops an unrelated rustc ICE log at the repo root and a `#[allow(deprecated)]` change in `tinybox-host`. The description needs to be rewritten to match the diff before this merges.
Evidence and run details
  • Models: ladder/vectors-oai3, deepseek-v4.1-flash
  • Spend: $0.001939
  • Tokens: 1368537 input · 13969 output · 325632 cached · 818 embedding
  • Updated: 2026-10-23T18:46:46Z
  • Continuity: summary cache chain restarted at the storage ceiling.
Head State Pass summary
9f0dda8d37c3 ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-10-23T14:22:26Z)
46200538213b ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-10-23T14:34:05Z)
51606b94ab11 ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-10-23T14:41:18Z)
b9a5a70aa105 incomplete 0 active finding(s), 0 resolved finding(s) (at 2026-10-23T18:46:46Z)

tinysweeper 0.1.2

@coderabbitai

coderabbitai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 5395676a-f9e2-4762-9b3e-37deb0c0b7fc

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-23T18:47:55.503894Z b9a5a70 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

tinysweeper[bot]
tinysweeper Bot previously approved these changes Oct 23, 2026

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

tinysweeper found nothing blocking. Approving.

Diagnostics

Total spend: $0.0069

@tinysweeper tinysweeper Bot added the priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect. label Oct 23, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9f0dda8d37

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/tinybox-docker/src/egress_tests.rs
Comment thread crates/tinybox-docker/src/egress_tests.rs Outdated
Comment thread crates/tinybox-docker/src/egress.rs Outdated
senamakel and others added 21 commits October 23, 2026 14:28
Hostname normalization no longer strips the trailing root dot, so absolute
names such as "service." stay distinct from their relative counterparts
instead of collapsing into the same allowlist entry. Tests were extended to
cover the absolute-name case and to disambiguate Result from the crate's
Error type.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds tests asserting that IP literal allowlist entries are pinned without
resolution and that forbidden literals are rejected, plus a test that resolver
failures surface as backend errors. The existing hostname test now also checks
that both configured ports are retained.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add assertions for the execution_location accessor on the local and SSH
hosts, and on the SSH host's inner host, so the new reporting behaviour
is exercised for local, remote, and unknown cases.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Introduce a HostExecutionLocation enum and an execution_location method on the Host trait so security-sensitive controllers can determine whether a host runs commands locally or remotely instead of inferring it from the host's registration name. Custom hosts default to Unknown until they explicitly attest to their locality.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Implement execution_location on LocalHost so callers can tell that
commands run on the local machine rather than a remote host.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
SshHost now implements execution_location and returns Remote, so callers can
tell that commands run on the far machine rather than locally.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add tests that verify the egress controller prepares the allowlist before the container starts and cleans up after destroy, and that a controller failure rolls back the box record before it is dropped.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Register the new egress_enforcement module and re-export
DockerEgressController and NativeLinuxDockerEgressController so callers can
enforce egress policy through the native Linux backend.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The Docker sandbox now accepts an optional egress controller that can rewrite
docker argv before execution, letting callers install per-box allowlist
filtering. Without a controller the sandbox keeps refusing
NetworkPolicy::Allowlist as before.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The remove_container helper now passes its argv straight to run_docker instead of wrapping it with docker_argv, since the caller already supplies the full command. This removes a redundant layer of argument construction with no change in behaviour.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a validate_host method to the DockerEgressController trait with a
default no-op implementation, and have the native Linux controller check
that the host is supported, the target is Linux, and the engine and
firewall are available. Also skip forcing the local rootful docker env
prefix when the command already starts with env.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Wrap the host in a ControllerHost that rewrites docker argv through the
egress controller before execution, so observed hosts and sandboxes with a
controller attached get the same egress handling as the primary host.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The docker sandbox now installs an egress filter through the egress
controller when a box requests an allowlist network policy, instead of
refusing the policy outright. The controller validates the host and
prepares the default-drop filter before the container starts, and any
failure during create tears the egress state back down so no filter is
left behind.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformat the egress enforcement and sandbox modules to satisfy rustfmt, wrapping long argument lists and error strings and collapsing short ones. No behaviour changes.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The firewall output helper now converts the lossy stdout to an owned value before returning it, so the result no longer borrows from the command output.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Failed container creates now run a shared rollback path that tears down
egress rules and records the attempt for later cleanup when that teardown
itself fails, instead of silently discarding the failure record. Egress
enforcement also installs allowlist rules only on the forward chain and
drops all host-origin traffic on the input chain, so allowlisted
destinations no longer apply to traffic originating from the host.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…identity

Add a test asserting that the native Linux egress controller refuses to
create a sandbox when the host has no explicit local identity, returning
ConstraintNotEnforced for the network constraint without issuing any
host commands.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Update the test resolver to return std::io::Result so it matches the
EgressResolver trait signature.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Extend the live Docker test imports with BoxId, EgressAllowlist, and the
egress controller types so the suite can exercise network policy
enforcement against a real daemon.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a live test that runs two listeners in a test-owned bridge container and
checks the native firewall controller permits the allowlisted port while
blocking other raw TCP, using a fixed resolver so no public service or Docker
host path is involved. The test tears down the sandbox and listener container
before asserting so failures do not leak resources.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a unit test asserting that a failed egress cleanup keeps the box record
so a later cleanup retry can find it, and extend the live allowlist test to
prove that direct raw TCP to the host gateway is refused while the pinned
destination still connects. The CI workflow now runs the new live test.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
senamakel and others added 24 commits October 23, 2026 18:21
…cases

The single atomic-chain test was replaced by two focused tests: one
asserting that cleanup reclaims an empty attempt chain after a restart,
and one asserting that cleanup refuses a nonempty unmarked chain and
issues no delete, flush, or chain-removal commands.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…reates

The ownership check used by ambiguous network create reconciliation now also
compares the attempt label, so a network left behind by an earlier create
attempt for the same box is no longer mistaken for this attempt's network.
Inspection and verification both parse the extra label and treat a mismatch
as a foreign network to leave alone.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a Linux-only test asserting that when a network create fails after
its side effect, cleanup inspects the attempt label and leaves a network
belonging to an older attempt untouched instead of removing it.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The attempt-label assertion now spells out the full label string instead of
relying on the shared constant, and two prepared native hosts are bound as
mutable so the tests can adjust their state.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Two test bindings had the wrong mutability, so the host is now declared mutable where its fields are assigned and immutable where they are not.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Two test bindings had the wrong mutability, so the first now declares the
prepared host as mutable and the second no longer does. This keeps the tests
compiling without changing what they exercise.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The README now documents that each attempt gets a fresh random token stored on
the Docker network and folded into chain names, and that chains are created
exclusively before receiving their owner marker. It also explains the
reclaim rules for unmarked chains after a restart and that ambiguous orphans
fail closed.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The fake host now records which chains have rules added and reports
missing rules for -C checks, so the egress enforcement tests can
exercise the rule verification path.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test host now resolves the iptables or ip6tables binary once per
invocation and reuses it for the marked-chain and chains-with-rules
bookkeeping, instead of re-deriving it inline at each call site. A
chains_with_rules removal was also added to the -D path so deletions
stay consistent with insertions.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test host now clones the firewall identifier for the marked-chains
removal and reuses the original value for the chains-with-rules removal,
instead of re-deriving it from argv. This keeps both removals keyed on the
same firewall value.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformatted the egress enforcement module and its tests to satisfy rustfmt, wrapping long call chains and assertions and collapsing short conditions onto single lines. No behaviour changed.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The firewall binary lookup was computed twice in a row, so the first
binding was dropped and the second reused. The remaining binding is now
cloned at the insertion site to keep the value available.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The cleanup test now also records the forward and input chains as having
rules and carrying the owner marker, so the refusal path is exercised
against chains that look genuinely owned by another instance.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Rename the test to make clear it covers rollback of both the created
chain and the network when the owner marker install fails.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a Linux-only test asserting that cleanup succeeds and issues only the
two expected chain-existence probes when the box network is already absent
and no matching hooks remain, without running any delete, flush, or remove
commands.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The cleanup test asserted exactly two `-S` invocations, which is brittle
against extra read-only listing calls. It now checks that at least two
occur, keeping the intent while tolerating additional chain inspections.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a test that makes the owned network removal fail after an ambiguous
create, verifying the cleanup error is retained and the network is
inspected twice.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a fault injection hook to the prepared native host so a single
`docker network rm` invocation can be made to fail, letting tests
exercise the cleanup path when network removal errors out.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The ambiguous create rollback test now binds the prepare result and includes
it along with the recorded commands in the assertion message, so a failure
shows what the controller actually returned instead of only the mismatch.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The ambiguous create test now asserts on the simulated lost command
response message instead of the owned egress network rollback failure,
so the assertion matches the error the test actually injects.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test now compiles the network-removal assertion only on Linux, since the
behaviour it exercises is platform-specific and broke builds elsewhere.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The no_matching_hooks field and its initializer are now behind a
cfg(target_os = "linux") attribute, matching the other Linux-only fields in
PreparedNativeHost. This avoids an unused-field warning on other platforms.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
feat(docker): enforce pinned Linux egress allowlists
@tinysweeper
tinysweeper Bot dismissed their stale review October 23, 2026 18:47

tinysweeper could not review the latest push, so its earlier approval no longer speaks for this pull request.

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

tinysweeper could not complete coverage (18 unanswered item(s)), so this is not an approval.

Diagnostics

Total spend: $0.0019

@tinysweeper tinysweeper Bot added priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later. and removed priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect. labels Oct 23, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b9a5a70aa1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1188 to +1192
"env".into(),
"-u".into(),
"DOCKER_HOST".into(),
"-u".into(),
"DOCKER_CONTEXT".into(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve workload variables when sanitizing Docker's environment

When a sandbox configured with this controller receives DOCKER_HOST, DOCKER_CONTEXT, DOCKER_TLS_VERIFY, or DOCKER_CERT_PATH in BoxSpec.env or ExecRequest.env, this wrapper silently removes the requested workload value. In the inspected sandbox/args.rs, both run and add_exec_context emit name-only --env KEY flags, while their callers supply values through the CLI process environment. Docker documents that if such a variable is absent, “the variable is unset in the container.” (Docker CLI documentation) Consequently, installing the controller breaks workloads that need these variables, including exec-time overrides. Separate daemon-connection sanitization from workload environment transport so the local socket remains pinned without dropping explicitly requested container variables.

Useful? React with 👍 / 👎.

@senamakel
senamakel merged commit b497130 into tinybox-27-metadata-ip-alias Oct 23, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant