Tamper resistance and self-update
What this page is
The hardening measures the sensor applies to its own process at startup, the binary integrity attestation it computes, and the crash-loop-rollback state machine that swaps it back to a known- good version when the current binary cannot stay alive.
Why it exists this way
Architecture Section 7.2 and 7.4 are explicit: the sensor must resist tampering and must not become a permanent regression when a bad update ships. None of the measures here defeat a local-root attacker, but they raise the bar against casual tampering and surface observable signals (failed seccomp, mismatched binary hash) that the central server can act on.
How it works
Two modules collaborate:
edr_linux::guard applies four measures at startup,
all entered from a single apply_hardening call:
prctl(PR_SET_DUMPABLE, 0), kernel refuses to write a core dump that could leak eBPF map contents, the mTLS client key, or analyst-collected forensic data on crash.prctl(PR_SET_NO_NEW_PRIVS, 1), setuid/setgid bits and file capabilities cannot take effect for any child the sensor spawns. Precondition for unprivileged seccomp.- Capability bounding-set trimmed to
{CAP_BPF, CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW}viacapset; every other capability is dropped. - Seccomp-bpf filter installed in kill-process mode with an allow-list of approximately 100 syscalls needed for sensor operation. Filter is encoded with raw BPF instructions rather than libseccomp to keep the crate's dependency surface in Rust.
compute_binary_hash returns the SHA-256 of
/proc/self/exe so the daemon's startup log can attest the
binary identity. An attacker rewriting the binary cannot avoid
this report without patching the hash computation itself.
edr_linux::update is the state machine that recovers
from a bad update.
UpdateStatecycles Idle → Available → Downloading → Verifying → Ready → Applying, with the terminal RolledBack state. State is persisted to a small JSON file so a crash between phases does not lose progress.CrashLoopDetectorrecords crash timestamps in a bounded ring (last sixteen). Three crashes inside ten minutes is the rollback threshold per the architecture document.RollbackManagerstages the current binary as the rollback target before applying an update. A failed apply reverts byrename, atomic on POSIX filesystems.evaluate_startupis the entry-point the daemon calls before initialising the runtime. It returns a typedCrashOutcome:Healthy,CrashLoopDetected(rollback requested), orCrashLoopDetectedNoRollback(no previous version on disk; the init system surfaces the failure).
What goes wrong
- Hardening on systems without the syscalls the allow-list
expects (older kernels, containerised hosts with restricted
seccomp),
HardeningErrorsurfaces with the specific measure that failed. The daemon refuses to start under hardening failure rather than silently running unprotected. - Rollback impossible because
RollbackManager.previous_pathisNone, the sensor exits and lets the init system surface the failure. Masking the crash by ignoring it would hide the regression from the fleet. - The binary hash on a freshly built sensor differs from the
one the Collector last saw, the central server raises a
binary_driftalert. Causes range from legitimate updates (expected) to silent tampering (worth investigating).