Filesystem Security
Integrity Monitoring
Detecting unexpected file changes with AIDE and friends.
Prevention fails eventually. When it does, the difference between a contained incident and a lingering compromise is often whether you noticed the change at all. Attackers who gain a foothold routinely modify files to persist: they replace a system binary with a trojaned version, add an SSH key to authorized_keys, or edit /etc/passwd. Integrity monitoring records a trusted baseline of chosen files and periodically compares the live system against it, flagging anything added, removed, or altered. It is the detection layer that pairs with the prevention layers in Mount Options and Disk Encryption.
The core idea: baseline and compare
An integrity monitor works in two phases. First it walks a set of paths and records attributes for each file: a cryptographic hash of the contents plus metadata like owner, permissions, size, and inode. That snapshot is the baseline, and it must be created on a system you trust (ideally right after a clean install). Later, a check re-scans the same paths and reports the diff. The baseline itself becomes a target, so it should be stored read-only or off the host.
| Tool | Scope | Strength | Limitation |
|---|---|---|---|
dpkg --verify | Files from installed .deb packages | Free, already present, no baseline step | Only knows package-managed files; misses /etc additions and non-package binaries |
| AIDE | Any paths you configure | Rich attribute checks, tunable rules, cryptographic hashes | You must build and protect the baseline; needs tuning to avoid noise |
rpm -V | Files from RPM packages | Equivalent of dpkg --verify on Fedora/RHEL | RPM distros only |
Package-manager verification (the free first pass)
On Debian and Ubuntu, dpkg already stored MD5 checksums for every packaged file at install time, so you can detect modified system binaries with no setup.
# Verify all installed packages against their recorded checksums
sudo dpkg --verify
Expected output (a modified file shows changed attributes):
??5?????? c /etc/hosts
missing /usr/bin/example-tool
The flag string decodes left to right: 5 means the MD5 checksum differs, and the trailing c marks it as a config file (so a change may be legitimate). A clean system prints nothing. This catches replaced binaries quickly, but it is blind to files that no package owns, such as a rogue script in /etc/cron.d or an added SSH key.
Baselining with AIDE
AIDE (Advanced Intrusion Detection Environment) covers what package verification cannot. Install it, generate a baseline, then run checks against it.
sudo apt-get install -y aide
# Build the initial database from the shipped ruleset
sudo aideinit
Expected output:
Running aide --init...
Start timestamp: 2026-07-06 10:14:22 +0000 (AIDE 0.18)
AIDE initialized database at /var/lib/aide/aide.db.new
Number of entries: 61034
Activate the new database (aideinit writes a .new file so you can review it first), then run a check.
sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
# Simulate a change, then check
echo "* * * * * root /tmp/x" | sudo tee /etc/cron.d/rogue >/dev/null
sudo aide --check
Expected output:
Summary:
Total number of entries: 61034
Added entries: 1
Removed entries: 0
Changed entries: 0
Added entries:
f++++++++++++++++: /etc/cron.d/rogue
The f marks a file and the + columns show which attributes are new. AIDE’s rules are configured in /etc/aide/aide.conf, where selection lines pick paths and rule macros decide which attributes to track (for example, checking permissions and hashes on /bin but ignoring mtime on log directories).
What to monitor and the alert-fatigue tradeoff
The instinct is to monitor everything, but a check that reports thousands of expected changes is a check nobody reads. The skill is scoping. Monitor slow-changing, high-value targets closely and exclude churn.
| Watch closely | Exclude or relax |
|---|---|
/bin, /sbin, /usr/bin, /usr/sbin (binaries) | /var/log (grows constantly) |
/etc (config, cron, SSH keys) | /tmp, /var/tmp (scratch) |
/boot (kernel, initramfs) | /proc, /sys, /dev (virtual) |
/root/.ssh, user authorized_keys | Package caches and build dirs |
Every legitimate change (a package update, a config edit) will trigger AIDE, so you must re-baseline after authorised changes, or the next check drowns real alerts in expected ones. Run checks from cron, ship the report somewhere durable, and update the baseline as a deliberate step in your change process. An unread alert stream is worse than none, because it creates false confidence.
Practical Guidance
- Baseline immediately after a clean install, while you still trust the system, and store the baseline read-only or off the host.
- Use
sudo dpkg --verifyas a zero-setup first pass for modified system binaries before reaching for AIDE. - Scope AIDE tightly: watch binaries,
/etc,/boot, and SSH key files closely; exclude logs, scratch, and virtual filesystems. - Re-baseline deliberately after every authorised change (package updates, config edits) so real alerts are not buried in expected ones.
- Automate checks from cron and route the report to durable, off-host storage, since the local database and reports are themselves attacker targets.
- Treat monitoring as detection, not prevention: it tells you something changed, so pair it with mount options and encryption.