Kernel confinement: capabilities, cgroups, Landlock and egress
Deciding "no" is half the job. The other half is limiting the blast radius: confining the block to the right process (cgroup), running with minimum privilege (Landlock, no root) and cutting the network egress that serves exfiltration and C2 (egress allowlist).
In 30 seconds
The goal is not to block the whole system, only the agent. That is why the eBPF program only acts on processes in the agent cgroup. For Nemesis itself to run with little power, there is Landlock, which restricts what the process can execute and touch without root, enabling no_new_privs first. And to keep data from leaving, an egress allowlist: deny-all by default, only what is explicitly allowed passes.
From scratch: the three mechanisms
Capabilities split root power into pieces: instead of "all or nothing", a process receives only the capability it needs. cgroups (control groups) group processes for kernel accounting and control; each group has an identifier, the cgroup_id, and that is how the system knows "this process belongs to the agent". Landlock is an LSM that lets an ordinary process sandbox itself (no privilege needed): it declares what it may do and the kernel starts refusing the rest, including for its children. Egress is outbound traffic; controlling it means deciding where the process may open connections.
Why it matters
A compromised agent tends to do two things: execute something destructive and send data out (exfiltration, or fetching orders from a command and control server, the C2). Confining to the cgroup guarantees the containment targets only the agent, not the user's system. Running without root guarantees Nemesis itself is not an escalation vector. And denying egress by default closes the data's way out, which is the end goal of a large share of attacks.
How it correlates
The cgroup scoping is what ties the other two to the agent: the exec hook from group 2 only acts if the process belongs to the agent cgroup, and the same goes for the egress decision. Landlock is a complementary least-privilege layer in userspace, and the egress allowlist is the network counterpart of exec blocking: one cuts what runs, the other cuts where it talks to. Together they turn "detected" into "contained without collateral damage".
How Nemesis applies it
The Landlock sandbox enables no_new_privs via prctl (mandatory to restrict without CAP_SYS_ADMIN) and declares the access classes it will control:
📄 .nemesis/ebpf-kernel/src/landlock.rs:24-38 · apply_sandbox, sem root
let r = unsafe { libc::prctl(libc::PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) };
// ABI V5 (kernel 6.8); degrada em kernels mais antigos
let ruleset_result = Ruleset::default()
.handle_access(AccessFs::Execute)
.and_then(|r| r.handle_access(AccessFs::WriteFile))
.and_then(|r| r.handle_access(AccessFs::ReadFile))
.and_then(|r| r.create());The cgroup scoping lives in the kernel hook: if the agent cgroup is not configured, or the process is not the agent's, the program allows and exits. Only agent processes are checked:
📄 .nemesis/ebpf-kernel/ebpf/nemesis-block.bpf.c:83-94 · escopo por cgroup do agente
u64 *agent_cgroup = bpf_map_lookup_elem(&agent_cgroup_map, &cgroup_key);
if (!agent_cgroup || *agent_cgroup == 0)
return 0; // cgroup não configurado, permite tudo
u64 current_cgroup = bpf_get_current_cgroup_id();
if (current_cgroup != *agent_cgroup)
return 0; // processo não é do agente, permiteEgress is decided at the socket_connect hook: with enforce on, the destination must be in the allowlist (LPM trie by CIDR and port), otherwise it is -EPERM. The allowlist's pure logic is fail-closed by construction: an empty list means no connections.
📄 .nemesis/ebpf-kernel/ebpf/nemesis-block.bpf.c:173-205 e src/egress.rs:1-6
struct egress_val *val = bpf_map_lookup_elem(&egress_allow_v4, &key);
if (val && (val->port == 0 || val->port == dport))
return 0; // destino allowlistado
egress_emit("ipv4");
return -EPERM; // fora da allowlist, negaDecisions and limits
Egress has an enforce switch: off, it only observes and logs (rollout mode), a default chosen so nobody's machine breaks before the allowlist is tuned. Non-network socket families (like AF_UNIX) are allowed, because blocking them would break legitimate local communication.
Capabilities here are more posture than explicit calls in the code: the system does not require root and uses no_new_privs so not even child processes gain privilege. It is least privilege applied end to end.
Self-check
▸Why is blocking restricted to the agent cgroup?
To contain only the agent without touching the rest of the user's system. If the process does not belong to the agent cgroup, the eBPF program allows and exits immediately.
▸What does it mean for the egress allowlist to be fail-closed?
That the default is deny: an empty allowlist means no outbound connection is allowed. Only what was deliberately allowed passes, which closes the exfiltration and C2 channel.
▸How does Landlock run without root?
By enabling no_new_privs via prctl before applying the ruleset; the kernel then accepts the self-restriction without requiring CAP_SYS_ADMIN.
Further reading
Citações verificadas contra o repositório. Voltar ao índice.