8000 T74 — Decide a DNS box's destinations on the host from the answers its own queries received · Issue #1808 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content

T74 — Decide a DNS box's destinations on the host from the answers its own queries received #1808

Closed

Description

@norrietaylor

Task

slice size difficulty model who estimated lines
Escape into the VM reaches only the declared union L needs care strongest an agent 1000 (520 code, 480 tests)

On a VM-backed host, a box that allows github.com by name reaches the addresses its own lookup returned and nothing else, decided outside the VM: with the in-VM relay replaced by a hostile one that carries the box's frames to any subnet, the host gate drops every destination the box's own answers did not pin, refuses an answer that resolved into the row's deny set or the infrastructure set, keeps applying every non-DNS rule to the row, and the bundle's host log shows the decision was the host's; the NET-068 toolchain harness (hostname_allowlist_toolchain_completes, #1771) still passes unchanged as the regression oracle for host-side resolution.

Covers:

  • NET-081
  • NET-066
  • NET-067

Layers:

  • vmhost:
    • crates/minvmd/src/net/dns_pins.rs (new): the host-side admission table, one per registered box, filled from the DNS replies the host switch returns toward the box's switch address for a name the box's row declares in allow_dns_hosts, so every pin is an answer the box's own query received and never a second resolution; the answers pass the sessions crate's rebinding intersection against the row's deny_subnets and the infrastructure deny set before entry; the window, the per-name address cap, the TTL floor and cap and the used-pin retention past the window are the ones the in-VM gate applies, read from the sessions crate so both legs hold one number; the table is bounded per box and dropped with the row
    • crates/minvmd/src/net/egress_gate.rs: the deferral arm is gone; a frame a DNS-resolving row would drop as an undeclared destination is decided on the host against that box's admission table, admitted when a live pin names the destination and dropped otherwise, with the in-VM pin table left as the precision copy; a DNS reply the switch returns toward a box is passed to the table on its way to the guest; the row's deny set, the infrastructure deny set, the protocol rule, the control surface and the unknown-source and undeclared-port drops are never deferred for any row
    • crates/minvmd/src/net.rs: hands the gate each registered row's allow_dns_hosts with the row and wires the table's lifetime to the row's
    • cargo nextest run -p minvmd dns_box_undeclared_destination_decided_on_host
    • cargo nextest run -p minvmd host_pins_are_the_answers_the_box_received
    • cargo nextest run -p minvmd host_window_refuses_denied_and_infrastructure_ranges
    • cargo nextest run -p minvmd hostile_relay_reaches_only_the_pinned_answers
    • cargo nextest run -p minvmd host_side_rules_applied_outside_vm
  • daemon:
    • crates/minimald/src/net/dns_gate.rs: the module doc names the host-side table as the deciding copy on a VM-backed host and this one as the precision copy inside the VM; no behaviour change
    • cargo nextest run -p minimald dns_pinned_admission_window

Waits on:

Plan block
### T74 — Decide a DNS box's destinations on the host from the answers its own queries received
  slice: S9
  kind: task
  covers: NET-081, NET-066, NET-067
  size: L
  est: 520 code, 480 tests
  executor: agent
  difficulty: needs care
  model: strongest
  diagnostics: the bundle's VM host daemon log tail carries one line per box when its host-side admission table is first filled and one rate-limited line per refused resolved range, so a DNS box whose destination was decided on the host reads so in a bundle
  observability: one info log line per box at the first host-side pin, naming the box and the name; one rate-limited warn line per refused answer in the shared refusal format; a counter of frames the host gate admitted by a pin and refused for want of one
  layers:
    - vmhost:
      - crates/minvmd/src/net/dns_pins.rs (new): the host-side admission table, one per registered box, filled from the DNS replies the host switch returns toward the box's switch address for a name the box's row declares in allow_dns_hosts, so every pin is an answer the box's own query received and never a second resolution; the answers pass the sessions crate's rebinding intersection against the row's deny_subnets and the infrastructure deny set before entry; the window, the per-name address cap, the TTL floor and cap and the used-pin retention past the window are the ones the in-VM gate applies, read from the sessions crate so both legs hold one number; the table is bounded per box and dropped with the row
      - crates/minvmd/src/net/egress_gate.rs: the deferral arm is gone; a frame a DNS-resolving row would drop as an undeclared destination is decided on the host against that box's admission table, admitted when a live pin names the destination and dropped otherwise, with the in-VM pin table left as the precision copy; a DNS reply the switch returns toward a box is passed to the table on its way to the guest; the row's deny set, the infrastructure deny set, the protocol rule, the control surface and the unknown-source and undeclared-port drops are never deferred for any row
      - crates/minvmd/src/net.rs: hands the gate each registered row's allow_dns_hosts with the row and wires the table's lifetime to the row's
      test: cargo nextest run -p minvmd dns_box_undeclared_destination_decided_on_host
      test: cargo nextest run -p minvmd host_pins_are_the_answers_the_box_received
      test: cargo nextest run -p minvmd host_window_refuses_denied_and_infrastructure_ranges
      test: cargo nextest run -p minvmd hostile_relay_reaches_only_the_pinned_answers
      test: cargo nextest run -p minvmd host_side_rules_applied_outside_vm
    - daemon:
      - crates/minimald/src/net/dns_gate.rs: the module doc names the host-side table as the deciding copy on a VM-backed host and this one as the precision copy inside the VM; no behaviour change
      test: cargo nextest run -p minimald dns_pinned_admission_window
  produces:
    - crates/minvmd/src/net/dns_pins.rs
  consumes:
    - crates/minvmd/src/net/egress_gate.rs
    - crates/sessions/src/core/egress.rs
    - crates/minimald/src/net/dns_gate.rs
  proof: On a VM-backed host, a box that allows github.com by name reaches the addresses its own lookup returned and nothing else, decided outside the VM: with the in-VM relay replaced by a hostile one that carries the box's frames to any subnet, the host gate drops every destination the box's own answers did not pin, refuses an answer that resolved into the row's deny set or the infrastructure set, keeps applying every non-DNS rule to the row, and the bundle's host log shows the decision was the host's; the NET-068 toolchain harness (`hostname_allowlist_toolchain_completes`, gominimal/minimal#1771) still passes unchanged as the regression oracle for host-side resolution.
  depends on:
    - blocked by #1708
    - blocked by #1711

Design ruling

Filed from the architecture review of #1788 (T63): the host gate defers an undeclared-destination drop to the in-VM gate for every row that resolves names, so a DNS-using box's destination rule is decided only inside the VM and the post-escape floor for such a box is any subnet; NET-081 is not met for name-resolving rows until this task lands. Two alternatives are ruled out: an admission set compiled on the host from allow_dns_hosts by resolving the names again (a second resolution yields different answers; the pins must be the addresses the box's own query received, design §5.3), and pins pushed up from the guest (a guest-asserted admission is what the host gate exists to refuse). The host switch is the box's resolver, so the replies it returns toward the box are the box's own answers; the host pins from those on their way in. Until this lands, the deferral arm in the gate stays exactly as narrow as it is and every other drop class is applied host-side for a DNS row, with one info line per such row saying the destination rule is deferred to the guest.

Activity

  1. added
    kind:taskA task the plan skill rendered; the body carries ## Task
    size:L500 to 900 net lines; one PR, and the ceiling
    model:strongestThe strongest model available: unknown ground, or large work that needs care
    on Oct 2, 2026
  2. minimal-foundry commented on Oct 2, 2026

    @minimal-foundry
    Contributor

    Done. #1811 merged.

    branch agent/1808-decide-a-dns-box-s-destinations-on-the-host-from · epic #1437 · PR #1811

    Updated 2026-10-28T06:17:32.453Z.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind:taskA task the plan skill rendered; the body carries ## Taskmodel:strongestThe strongest model available: unknown ground, or large work that needs caresize:L500 to 900 net lines; one PR, and the ceiling

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      0