Skip to content

j0yen/agentns

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

agentns — an eighth Linux namespace, for agents

A vendor patch series that adds an agent namespace to the Linux kernel: every task carries an opaque 128-bit agent_session_id, an optional intent_tag, and a set of per-namespace counters, exposed at /proc/$PID/agent_session, /proc/$PID/agent_counters, and /proc/$PID/ns/agent.

The kernel already isolates a process's view of pids, mounts, networks, users, and four other resources, each as a namespace. What it has no notion of is which agent session a task belongs to — the unit that matters when an autonomous process forks children, spends a syscall budget, and must be accounted for and reaped as one thing. agentns makes that a first-class kernel object rather than something reconstructed after the fact from comm:pid:uid guesswork.

Creation is a prctl, not a clone flag

This is the design decision that shapes everything else, so it goes first. The namespace is created with prctl(PR_SET_AGENT_NS), which allocates a fresh agent_ns — new session id, zeroed counters, cleared intent tag — and installs it on the calling task's nsproxy. No clone flag is involved, and unshare(2) is not the entry point.

The reason is that the 32-bit clone-flag space is full. Bits 0x80 (CLONE_NEWTIME) through 0x80000000 (CLONE_IO) are all claimed upstream; CLONE_NEWTIME took the last low bit. There is no free bit for an eighth namespace reachable from unshare(2). An early version of this work missed that and #defined CLONE_NEWAGENT as 0x00000100 — which is CLONE_VM. Two failures followed, both reproduced:

  • unshare(CLONE_NEWAGENT) returned EINVAL, because check_unshare_flags() read the bit as CLONE_VM and rejected it before any agentns code ran. The namespace could never be created; /proc/self/agent_session read 32 zeros.
  • Every kthread fork was misread as also requesting a new agent NS. copy_agent_ns() ran before its cache was initialized and the kernel hung on the firmware splash with no console output.

Picking a different low bit can't work — they are all taken. So creation moved off clone flags entirely and onto a dedicated prctl op. A CLONE_NEWAGENT flag does still exist, at 0x400000000ULL (a 64-bit clone3-only slot above CLONE_INTO_CGROUP), used for the clone3 inheritance path and as the ns_common type tag — but it is unreachable from unshare(2) and is not how a namespace is created. A BUILD_BUG_ON against every upstream CLONE_* bit guarantees that a future re-aliasing of a low bit fails to compile rather than bricking boot.

prctl interface

PR_SET_AGENT_NS              create + enter a fresh agent namespace (CAP_SYS_ADMIN)
PR_GET_AGENT_SESSION_ID      read the 128-bit session id
PR_SET_AGENT_INTENT_TAG      set / PR_GET_AGENT_INTENT_TAG read the intent tag
PR_GET_AGENT_PARENT_ID       read the parent session's id
PR_SET_AGENT_BUDGET_LIMITS   set max_syscalls / max_write_bytes / max_elapsed_ns + action
PR_GET_AGENT_COUNTERS        read the per-namespace counters

What ships here

patches/        0001–0011, unified diffs against linux mainline
kernel/         agent_namespaces.c — the implementation
include/        in-kernel API, the uapi header (prctl ops, structs), tracepoints
scripts/        build.sh (fetch + patch + build), rebase-check.sh (drift detector)
userspace/      agentns-unshare.c — the create-and-exec shim; agent-wrap.c (deprecated)
tests/          test_prctl_ns.c (primary), test_unshare.c, and the .sh checks

patches/0010 is a placeholder: the new kernel/ and include/ files are copied into the tree directly by build.sh rather than carried as a diff. patches/0011 is the prctl-create fix described above.

Building

The target is whatever kernel pacman -Q linux reports. scripts/build.sh fetches the matching sources via asp (or pkgctl), copies the new files in, applies the patch series with patch -p1, enables CONFIG_AGENT_NS, and builds:

./scripts/build.sh                 # fetch sources, apply patches, build the kernel
BUILD_KERNEL=0 ./scripts/build.sh  # apply patches only, skip the build
KVER=7.1.2 ./scripts/build.sh      # target a specific kernel version

Install the resulting package with the usual Arch flow (pacman -U linux-wintermute-*.pkg.tar.zst …), then reboot into it. The namespace is on by default (CONFIG_AGENT_NS=y) and can be disabled at runtime without rebooting:

sysctl -w kernel.agent_ns.enabled=0   # all counter hooks become no-ops

Verify it's live:

agentns-unshare cat /proc/self/agent_session   # prints a non-zero 128-bit id

Rebasing onto a new kernel

A kernel package bump may shift a context line and break a patch. scripts/rebase-check.sh reports which patches no longer apply cleanly. When one genuinely conflicts, apply it by hand (patch -p1 --merge), resolve the .rej hunks, and regenerate the patch with git diff.

What the patch series implements

Phase Implements
0 task_struct->nsproxy->agent_ns, /proc/$PID/agent_session, a default init NS for unmodified tasks
1 per-cpu counters; the prctl interface; an agent_session_end tracepoint carrying counters
2 tracepoints (agent_ns:agent_session_start/_end/_intent_set) under /sys/kernel/tracing/events/agent_ns/
3 LSM xattr stamping that reads the session id when CONFIG_AGENT_NS=y, falling back to comm:pid:uid otherwise
4 budget enforcement: a reaper checks max_syscalls / max_write_bytes / max_elapsed_ns each minute and applies the configured action (log / SIGTERM / SIGKILL)

Tests

From a running patched kernel:

cd tests && make
sudo ./test_prctl_ns          # round-trip: PR_SET_AGENT_NS → /proc → prctl
sudo ./test_unshare           # legacy probe: unshare(0x100) must fail, then prctl-create
sudo ./test_inheritance.sh    # child inherits the parent's session id
sudo ./test_bpf_tracepoints.sh

test_prctl_ns is hermetic — no external deps — and exits 77 (the autotest "skip" code) if /proc/self/agent_session is absent, so running it on a stock kernel is harmless. To launch a command inside a fresh namespace from a shell, use the agentns-unshare shim (needs CAP_SYS_ADMIN):

sudo setcap cap_sys_admin+ep ./userspace/agentns-unshare
AGENT_INTENT=my-task ./userspace/agentns-unshare cat /proc/self/agent_session

Sysctls

kernel.agent_ns.enabled       (0|1, default 1)
kernel.agent_ns.max_lifetime  (seconds, default 86400 = 24h)

The reaper runs once a minute and SIGKILLs every task in any namespace older than max_lifetime — the backstop for a tracer that leaked and never ended its own session.

Caveats

  • This is a vendor fork, not an upstream proposal. Mainline will not take a new namespace type for an application-specific use case; the standing cost is rebasing the series across kernel bumps.
  • Creation needs CAP_SYS_ADMIN from the init namespace. Rootless creation is deferred.
  • Per-cpu u64 counters wrap at 2^64 — at 10 GB/s of writes, ~58 years out. Not a concern in practice.

License

GPL-2.0 (LICENSE), matching the kernel it patches.

About

An eighth Linux namespace for agent sessions: 128-bit id, counters, budget enforcement; created via prctl, not clone

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors