Node Agent Architecture
Scope of this page. The descriptions below are architecture (validated by design, ADR-accepted) — they describe how the per-node agent is structured in the current codebase. No proof-file measurements appear on this page. For the full node-agent DaemonSet privilege posture and threat model, this page cross-links to the Security Architecture page rather than restating it.
1. What the Node Agent Does
The PodMotion node agent runs as a DaemonSet — one instance per node — and is the component that actually performs the low-level work of a live migration on the machine where the pod lives. Its responsibilities are:
- CRIU checkpoint/restore execution — freezing the source pod's process tree, writing the memory snapshot, and restoring it on the destination node.
- The netns broker — a narrow, privileged sidecar that holds the single irreducible host-mount-namespace privilege CRIU requires (see §3).
- cgroup handling — placing a restored process tree into its container's own cgroup v2 scope after restore.
The operator (controller-manager) never performs CRIU operations itself; it
reconciles the PodMigration CR and drives the state machine over gRPC, and the
node agent executes each step on the node. That control-plane split, and the
elevated privilege posture the agent requires, are documented on the
Security Architecture page (§1, §2).
2. CRIU Checkpoint/Restore Execution
CRIU is invoked as a subprocess over the go-criu RPC interface — never statically linked (ADR-0002), which keeps PodMotion's Apache 2.0 licensing compatible with CRIU's GPLv2. The agent drives CRIU through the standard checkpoint and restore phases of the migration state machine.
Checkpoint images are written to tmpfs at /run/podmotion/checkpoints/<ref>/,
mode 0700, owned by root and zeroized on all exit paths. These images contain
the full in-memory state of the checkpointed process. For what that state includes,
the storage access-control model, and the (currently unimplemented) encryption
roadmap, see Security Architecture §3.
3. The Netns Broker
CRIU restore on the destination node has to open the target pod's network-namespace
path from the host mount namespace — the --join-ns net:<path> mechanism. Doing
so requires a host-mount-namespace bind-mount privilege (CAP_SYS_ADMIN /
mount/umount2 in the host mount namespace) that cannot be reduced away: it is
what the operation fundamentally needs.
Rather than hold that privilege in the long-lived, broad-surface main agent for the
whole life of the pod, PodMotion isolates it in a small sidecar — the netns
broker, which is the sole long-lived container in the agent pod that runs
privileged: true. The broker holds the irreducible bind-mount privilege; the
main agent does not. (Two short-lived privileged init containers also run at pod
startup and exit before the agent serves traffic; they are enumerated in the
authoritative privilege disclosure on the
Security Architecture page.) This keeps the
long-lived highest-privilege surface as narrow as possible.
Broker operations
The broker exposes a narrow set of root-owned Unix-domain-socket operations
(socket mode 0600). It speaks no gRPC. As of the current codebase the operation
set is these fourteen ops, grouped by purpose:
| Group | Operations |
|---|---|
| Per-migration netns lifecycle | create, teardown |
| Host-mount-namespace exec (restore path) | exec, exec_start, exec_poll, exec_signal |
| Process signalling / liveness | signal_pid, kill_source_pid, proc_alive |
| Namespace-scoped read / exec | resolve_veth, holder_sentinel, netns_exec, netns_exec_start |
| cgroup placement | move_cgroup |
Each operation exists because the de-privileged main agent cannot perform that exact
step itself — for example, resolve_veth and holder_sentinel read another pod's
namespace state that the agent's own profile denies, and move_cgroup places an
already-restored process tree into its container's cgroup v2 scope.
Server-side input validation
Socket mode 0600 authenticates the filesystem caller, not the request, so every
broker operation validates its inputs server-side:
- The migration UID is checked as a syntactic token — a bounded alphanumeric-and- hyphen shape that forbids path-traversal characters and shell metacharacters.
- On
create, the source PID is additionally validated as a live, in-migration PID when supplied. - The broker never accepts a client-supplied host path for the bind-mount target. It computes that target itself from the migration UID, so a compromised main agent cannot direct the privileged sidecar to bind-mount over an arbitrary host location.
For the full node-agent privilege model — the DaemonSet capability set, hostPID,
seccomp posture, and the ADR-0148 hardening roadmap — see
Security Architecture §1. This page deliberately does
not restate that disclosure.
4. cgroup Handling
After CRIU restores a process tree on the destination node, its threads must be
moved into the restored container's own cgroup v2 scope. Because that placement
touches host-level cgroup state, it runs through the broker's move_cgroup
operation rather than in the de-privileged main agent (ADR-0351). Like every other
broker operation, move_cgroup validates its inputs server-side, including a
PID-reuse guard on the target process.
5. Current Scope
- The node agent, the netns broker sidecar, and the fourteen broker operations above reflect the current codebase — they are architecture descriptions, not proof-file measurements.
- The broker's privilege-isolation design — it is the sole long-lived container
running
privileged: true, while the main agent runsprivileged: false— is the mechanism that narrows the highest-privilege surface. The complete, authoritative privilege disclosure — including what the shipping v0.1.0-alpha DaemonSet posture is today (the main agent, the netns-broker sidecar, and the short-lived privileged init containers) and what remains on the ADR-0148 hardening roadmap — lives on the Security Architecture page and is not duplicated here.