Repository navigation
docs(specs): refuse names a box's allow list does not match before forwarding upstream #2009
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fbd22c4
8b865c2
264287f
af9dab6
b4537f4
f17741a
9aa8885
270f8af
9c8cb53
1ee8aa4
6ea1057
f990b8b
c06b31f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -212,7 +212,7 @@ included, with every refusal logged (NET-001 to NET-004). | |
| verify: cargo nextest run -p minimald run_box_ends_when_its_run_ends | ||
| <!-- S1b-2c; event-driven within the WHILE; `min task run` creates a session for the run, execs the task into it, and today destroys it from the client; the destroy moves to the daemon side of the exec's exit so a lost client strands no session; the abandoned-launch reap covers only un-finalized sessions and does not reach this case --> | ||
|
|
||
| - **NET-141** WHEN an activation targets a host whose user-namespace verdict refuses the sandbox THE SYSTEM SHALL fail the activation before the session is created, name the cause, exit non-zero, and leave no session behind. | ||
| - **NET-143** WHEN an activation targets a host whose user-namespace verdict refuses the sandbox THE SYSTEM SHALL fail the activation before the session is created, name the cause, exit non-zero, and leave no session behind. | ||
| tier: T0 | ||
| verify: cargo nextest run -p minimal activate_refuses_unconfinable_sandbox_before_session_creation | ||
| verify: cargo nextest run -p minimald create_reply_carries_user_namespace_verdict | ||
|
|
@@ -484,6 +484,12 @@ included, with every refusal logged (NET-001 to NET-004). | |
| verify: cargo nextest run -p minimald dns_pinned_admission_window | ||
| <!-- S8b/AC1; prose 42; event-driven --> | ||
|
|
||
| - **NET-141** WHEN the resolver Minimal owns for a box receives a query for a name that its `egress.allow_dns_hosts` does not match, as NET-066 matches names, and that is not in the box zone (NET-072, including `host.min.internal`), THE SYSTEM SHALL answer REFUSED and forward the query to no resolver beyond it. | ||
| tier: T1 | ||
| verify: cargo nextest run -p minimald unmatched_name_refused_without_upstream_forward | ||
| property: for every name, every allow-pattern set, and every zone, the resolver forwards the query beyond the one Minimal owns for the box only if the name matches a pattern or is in the zone, and it never forwards a name that matches none and is not in the zone | ||
| <!-- design §5.3 ("Non-matching names are refused"); event-driven; the complement of NET-066, decided by the same matcher, so admission and refusal never disagree on a name: case-insensitive, a trailing dot ignored, the same wildcard rule. "Beyond the one Minimal owns" covers every onward hop: the gateway resolver, the host's resolvers, and the Egress Gateway when enrolled. The refusal is REFUSED (rcode 5), answered at once: never NXDOMAIN, which asserts the name does not exist and is negatively cached for the whole name (negative answers are governed by NET-125 and NET-128), and never a timeout. The refusal comes before any onward query, because a forwarded query carries data out in the queried name even when the connection that follows is dropped (NET-062). Names steered to the Box Egress Proxy need no exemption: the credentialed set stays inside the allow list, so they always match. Scope: the requirement binds queries that reach the resolver Minimal owns for the box. For an own-address box, on a native host and inside a VM-backed host alike, that is the relay's DNS gate: the box's `resolv.conf` names the switch gateway, every one of its frames passes the relay (NET-081), and the gate intercepts every standard query to that resolver before it reaches the switch, which is where the refusal is made. For a host-address box inside a VM-backed host it is the node's DNS layer once NET-003's sub-bullet holds; until that routing lands, a host-address box's queries go to the switch gateway without passing a per-box gate, and NET-141 binds nothing for it. A native host-address box shares the host's network namespace, so if its egress rules allow the host's own resolver it can query that resolver directly, and no Minimal resolver sees the query; that is the reduced tier the design concedes for host-address boxes, and closing it needs a native routing rule this spec does not yet state. On a VM-backed host the refusal follows NET-066's two legs: the deciding copy is host-side, in minvmd's DNS gate outside the VM escape boundary, and the in-VM gate is the precision copy, so the VM-host proof must show the refusal at the host-side leg. Residual: minvmd's DNS table today observes a box's queries and pins the replies but cannot refuse a query, so until it can, the refusal on a VM-backed host holds only below the escape boundary. The T1 property is the proof that no onward query is made. The session e2e has two legs for names outside the allow list. The deny-all box's leg asserts a fast resolution failure: its lookup fails inside 5000 ms, and its connection fails at resolution. The disallowed-name leg of the box with a partial allow list asserts only that the connection does not complete. It prints whether the name was refused fast or timed out. When the refusal covers a partial allow list, that leg must assert a fast resolution failure too. Neither leg observes whether an onward query occurs. Known gap: the relay's DNS gate refuses only for a deny-all box, and the verify test covers only that case. For a box with a partial allow list, the relay still forwards a name that matches no pattern. Until the gate refuses for every allow list and the test covers a partial list, NET-141 holds only for deny-all boxes. --> | ||
|
|
||
| - **NET-067** IF an allowed name resolves into the box's `egress.deny_subnets` or the infrastructure deny set THEN THE SYSTEM SHALL refuse the connection. | ||
| tier: T2 | ||
| verify: cargo nextest run -p minimald denied_range_resolution_refused | ||
|
|
@@ -1287,6 +1293,14 @@ What a native co-resident host cannot offer is an enforcement point outside its | |
| escape boundary ([design §7.4][design]); its egress rules hold only while the | ||
| daemon does, and the attribute that makes that gap visible to policy is EHE's. | ||
|
|
||
| The invariant that no unmatched name travels beyond the resolver Minimal owns | ||
| for a box (NET-141) has two stated limits. A native host-address box shares the | ||
| host's network namespace. If its egress rules allow the host's own resolver, | ||
| it can query that resolver directly, and no Minimal resolver sees the query. | ||
| The design concedes this reduced tier for host-address boxes. On a VM-backed | ||
| host, the host-side DNS table cannot yet refuse a query. Until it can, the | ||
| refusal holds only inside the VM, below the escape boundary. | ||
|
Comment on lines
+1297
to
+1302
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LOW · The text says the host-side DNS table cannot yet refuse a query. But What the reviewer checked
Hand this to an agent👍 useful · 👎 noise — react to tell the reviewer. Replying works too, and says more. |
||
|
|
||
| ## Security considerations | ||
|
|
||
| - **Invariant:** THE SYSTEM SHALL admit no connection to a box, and no | ||
|
|
@@ -1324,6 +1338,14 @@ daemon does, and the attribute that makes that gap visible to policy is EHE's. | |
| set. | ||
| enforced by: the rebinding intersection | ||
| covered by: NET-066, NET-067 | ||
| - **Invariant:** THE SYSTEM SHALL carry no name that a box's allow list does | ||
| not match, outside the box zone, beyond the resolver Minimal owns for the | ||
| box B6FD , for every query that reaches that resolver. | ||
| enforced by: the refusal at the resolver Minimal owns for the box. On a | ||
| VM-backed host the in-VM gate makes it, until minvmd's host-side DNS gate | ||
| can refuse a query itself. That residual and the native host-address box's | ||
| bypass are the two limits stated in the design reasoning. | ||
| covered by: NET-141 | ||
|
minimal-foundry[bot] marked this conversation as resolved.
Resolved
Comment on lines
+1341
to
+1348
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LOW · The new invariant says no unmatched name leaves the box's resolver, with no qualifier. NET-141's own comment says the relay's DNS gate refuses only for deny-all boxes. A box with a partial allow list still forwards unmatched names upstream, and the verify test covers only deny-all. The invariant is therefore false today for the common case, and a reader will assume the exfiltration channel is closed. Add the partial-allow-list gap to the invariant's What the reviewer checked
Hand this to an agent👍 useful · 👎 noise — react to tell the reviewer. Replying works too, and says more. |
||
| - **Invariant:** THE SYSTEM SHALL publish a dynamically requested port only | ||
| under a recorded allow decision. | ||
| enforced by: the local daemon evaluates the same request shape as the | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LOW ·
correctness· confidence 0.60 · inNET-141NET-141 states a general rule and a property over every allow-pattern set. Its
verify:testunmatched_name_refused_without_upstream_forwardincrates/minimald/src/net/dns_gate.rsdrives only a deny-all relay (deny_all_egress()). The annotation admits that a partial allow list is still forwarded. So the requirement is false for partial lists and the verify line passes without proving it. Add a partial-list case to the test, or scope the requirement to deny-all until the gate refuses for every list.What the reviewer checked
Hand this to an agent
👍 useful · 👎 noise — react to tell the reviewer. Replying works too, and says more.