Skip to content

Add the sessionview launcher for per-Session views - #339

Merged
SaladDay merged 1 commit into
feature/agent-outside-sandboxfrom
aos/session-launcher
Sep 30, 2026
Merged

SaladDay merged 1 commit into
feature/agent-outside-sandboxfrom
aos/session-launcher

Conversation

@SaladDay

@SaladDay SaladDay commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Adds apps/daemon/internal/sessionview, the Linux launcher that runs one process inside a per-Session view on the agent host.

How a view is built:

  • The daemon re-executes itself, through a private sessionview.Init() hook, into a launcher cloned into new mount, PID and network namespaces. The launcher is PID 1: it forwards signals, reaps orphans and exits with the process's status.
  • The world is a FUSE mount (nosuid, nodev, noexec). The launcher hands the /dev/fuse connection to the daemon's World callback over SCM_RIGHTS, so the daemon serves the file system from outside the view.
  • On top of the world it adds:
    • private /.oac/* directories;
    • trusted overlays;
    • the shim at /.oac/bin/<name> and over the declared absolute paths.
  • Mount targets are opened with openat2 and the new mount API: no symlinks, stay beneath the view root.
  • The view gets a fresh /proc and a minimal /dev (devpts, noexec /dev/shm).
  • The root switch is MS_MOVE plus chroot; container seccomp blocks pivot_root.

The exec guard is the one recorded in the spec after design review:

  • Mount flags alone decide exec: only the read-only Harness closure, the shim binds and exec-flagged overlays can run, and a writable exec directory is invalid.
  • One seccomp filter denies creating or entering user namespaces: clone/unshare with CLONE_NEWUSER and setns return EPERM, clone3 returns ENOSYS, and any arch other than amd64 or arm64 is killed.
  • The Harness runs with no capabilities, no_new_privs, a non-root uid and only fds 0–2.
  • There is no Landlock: ABI 1 breaks cross-directory rename.
  • Memfd exec, and code run by an allowed interpreter, are outside this guard; doc.go says so.

Every construction failure is typed. Probe checks the host (SYS_ADMIN, NET_ADMIN, /dev/fuse, seccomp). On other OSes the package compiles, and Start returns ErrUnsupported.

Checks:

  • The privileged container tests pass. The container was run as docker run --cap-add SYS_ADMIN --cap-add NET_ADMIN --device /dev/fuse --security-opt apparmor=unconfined. The tests cover:
    • FUSE read/write;
    • world-binary EACCES;
    • the shim over /bin/sh;
    • a noexec home;
    • a private /proc;
    • loopback only;
    • no capabilities and no_new_privs;
    • cross-directory rename and link;
    • the user-namespace denials;
    • signal delivery and teardown;
    • refusal of a symlinked mountpoint.
  • The BPF program is also unit-tested in the x/net/bpf interpreter for both arches.
  • go vet on linux/amd64, linux/arm64, darwin and windows; gofmt.

Part of the agent-outside-sandbox work, milestone M1, lane L4.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

@SaladDay
SaladDay merged commit 02a4ed2 into feature/agent-outside-sandbox Sep 30, 2026
1 check passed
@SaladDay
SaladDay deleted the aos/session-launcher branch September 30, 2026 22:55
The daemon re-executes itself into a launcher cloned into new mount,
PID and network namespaces. The launcher mounts the world over
/dev/fuse, hands the connection to the daemon's world server over
SCM_RIGHTS, binds the private directories, overlays and shim onto
symlink-free targets, adds /proc and a minimal /dev, moves the world
onto /, drops privileges, installs a seccomp filter that denies user
namespaces, setns and clone3, and runs the Harness as PID 1's only
child. Mount flags alone decide what can be executed.
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.

1 participant