8000 fix(net): backport the deny-all DNS refusal and the owner-only answerer socket to 0.7.0 by norrietaylor · Pull Request #2678 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content

fix(net): backport the deny-all DNS refusal and the owner-only answerer socket to 0.7.0 - #2678

Closed
norrietaylor wants to merge 5 commits into
release/0.7.0from
fix/net-security-backports-0.7.0
Closed

norrietaylor wants to merge 5 commits into
release/0.7.0from
fix/net-security-backports-0.7.0

Conversation

@norrietaylor
@norrietaylor norrietaylor commented Oct 28, 2026 •
Copy link
Copy Markdown
Member

What

Cherry-picks two security fixes from main onto release/0.7.0 for 0.7.2. Both applied cleanly with git cherry-pick -x:

Why

The NET close-out (#1437) ran the user stories by hand on the released 0.7.1 on macOS. A deny-all host_ip box resolved github.com (140.82.114.3), so the DNS channel is open in the current release. The fix is on main only, because 0.7.1 was cut from this branch before it merged.

Verification

  • just clippy (macOS scope) passes on this branch.
  • minimald is Linux-only, so this PR's Linux and VM lanes are the proof for the daemon side. Nothing has been run against a VM build of this branch by hand.
  • The fix is already on main, so the merge-back after tagging finds an identical patch for each commit.

Release-branch adjustments

gominimal-aw-bot Bot and others added 2 commits October 27, 2026 20:37
* fix(minimal,minvmd): hold the answerer channel socket owner-only

The box-zone answerer's channel socket was world-connectable: the
launchd plist bound it SockPathMode 0666 and the systemd socket unit
SocketMode=0666, leaving the peer-uid gate the only check on who may
connect. Both units now bind it owner-only -- SockPathOwner the
operator, SockPathMode 0600 on launchd; SocketUser the operator,
SocketMode=0600 on systemd -- with the uid gate kept as defence in
depth.

An install whose socket node carries group or other bits -- one that
predates the mode, or a hand that relaxed it -- now fails custody
through a channel_mode fact, so the advisory re-surfaces and a step
re-run re-binds the socket. The accept loop's foreign-refusal warn is
rate-limited to a few lines a second, so a connect storm cannot flood
the log; the refusals themselves are never limited.

* fix(minimal,minimald,minvmd): name the other account on a refused channel connect

A daemon whose connect the owner-only channel socket refuses now says
the host's DNS service belongs to another account, not a bare
Permission denied. The answerer step needs the operator's account to
resolve, with no stand-in uid, and custody fails a channel socket the
operator does not own.

* fix(minimal,minvmd): leave another operator's DNS service alone

A channel socket owned by another existing, non-root account is that
operator's service: the answerer step is now unavailable with the same
sentence the daemons say on a refused connect, instead of a custody
failure whose step would reinstall the unit and take the service over.
A root-owned socket, or one whose uid names no account, still fails
custody so the step re-binds it.

The minvmd tests that take the process-global foreign-refusal slots
now run one at a time and start and end with every slot free, and the
warn-rate test bounds the burst at two windows' worth of lines, so a
slow runner that spreads the burst does not fail it.

* fix(minimal): offer the step over a socket another account left behind

A channel socket another account owns refuses the answerer step only
when a service is installed; with none, the step's install removes the
leftover socket before it loads the unit, so it is offered.

---------

Co-authored-by: gominimal-aw-bot[bot] <281738952+gominimal-aw-bot[bot]@users.noreply.github.com>
(cherry picked from commit 746770d)
…er (#2300)

* fix(net): refuse out-of-zone names at a guest deny-subtree DNS listener

A deny-all host-address box on a VM-backed guest resolved out-of-zone
names at the switch gateway: the interim NET-079 carve-out admitted the
gateway's port 53 whole, UDP and TCP, so a box that admits no destination
could still ask a resolver beyond the machine — and DNS over TCP could
retry past the verdict.

The guest's deny subtree now steers its UDP-53 lookups onto a loopback
listener on the answerer's port (NET-141 slice 2): deny_out admits the
listener beside the reply direction, the dstnat match carries no
destination address (the box asks the gateway like every other
host-address box; the steer is the carve-out), and the listener itself
answers REFUSED for every name outside the box zone — apex and a
non-lowercase zone suffix among them — NODATA for an in-zone non-A
question, and forwards the zone's A questions to the switch gateway
alone. DNS over TCP is neither steered nor admitted.

The guest daemon starts the listener after its classifier table loads;
the daemon's own lookups are untouched (cgroup-keyed match, deny path
only). The interim --gateway-resolver installer flag and every test,
digest and pin that spelled the interim carve-out follow the new shape.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(net): bound and pin the guest DNS listener's forwards

Connect each forward socket to the gateway so a datagram from any other
sender is dropped, cap forwards in flight so a flood cannot exhaust the
daemon's descriptors, and drop the needless to_string calls clippy
rejects in the guest classifier integration test.

* fix(net): refuse questionless and non-QUERY messages at the guest DNS listener

decide() forwarded a message with no question, and an UPDATE or NOTIFY
whose zone section named an in-zone A, because every per-question check
passed vacuously. Answer the first REFUSED and the rest NOTIMP, so the
forward path carries only standard lookups.

Bind the listener directly so its log does not read as the box-zone
answerer, pin the integration test's boot-log check to the exact
example.com REFUSED line, and cite NET-079 (the deny-all case) rather
than NET-141 at the lines this change added.

---------

Co-authored-by: gominimal-aw-bot[bot] <281738952+gominimal-aw-bot[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 2c2f8cc)
@norrietaylor
norrietaylor requested a review from a team as a code owner October 28, 2026 03:38
@coderabbitai
coderabbitai Bot commented Oct 28, 2026 •
Copy link
Copy Markdown
Contributor

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: Essentials
  • Run ID: 4a29da8e-c812-4760-ab05-77b8659f319c

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.

@minimal-foundry
minimal-foundry Bot commented Oct 28, 2026 •
Copy link
Copy Markdown
Contributor

Reviewed: no findings; approval withheld, 1 thread unresolved at 882bc3453e5f.

APPROVE withheld: 1 unresolved thread — minimal-foundry[bot] crates/minimald/src/net/guest_dns.rs:212. Resolving the last thread releases the approval on this head.

Resolve the open threads. Resolving the last one releases the approval on this head; no new review is required.

Clobber: none seen.

@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

minimal-review — approved

Verdict APPROVE — No findings.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

@minimal-foundry minimal-foundry Bot added the review:approved minimal-review approved the current head label Oct 28, 2026
crate::traced landed on main after the release branch was cut, so the
cherry-pick of #2300 did not compile here.
@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

minimal-review — approved

Verdict APPROVE — No findings.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

v0.7.2 is tagged at the branch head, so the version check requires the
next patch release.
@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

minimal-review — advisory review

Verdict COMMENT — 1 finding, 1 inline.

Panel: 1 of 3 personas ran (correctness); did not complete: security, supply-chain.

Comment on lines +205 to +212
if datagram.len() > MAX_DATAGRAM {
tracing::debug!(
component = COMPONENT,
n = datagram.len(),
"dropping an oversized DNS query at the guest listener"
);
return None;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LOW · correctness · confidence 0.55 · in parse

The oversize guard in parse can never fire from serve_bounded. The receive buffer is exactly MAX_DATAGRAM bytes, so recv_from truncates a larger datagram to 4096 bytes and len never exceeds the limit. The truncated bytes are then parsed and can be forwarded, although the doc comment says a larger datagram is dropped. Use a buffer of MAX_DATAGRAM + 1 bytes, or drop a datagram whose len equals the buffer size. The unit test only passes MAX_DATAGRAM + 1 bytes directly to parse, so it does not cover the real path.

What the reviewer checked

guest_dns.rs:143 let mut buf = vec![0u8; MAX_DATAGRAM]; and :205 if datagram.len() > MAX_DATAGRAM

Hand this to an agent
Act on a code-review finding.

File:       crates/minimald/src/net/guest_dns.rs
Lines:      205-212
Category:   correctness
Severity:   low (reviewer confidence 0.55)

Finding:
The oversize guard in `parse` can never fire from `serve_bounded`. The receive buffer is exactly `MAX_DATAGRAM` bytes, so `recv_from` truncates a larger datagram to 4096 bytes and `len` never exceeds the limit. The truncated bytes are then parsed and can be forwarded, although the doc comment says a larger datagram is dropped. Use a buffer of `MAX_DATAGRAM + 1` bytes, or drop a datagram whose `len` equals the buffer size. The unit test only passes `MAX_DATAGRAM + 1` bytes directly to `parse`, so it does not cover the real path.

First verify this against the code yourself. It is model output and
may be wrong. If it is wrong, or the tradeoff was deliberate, say so
and change nothing.

👍 useful · 👎 noise — react to tell the reviewer. Replying works too, and says more.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Not fixed in this PR. The finding looks valid, but the same code is on main (crates/minimald/src/net/guest_dns.rs: the receive buffer is vec![0u8; MAX_DATAGRAM] and parse checks > MAX_DATAGRAM). This PR is a cherry-pick backport of #2300, and its merge-back relies on identical patches. The fix belongs on main first, then a backport if it is wanted for 0.7.x. Leaving this open for Norrie to decide.

@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

minimal-review — approval withheld

Verdict COMMENT — APPROVE withheld: 1 unresolved thread — minimal-foundry[bot] crates/minimald/src/net/guest_dns.rs:212. No findings.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

@norrietaylor
norrietaylor deleted the fix/net-security-backports-0.7.0 branch October 30, 2026 06:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0