From e20f9a7a06c1439edd58ccfde22cdae0da57a2c7 Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Tue, 19 May 2026 14:10:09 +0200 Subject: [PATCH] execute: per-session FUSE mount for 9P identity The execute server now starts ollie-9p-mount when OLLIE_SESSION_ID is set, giving each agent session its own FUSE proxy that attaches to olliesrv with Uname=session_id. Tools see the full namespace through $OLLIE but all 9P operations carry the session's identity. Close() tears down the mount on session destroy. --- pkg/agent/core.go | 8 ++++++ pkg/tools/execute/server.go | 49 +++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/pkg/agent/core.go b/pkg/agent/core.go index 9cb3a9c..e382dc1 100644 --- a/pkg/agent/core.go +++ b/pkg/agent/core.go @@ -445,6 +445,14 @@ func NewAgentCore(cfg AgentCoreConfig) Core { // Close releases resources for this session, including its tmpdir. func (s *agent) Close() { s.log.Debug("Close() session=%q", s.sessionID) + if s.dispatcher != nil { + if srv, ok := s.dispatcher.GetServer("execute"); ok { + if c, ok := srv.(interface{ Close() }); ok { + s.log.Debug("Close() calling execute.Close()") + c.Close() + } + } + } if s.sessionID != "" { os.RemoveAll(filepath.Join(ollieTmpDir(), s.sessionID)) //nolint:errcheck } diff --git a/pkg/tools/execute/server.go b/pkg/tools/execute/server.go index 6332edf..e40492b 100644 --- a/pkg/tools/execute/server.go +++ b/pkg/tools/execute/server.go @@ -41,6 +41,10 @@ type Server struct { // step dispatch. Set once at session init via SetLockDir; empty disables locking. lockDir string + // mountProc is the ollie-9p-mount subprocess for this session's FUSE proxy. + mountProc *os.Process + mountPath string + // Strict rejects inline {code} steps; only {tool} steps are allowed. Strict bool @@ -358,9 +362,54 @@ func (e *Server) SetEnv(key, value string) { e.envExtra = make(map[string]string) } e.envExtra[key] = value + if key == "OLLIE_SESSION_ID" && value != "" { + e.startMount(value) + } e.envMu.Unlock() } +// startMount launches ollie-9p-mount for this session. Must be called with envMu held. +func (e *Server) startMount(sessionID string) { + bin, err := exec.LookPath("ollie-9p-mount") + if err != nil { + return // binary not installed; skip silently + } + mnt := filepath.Join(os.TempDir(), "ollie-mount-"+sessionID) + os.MkdirAll(mnt, 0755) //nolint:errcheck + cmd := exec.Command(bin, "-u", sessionID, mnt) + cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} + if err := cmd.Start(); err != nil { + return + } + // Reap in background so Wait works later. + go cmd.Wait() //nolint:errcheck + // Wait for mount to be ready. + for i := 0; i < 20; i++ { + time.Sleep(50 * time.Millisecond) + if _, err := os.Stat(filepath.Join(mnt, "s")); err == nil { + break + } + } + e.mountProc = cmd.Process + e.mountPath = mnt + e.envExtra["OLLIE"] = mnt +} + +// Close tears down the per-session FUSE mount. +func (e *Server) Close() { + e.envMu.Lock() + proc := e.mountProc + mnt := e.mountPath + e.mountProc = nil + e.mountPath = "" + e.envMu.Unlock() + if proc != nil { + exec.Command("fusermount", "-u", mnt).Run() //nolint:errcheck + syscall.Kill(proc.Pid, syscall.SIGKILL) //nolint:errcheck + os.Remove(mnt) //nolint:errcheck + } +} + // executeElevated runs cmd outside the sandbox via x/elevate. // Returns (output, error); a non-zero exit code is treated as an error. func (e *Server) executeElevated(ctx context.Context, cmd, dir string, timeout int) (string, error) {