Skip to content

per sandbox networking - #1689

Open
Benjamin Elder (BenTheElder) wants to merge 4 commits into
agent-substrate:mainfrom
BenTheElder:per-sandbox-networking
Open

Benjamin Elder (BenTheElder) wants to merge 4 commits into
agent-substrate:mainfrom
BenTheElder:per-sandbox-networking

Conversation

@BenTheElder

@BenTheElder Benjamin Elder (BenTheElder) commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

This is part of #1266 , opening now for discussion.

Stacked on #1682 which was slightly orthogonal.

This is loosely based on the mini proposal by John Howard (@howardjohn) as discussed in the community meeting, and feedback from Bowei Du (@bowei) Eitan Yarmush (@EItanya) Lior Lieberman (@LiorLieberman) Antonio Ojea (@aojea).

https://docs.google.com/document/d/1TycfQ3iiEpbI3rveMIj0S2PpPuLecb8I5R--yTpt9Ig/edit?resourcekey=0-kJbtEZ-KGzuL5eCjHDvBhg&tab=t.0#heading=h.ga9bfaf55ptk

Roughly:

  1. Actor sandboxes each get their own netns.
    • gVisor grabs all interfaces in the netns, and tap currently requires running something like slipr, so for now we do two netns + a veth when gVisor.
  2. In the netns we directly intercept TCP => atunnel for general traffic.
  3. In the netns we serve a trivial TCP+UDP DNS relay to the pod resolution.
    • In the future we can insert policy here.
  4. We consistently inject a modified resolv.conf instead of bind-mounting it (gVisor) across both runtimes.
  5. Readiness probe dialing happens in the actor netns.

Every actor gets the same fixed guest IP as before, which is only visible to the actor.
All inbound/outbound traffic comes from atunnel / the DNS relay.
The actor no longer has any direct use of the pod interface, so we can begin to consider ateom using the network itself.
When we add the rest of multi-actor changes, this greatly simplifies thing.

Full multi-actor requires further changes, but this diff is already large (suggest reading commit by commit) and can stand-alone. I'll file more stacked changes when we've got consensus on this one.

@EItanya Eitan Yarmush (EItanya) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 AI-generated review.

Comment thread internal/ateomnet/sandbox.go Outdated

@EItanya Eitan Yarmush (EItanya) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 mostly AI generated, reviewed by me :)

Sorry for the double review, the agent initially didn't understand that I wanted to append the first review comment to these...

if err := s.releaseSandboxNetwork(ctx); err != nil {
return fmt.Errorf("while releasing the previous sandbox network: %w", err)
}
session, err := ateomnet.ServeSandbox(ctx, ateomnet.SandboxNetworkConfig{

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Preserve the documented behavior when no egress gateway is configured.

gVisor setup and microVM setup now always redirect TCP into atunnel, but prepareActorEgress still returns nil for an absent gateway and activateActorNetworking then skips activating egress. The inactive handler closes every intercepted connection. The API server still defaults the gateway address to empty, and the protocol contract explicitly promises direct egress in that case.

Could we preserve this supported mode, or explicitly require a gateway and update the configuration contract? At present an actor can start successfully with all external TCP connectivity broken. This was traced through both runtimes; I did not run a complete boot without a gateway.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

After some side discussions: I think we want to change this to clearly state that egress is not supported without a gateway, but a gateway is not strictly required (perhaps your actor does not need egress).

If we really need it later we can add a no-capture mode, but it greatly simplifies things to always use atunnel, and atunnel will need an egress gateway for egress.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

plus 1 to ben

Comment thread internal/atunnel/dns.go
Comment thread internal/ateomnet/sandbox.go
Comment thread internal/ateomnet/sandbox_linux_test.go Outdated
Comment thread cmd/ateom-gvisor/sandboxnet.go
Comment thread cmd/ateom-microvm/sandboxnet.go Outdated
Comment thread internal/atunnel/ingress.go Outdated
Comment thread internal/atunnel/dns.go Outdated
Comment thread internal/ateomnet/net.go
@bowei Bowei Du (bowei) added the kind/feature An enhancement / feature request or implementation label Sep 17, 2026
if err := root.Mkdir("etc", 0o755); err != nil && !errors.Is(err, fs.ErrExist) {
return fmt.Errorf("creating %q: %w", filepath.Join(rootfs, "etc"), err)
}
if err := root.Remove("etc/resolv.conf"); err != nil && !errors.Is(err, fs.ErrNotExist) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Prefer write out file and then use atomic rename to replace file contents.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We're doing this while nothing is reading it? I don't see the benefit. This only happens when the actor is not yet running.

Comment thread internal/ateomnet/sandbox.go Outdated
Comment thread internal/ateomnet/sandbox.go Outdated
Comment thread internal/ateomnet/sandbox.go Outdated
Comment thread internal/ateomnet/sandbox.go Outdated
Comment thread internal/ateomnet/sandbox.go Outdated
Comment thread internal/ateomnet/sandbox.go Outdated
Comment thread internal/ateomnet/sandbox.go Outdated
Comment thread internal/ateomnet/sandbox.go Outdated
Comment thread internal/ateomnet/sandbox.go Outdated
@bowei Bowei Du (bowei) self-assigned this Sep 17, 2026
Comment thread internal/proto/ateletpb/atelet.proto
Comment thread internal/ateomnet/sandbox.go Outdated
// TODO: we hope gVisor can take that same single-namespace shape soon,
// once runsc can be given one interface rather than claiming every
// interface in the namespace it runs in.
GatewayNetNS netns.NsHandle

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Naming this GatewayNetNS is very confusing, since this is for atunnel. How about this?

Current Proposed
SandboxNetwork.GatewayNetNS SandboxNetwork.AtunnelNetNS
SandboxGatewayNetNSName SandboxAtunnelNetNSName
setupGatewaySide setupAtunnelSide
gatewayVethName atunnelVethName

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It's not just atunnel, so I'm not sure that's clearer. We're also doing the DNS listener, and possibly soon an "upward API" socket.

I do hope we can align gVisor soon so there's just one netns and no veth.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Point is that its not "Gateway" and using the word "gateway" is confusing? What is it beyond Atunnel?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

keep in mind we will not have both of these once gvisor can adapt. RuntimeNetNS will be the only one. GatewayNetNS is only if we have to split them across the veth, we drop this one later.

I don't think Atunnel vs Runtime is clearer personally but I'll rename to a consensus.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM overall

// TODO: we hope gVisor can take that same single-namespace shape soon,
// once runsc can be given one interface rather than claiming every
// interface in the namespace it runs in.
GatewayNetNS netns.NsHandle

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Point is that its not "Gateway" and using the word "gateway" is confusing? What is it beyond Atunnel?

Comment thread internal/ateomnet/sandbox.go Outdated
Comment thread internal/ateomnet/sandbox.go Outdated
Comment thread internal/ateomnet/sandbox.go Outdated
Comment thread internal/ateomnet/sandbox.go Outdated
Comment thread internal/ateompath/ateompath.go Outdated
Comment thread internal/atunnel/dns.go
if err := s.releaseSandboxNetwork(ctx); err != nil {
return fmt.Errorf("while releasing the previous sandbox network: %w", err)
}
session, err := ateomnet.ServeSandbox(ctx, ateomnet.SandboxNetworkConfig{

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

plus 1 to ben

Comment thread internal/ateomnet/dns.go Outdated
Comment thread internal/ateompath/ateompath.go
Use fixed sandbox addresses in private namespaces. gVisor uses a veth
pair across two namespaces; microVMs use a tap in one. Redirect TCP
egress to atunnel and provide namespace-scoped listeners and dialers.
The dialer takes TCP and UDP IP literals only, and restores the worker
namespace once the socket exists so a pending connect does not pin a
native thread.
Forward UDP and TCP DNS unchanged through the worker pod. Bound
concurrency and connection lifetime, and close TCP connections on
cancellation. DNS bypasses the actor egress policy.
A sandbox now lives in its own network namespace, so a readiness probe has to
be dialed from there rather than from the worker's. Take the dialer as a
parameter, and let a caller name the resolv.conf bound into the sandbox.
Wire both runtimes to isolated namespaces, namespace-aware ingress and
readiness, and gateway DNS. Preserve fixed addresses across restore.
Replace worker-wide routing and microVM TC mirroring with per-sandbox
TCP redirects.
@BenTheElder

Copy link
Copy Markdown
Collaborator Author

One naming bikeshed outstanding. We can revisit that monday.

Resolved the others and did some more passes for bugs / minor nits.

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

Labels

area/gvisor area/microVM area/network area/node kind/feature An enhancement / feature request or implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants