Project · Runtime Security
Aegis v2: Runtime Threat Detection That Reads the Whole Process Tree
A binary runs out of /tmp, then something starts scanning the subnet. Most tools log that as two lines you read on Monday. Aegis watches from the kernel with eBPF, adds the signals up across the process tree, and hands you one incident with the evidence attached.
Overview
I look after five servers. Soon it’ll be a few hundred, and the way I keep an eye on five doesn’t stretch that far. Reading logs on Monday works when there’s one person and one rack. It doesn’t when the thing you missed happened on host 212 at three in the morning.
So I built Aegis v2. It’s a runtime threat detector for Rocky Linux 10, written in Rust, that watches every process from the kernel with eBPF and scores what it sees. It has a sibling, Sentry v2, which watches the same hosts for a different kind of trouble: memory pressure, failed units and SELinux denials. Sentry worries about the server falling over. Aegis worries about someone else being on it.
The problem
An intruder who gets a foothold tends to do the same handful of things. Drop a payload in /tmp and run it. Run code that only exists in memory. Attach a debugger to a system service. Start knocking on every port in the subnet.
Each of those, on its own, has an innocent twin. Your build script runs things out of /tmp. Your admin attaches gdb to a daemon. Alert on every one and you’ll be drowning by lunchtime, and you’ll start ignoring the alerts, which is worse than not having them. Most tools give you one line per rule match, and leave the joining up to you.
What I built
Aegis adds things up instead. Every process gets a risk score, and every signal it raises puts points on it: 25 for running something out of a temp directory, 65 for running a memfd, 45 for attaching to pid 1 or a system service, 40 for a burst of outbound connects. The score halves after 30 seconds of quiet, so one odd event fades. A chain of them doesn’t.
Cross 50 and you get a would_throttle incident. Cross 80 and it’s would_quarantine. Here’s the part I care about most: once a process crosses the line, its children join the same incident, and the incident is judged on everything they did between them. Attacks don’t stay in one PID, so the score shouldn’t either.
The repo has a lab script that shows it on a throwaway VM, using nothing but copies of the host’s own bash and strace. A copy of bash in /tmp scores 25. It scans loopback, and the score reaches 65. Its child, a copy of strace, attaches to a throwaway service, and the incident lands at 110. That’s one incident, written to disk with a timeline of every decision, instead of four lines in four places.
Allowlists that ask for proof
A name is easy to fake. Call your payload dnf and plenty of tools will wave it through.
Aegis forgives dnf, gdb, podman and friends only when the binary is owned by an installed RPM and matches the package’s SHA-256, or matches a hash you pinned yourself. It also has to be the same file the kernel actually executed. And a process that’s been loaded with LD_PRELOAD, or has a debugger attached, gets no forgiveness at all.
What it won’t catch
I’d rather you hear this from me. Root who knows Aegis is there can stop it, so ship your telemetry off the host. The LD_PRELOAD and ptrace checks read /proc just after the exec, and a careful attacker can scrub that first. A script run as sh /tmp/x shows up as the shell. UDP isn’t counted, and neither is a scan spread one connection per process. The threat model lists every gap and why it’s there.
Where it is now
v0.1 is an observer, on purpose. It scores, records and reports, and it changes nothing on the host; the end-to-end tests check that /etc is byte-for-byte unchanged. Set mode = "enforce" and aegisd refuses to start. Enforcement is planned for v0.2.0: throttling a process tree, quarantining it and cutting off its network. A honeypot for quarantined traffic is further out on the roadmap.
Why ship a detector that can’t stop anything? Because enforcement that fires on your own backup job is worse than none. A few days in audit mode tells you what normal looks like before anything acts on it. I wrote more about that in I built the smoke detector before the fire.
v0.1.0 came out on 19 October 2026 and v0.1.1 two days later. It’s tested end to end on Rocky Linux 10.2, x86_64 and aarch64, with twelve scenarios on a disposable host, including a container start and an ssh login that must stay quiet. aegisd runs under a hardened systemd unit with six capabilities, and SELinux confines it to its own aegisd_t domain. Overhead hasn’t been measured yet, so I’m not quoting a number.
Try it
Grab both RPMs for your architecture from the releases page, then, as root on a Rocky Linux 10 host:
dnf install ./aegis-v2-*.x86_64.rpm ./aegis-v2-selinux-*.noarch.rpm
aegisctl preflight
systemctl enable --now aegisd
Leave it for 48 to 72 hours of normal work, then run aegisctl incidents list. Anything there is either worth a look or a false positive you can allowlist. The code and the docs are all on GitHub.
// Let's talk
You have ideas.
I have IT solutions.
Fifteen minutes on Zoom is usually enough to tell whether I'm the right person for what you're building.
Denver, CO · Available for architecture, infrastructure, and DevOps engagements