Skip to content

Commit de7c5dd

Browse files
authored
Merge pull request #346 from aojea/sovereign
sandbox: flip the boundary to named HTTP tunnels via tun2connect
2 parents 081935f + 207b8d1 commit de7c5dd

45 files changed

Lines changed: 1691 additions & 1787 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/k8s/sam-box-canary-template.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ metadata:
2323
namespace: sam-canary-${ENV_NAME}
2424
data:
2525
resolv.conf: |
26-
nameserver 169.254.1.1
26+
nameserver 100.127.255.253
2727
---
2828
apiVersion: v1
2929
kind: ServiceAccount
@@ -43,8 +43,8 @@ metadata:
4343
# nano-init makes its own with --create-namespaces and gives itself the only
4444
# route out; the agent then speaks ordinary HTTP to ordinary names, and the
4545
# fact that it reaches anything at all is the assertion. A curl pointed at
46-
# --socks5-hostname would prove the boundary works for a client that chose to
47-
# use it, which is the one case nobody needs proving.
46+
# --proxy would prove the boundary works for a client that chose to use it,
47+
# which is the one case nobody needs proving.
4848
apiVersion: apps/v1
4949
kind: Deployment
5050
metadata:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ go.work.sum
3333
# .vscode/
3434
#
3535
bin/
36+
dist/
3637
# Stray binaries from `go build ./cmd/<name>/` in the repo root
3738
/sam-node
3839
/sam-box

.goreleaser.yaml

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
version: 2
22
project_name: sam
3+
# Deliberately not released: sam-bench (a measurement instrument for people
4+
# working on the mesh, built from the repo, not something operators install)
5+
# and chaos-agent (a Python dev tool).
36
builds:
47
- id: sam-node
58
main: ./cmd/sam-node
@@ -71,6 +74,20 @@ builds:
7174
- arm64
7275
ldflags:
7376
- -s -w
77+
- id: sam-console
78+
main: ./cmd/sam-console
79+
binary: sam-console
80+
env:
81+
- CGO_ENABLED=0
82+
goos:
83+
- linux
84+
- windows
85+
- darwin
86+
goarch:
87+
- amd64
88+
- arm64
89+
ldflags:
90+
- -s -w
7491
- id: nano-init
7592
main: .
7693
# nano-init is its own module: it carries a userspace TCP stack, and that
@@ -79,9 +96,10 @@ builds:
7996
binary: nano-init
8097
env:
8198
- CGO_ENABLED=0
99+
# linux only: nano-init is PID 1 inside a Linux sandbox — TUN ioctls,
100+
# netlink, namespaces and vsock have no meaning anywhere else.
82101
goos:
83102
- linux
84-
- darwin
85103
goarch:
86104
- amd64
87105
- arm64
@@ -101,6 +119,6 @@ archives:
101119
- goos: windows
102120
formats:
103121
- zip
104-
# nano-init has no windows build (see build id "nano-init" above), so the
105-
# windows archive intentionally has one fewer binary than linux/darwin.
122+
# nano-init only builds for linux (see build id "nano-init" above), so the
123+
# windows and darwin archives intentionally have one fewer binary.
106124
allow_different_binary_count: true

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ You are an expert software engineering assistant helping to develop, maintain, a
55
## 1. Architecture & Component Independence
66
* **Decoupled Architecture:** The `sam-control-plane`, `sam-router` and `sam-node` components are strictly independent. They must not share internal state or tightly couple their logic.
77
* **API Communication:** All data communication between `sam-control-plane`, `sam-router` and `sam-node` must happen exclusively via the common API defined in `api/sam.proto`.
8-
* **Sandbox Dataplane:** `sam-box` (one per sandbox) is the single egress policy enforcement point. It holds no libp2p host, no enrollment and no mesh identity, and reaches the mesh exclusively as a client of the local `sam-node` sidecar socket. `nano-init` (PID 1 inside the guest, its own Go module) owns the guest side. The sandbox boundary is a Unix socket: SOCKS5 out, `CONNECT <port>` back in. The authoritative design is `site/content/docs/agent-architecture.md`; do not contradict it.
8+
* **Sandbox Dataplane:** `sam-box` (one per sandbox) is the single egress policy enforcement point. It holds no libp2p host, no enrollment and no mesh identity, and reaches the mesh exclusively as a client of the local `sam-node` sidecar socket. `nano-init` (PID 1 inside the guest, its own Go module) owns the guest side; its datapath is the `tun2connect` library. The sandbox boundary is a Unix socket speaking named HTTP tunnels: CONNECT (TCP) and connect-udp (UDP) out, `CONNECT <port>` back in. The authoritative design is `site/content/docs/agent-architecture.md`; do not contradict it.
99
* **Enforcement over Convention:** never gate sandbox traffic on the agent's cooperation — no proxy environment variables, no `LD_PRELOAD` shims, no DNS spoofing. The agent harness stays unmodified and mesh-unaware; confinement is a route and a socket, built by the userspace launcher (`nano-init`) and judged in `sam-box`. An agent that must cooperate with its own confinement is not confined.
1010
* **Policy on Names:** egress policy, secret injection and routing decisions are made on the destination *name*, never on an IP. Deny by default.
1111
* **Agent Identity:** the agent is the principal; the node is only the channel. Agent identity comes from the platform's workload credential, verified at admission — never asserted in-band from inside the sandbox. Platforms integrate solely through the connector interface (`Attach`/`Detach`/`Refresh`/`Status` and the agent bundle), not by reaching into SAM internals.

api/sam.pb.go

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/sam.proto

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -303,9 +303,9 @@ message AgentAttachRequest {
303303
}
304304

305305
message AgentAttachResponse {
306-
// Sandbox boundary endpoints to wire into the sandbox: SOCKS5 for guest to
307-
// host, and a reverse channel for host to guest that is empty when the
308-
// bundle declares no ingress.
306+
// Sandbox boundary endpoints to wire into the sandbox: named HTTP tunnels
307+
// (CONNECT, connect-udp) for guest to host, and a reverse channel for host
308+
// to guest that is empty when the bundle declares no ingress.
309309
string egress_socket = 1;
310310
string ingress_socket = 2;
311311
string error = 3;

cmd/nano-init/README.md

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,28 @@ boundary, and then gets out of the agent's way.
55

66
## What it does
77

8-
1. **Builds the only way out.** Creates `tun0` over netlink, gives it a
9-
link-local address, and makes it the default route. There is no other
10-
interface in the sandbox, so this is not the preferred path out; it is the
11-
only one.
12-
2. **Carries a TCP stack.** Terminates the sandbox's TCP in userspace via
13-
gVisor (through `tun2socks`) and opens a SOCKS5 flow to the boundary for
14-
each connection.
15-
3. **Keeps the name.** Answers DNS with a placeholder address per name and
16-
remembers the pairing, so what reaches the boundary is `mesh.sam.alt` rather
17-
than an address. The boundary chooses a provider from the name, which is the
18-
entire reason the name has to survive the trip.
8+
1. **Builds the only way out.** Creates `tun0` over netlink and gives it the
9+
guest ends of the synthetic address pools (`100.64.0.0/10`, `100::/64`).
10+
There is no other interface in the sandbox, so this is not the preferred
11+
path out; it is the only one.
12+
2. **Carries a TCP stack.** Terminates the sandbox's TCP/IP in userspace via
13+
the [tun2connect](https://github.com/aojea/agents.net) library (gVisor's
14+
netstack) and opens one named HTTP tunnel to the boundary per flow:
15+
authority-form `CONNECT` for TCP, `connect-udp` for UDP.
16+
3. **Keeps the name.** The virtual DNS answers with a synthetic address per
17+
name and remembers the pairing, so what reaches the boundary is
18+
`mesh.sam.alt` rather than an address. The boundary chooses a provider from
19+
the name, which is the entire reason the name has to survive the trip. A
20+
flow to an address the guest never resolved has no name, and is refused.
1921
4. **PID 1 duties.** Reaps orphans, propagates `SIGINT`/`SIGTERM`/`SIGQUIT` to
2022
the child's process group, and exits with the agent's own status.
2123

2224
It is a separate Go module. A userspace TCP stack is a large dependency and has
23-
no business in the graph every other SAM binary builds from.
25+
no business in the graph every other SAM binary builds from. The datapath —
26+
engine, tunnel client, virtual DNS — is the tun2connect library, consumed here
27+
rather than forked: what this module owns is exactly the SAM- and
28+
platform-specific part, the vsock boundary for microVMs, `--create-namespaces`
29+
for pods, `copy` for image builds, and PID 1.
2430

2531
## What it deliberately does not do
2632

@@ -79,4 +85,4 @@ image that has nothing else in it.
7985
- [Running agents on SAM](https://sam-mesh.dev/docs/user/running-agents/) — the
8086
full picture, including the microVM arrangement
8187
- [Agent architecture](https://sam-mesh.dev/docs/agent-architecture/) — why the
82-
boundary speaks SOCKS5
88+
boundary speaks named HTTP tunnels

cmd/nano-init/boundary.go

Lines changed: 0 additions & 197 deletions
This file was deleted.

0 commit comments

Comments
 (0)