Repository navigation
Conversation
brig's credential broker serves a sandbox at a name the sandbox reaches through its gateway, so the gateway needs a way to hand a guest's TCP connections to a destination to a local process. It dialed every allowed destination itself, so there was no way to put that process in the path. --divert host:port=socket hands such connections to a Unix socket. The gateway writes one preamble line, "BRIG-DIVERT/1 <dst-ip> <dst-port> <src-ip> <src-port>", and then relays the guest's bytes both ways unchanged, with half-close. - A named host matches every address this gateway resolved it to: at start, every --egress-refresh, and on each guest query. The set only grows, so a rotated address is never dialed directly. - Diversion is checked before the egress policy. The socket's owner applies its own rules. A diverted name is answered under deny-default, but not pinned, so other ports on its addresses stay under the policy. - It fails closed. An unreachable socket, or an owner that closes without writing, resets the guest. A diverted connection is never dialed. - UDP to a diverted destination is dropped, so QUIC falls back to TCP. - GET /diverts on --api lists each divert with its counters. Refs: brig-sh/brig#265 Signed-off-by: Anastassios Nanos <ananos@nofire.ai>
A guest connection to the gateway's address on a port no divert names was dialed from the host. It then reached whatever answered that address on the host's network. brig now answers brig.internal with the gateway's address, and diverts its port 443 to the broker. Nothing else should answer there. - TCP to the gateway's address is reset, after the diverts are matched. - UDP to it is dropped. Its DNS port is an endpoint on the stack, and the forwarders never see it. The test starts a gateway the way brig does: a --host record of brig.internal and a divert of port 443. It checks the record under deny-default, the divert, and that ports 80, 8443 and 22 are reset and never dialed, with and without a policy. Refs: brig-sh/brig#302 Refs: brig-sh/brig#265 Signed-off-by: Anastassios Nanos <ananos@nofire.ai>
ananos
marked this pull request as ready for review
October 23, 2026 17:17
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
brig's credential broker serves each sandbox at
brig.internal. On hull, the sandbox reaches it through its network gateway. The gateway dials every allowed destination itself, so there was no way to hand a guest's connection to a local process.This PR adds
--divert host:port=sockettohull network-gateway. A guest TCP connection to a diverted destination goes to a Unix socket: the gateway writes one preamble line and then relays the bytes both ways unchanged. It fails closed, so an unreachable socket or an owner that fails resets the guest, and a diverted connection is never dialed.The second commit stops the gateway from dialing its own address. brig answers
brig.internalwith the gateway's address, so nothing other than the divert should answer there.This is stacked on #130 (the hvi-vmm bump), so that the experimental channel carries both. The brig side is the broker PR stack in brig-sh/brig, whose last PR routes
brig.internalto microVMs. brig checks for--divertand refuses a routed run on a hull without it.Related issues
Refs brig-sh/brig#265, brig-sh/brig#302
Changes
--divert host:port=socket, repeatable. A named host matches every address the gateway resolved it to, and the set only grows. Diversion is checked before the egress policy.BRIG-DIVERT/1 <dst-ip> <dst-port> <src-ip> <src-port>, then a plain relay with half-close. The source address is whatever the guest sent, so the docs say not to use it as identity: the socket path identifies the sandbox.GET /divertson--apilists each divert with its counters.docs/networking.mddocuments the flag, the preamble and the fail-closed rules.Checklist
make testpassesmake macosbuilds hull, vz-runner and hvi, if Go, Swift or Rust code changedtest/*.py) for changes touching boot, console, or the run pathdocs/)Negative tests cover each fail-closed claim: missing socket, refusal, an owner that fails partway through a reply, deny policy, an address not yet resolved, gateway ports 80/8443/22 and UDP. golangci-lint v2.12.2 is clean for darwin and linux, and
go test -race ./internal/netgw/passes. The e2e harnesses do not exercise the gateway. A run through brig with a routed policy is pending the brig stack.