Repository navigation
Troubleshooting
Start with these two commands. Most problems show up in one of them.
aegisctl preflight
journalctl -u aegisd -n 50The config is invalid. The journal has the file, line and column:
journalctl -u aegisd -n 5
aegisd --check-configFix the file, then systemctl start aegisd. The unit doesn't retry on
status 78, because the same file would fail the same way.
The most common cause is mode = "enforce" on a host without the BPF
LSM or nft. The error says which; fix that, or set the mode back to
"audit".
The journal shows something like loading eBPF object, attaching ...
or a missing program. Run aegisctl preflight:
-
bpf-lsmFAIL: the BPF LSM isn't enabled, and the exec probes can't attach without it. Apply thegrubbycommand the check prints and reboot. See preflight. -
btfFAIL: the kernel has no BTF, so aegisd can't find the struct offsets it needs. -
kernelFAIL: the kernel is older than 6.12.
aegisd attaches all its probes and hold programs or none, and won't run with a partial set.
The kernel side stopped delivering events, and aegisd exited non-zero so systemd would restart it. If it happens again, report it with the journal around the restart and your kernel version.
A second copy is running, often one started by hand for testing while
the service is also up. Stop one of them. The lock is
/run/aegis-v2/aegisd.lock, and it's released when its process exits;
don't delete it.
Something changed the socket directory's ownership or mode. The unit creates it as root, 0700. Remove the directory and start the service again, so systemd recreates it.
That's on purpose. While aegisd runs, a BPF LSM program refuses every
other process, root included, an attach-level ptrace access to it:
strace -p, gdb -p, /proc/<pid>/mem and process_vm_readv. It
applies in audit mode too. To see what aegisd is doing, use
journalctl -u aegisd, aegisctl status, or run it in the foreground
with --debug on a test host. See the
threat model.
-
aegisd is not running (no socket at /run/aegis-v2/aegisd.sock): checksystemctl status aegisd. -
aegisctl must run as root: the socket only answers root. Usesudo. -
no reply from aegisd within 30 s: the daemon is alive but not answering. Check the journal, and report it as a bug with the output.
That's normal on a quiet host: most activity scores below 50, and
below-threshold decisions aren't recorded. To see that probes are
working, check that events in aegisctl status is climbing.
To see every scoring decision, including the ones that stay below the
threshold, run aegisd with --debug for a while. One way:
systemctl stop aegisd
aegisd --debug # Ctrl-C when done
systemctl start aegisdEach decision prints a line such as:
aegisd: debug: tgid 4242 comm x score 25.0 tier Log: exec from a temp directory (/tmp/x)
Also check incidents list only shows incidents updated in the last 30
days. incidents show <id> finds older ones on disk.
Work through them as scoring describes. Look at each incident's evidence and member verification:
-
A tool you trust, marked
unverified: aegisd only checks binaries an allowlist entry or pin names, so this is expected for anything not listed yet. Add its path to the list for the signal that fired, and pin its SHA-256 too if no RPM package owns it. -
A tool you trust, marked
modified: the file no longer matches its package or pin. Find out why before you allowlist anything:rpm -V <package>shows what changed. -
Evidence that says
allowlist withheld:: see the next section.
- The binary must verify. Check the incident's
verificationfor that process. - The path must be exact and absolute. Allowlists match the resolved
path the kernel executed, such as
/usr/bin/python3.12, not the symlink you typed. A symlink at a listed or pinned path never verifies. - Look for
allowlist withheld:in the evidence reason. It means the binary verified but the process wasn't trusted:-
LD_PRELOAD, LD_AUDIT or LD_LIBRARY_PATH is set: the process was started with one of those loader variables. On a host that setsLD_LIBRARY_PATHglobally (in/etc/environmentor a profile script, say), every allowlisted tool hits this. Remove the global setting, or expect these incidents. -
its environment could not be read: the kernel couldn't read the environment at exec, it was longer than 32 KiB, or no answer came within 1 second. For a process that was already running when aegisd started, the answer is a read of/proc/<pid>/environ, often lost because the process exited first. If it happens for every one of those until a restart, see stuck environment reads. -
it was the target of a ptrace attach: a debugger attached to it, or to its parent before the fork. The mark clears at the next exec.
-
- For
excluded_parent_processes, only the immediate parent of the process doing the exec counts. - For
scan_binaries, the scanner must run as root and from a terminal. - After a reload, check the journal for
configuration reloaded. A failed reload keeps the old config and says why.
If every allowlisted process that was running before aegisd started
shows its environment could not be read until aegisd restarts,
/proc environment reads are stuck. A read of a process
whose memory is backed by something that never answers, such as a hung
FUSE filesystem, can block for good, and after 16 of those every
further read is refused. That gives false positives, not missed
detections. systemctl restart aegisd clears it.
If the RPM database was broken or unreadable when aegisd checked a
binary, rpm can answer "not owned", and aegisd caches that. Fix the
database (rpm --rebuilddb), then systemctl reload aegisd, which
empties the cache.
On btrfs subvolumes and overlayfs, the device number the kernel reports
for an executed file doesn't match what stat reports, so aegisd can't
confirm it's the same file. Those binaries stay unverified, and
allowlists don't apply to them. There's no workaround yet; XFS and
ext4 aren't affected. See the threat model.
On a host that suspends, the 60-second process sweep can drop processes that were alive across the suspend, along with their scores. This is a known limitation, and servers rarely suspend.
These should stay at 0. Each one that grows means something was lost.
- dropped events: the 4 MiB ring buffer was full. Expect it only in huge bursts of execs or connects.
-
incidents dropped: the incident writer fell 256 decisions behind.
Check disk latency on
/var/lib. - incident write errors: an incident write failed. The journal has the error; check disk space.
-
verifications skipped: 1,024 verifications were already in
flight. Check that
rpm -qf /usr/bin/bashreturns quickly. - defer queue overflows: a process had 64 signals waiting on verification, or 8,192 were waiting in all. Report it, with the incident.
-
telemetry dropped: journald: journald was away or its buffer was
full. Check
systemctl status systemd-journald. - telemetry dropped: otlp ... or webhook ...: see telemetry and webhooks.
incident repeats not recorded is different: it counts decisions inside
an open incident that added nothing new, and it grows in normal use.
The journal says telemetry jsonl <path>: Permission denied or
Read-only file system. The path has to be under /var/log/aegis-v2.
Telemetry changes also need systemctl restart aegisd, not a reload.
aegisd doesn't rotate it. Set up logrotate with copytruncate; see
telemetry.
- Check the boolean:
getsebool aegisd_can_network_connectmust beon. Without it the journal shows a connection error andausearch -m AVC -ts recentshows the denial. - Check the journal for
otlp export failing, dropping records:, followed by the reason. - Check the endpoint. aegisd posts to
<endpoint>/v1/logswith a JSON body, so the collector needs its OTLP/HTTP receiver enabled. - For
https://, the collector's certificate must chain to a CA in the system trust store. - Restart after any telemetry change.
An endpoint with user:pass@ in it is rejected at load; put credentials
in [telemetry.otlp.headers].
Check that the table is still there:
nft list table inet aegisIf it isn't, something flushed the ruleset: systemctl reload nftables, a firewall tool, or nft flush ruleset. You don't need to
do anything. Within about 10 seconds aegisd puts the table back, with a
drop rule for each tree that's still quarantined, and the trees stay
frozen and held in the meantime. One that was moved but never frozen
gets its rule back as well. Until then only its held processes are
blocked, so a child that aegisd moved but hadn't held yet can reach
the network. aegisctl incidents show <id> then lists an nft_drop
re-added after the table was removed.
Still no rule after half a minute? Look in the journal
(journalctl -u aegisd) for the nft error. A quarantined tree
that's no longer frozen gets no rule back: its processes have all
exited, or something thawed it outside aegisctl. See the
threat model.
Removing the package while a tree was quarantined or throttled leaves
its scope, and the inet aegis table, in place, and aegisctl is gone.
Installation has the
commands to thaw it, end it, and clean up.
ausearch -m AVC -ts recent | grep aegisAny denial from aegisd_t is a bug; please report it with that output.
See SELinux.
Open an issue with your Rocky and kernel versions, the Aegis version,
the aegisctl preflight --json and aegisctl status --json output, and
the relevant journal lines.
From docs/troubleshooting.md at v0.2.3.