File integrity monitoring: know when system files change
File integrity monitoring takes a fingerprint of every important file and compares it later. If an attacker swaps /usr/bin/ssh, adds a cron job or edits sudoers, the next check lists it. AIDE does this for free on every major distribution; package checksums give a quick second opinion.
How it works
AIDE (Advanced Intrusion Detection Environment) reads its configuration, walks the file system and stores each selected file's checksums, size, permissions, owner and timestamps in a database: the baseline. A check repeats the walk and reports files that were added, removed or changed. Patching also changes files, so the routine is: check, review, accept expected changes, repeat.
Debian and Ubuntu
On Debian 13 (AIDE 0.19) and Ubuntu 24.04 and later, the aide package brings aide-common with Debian's helper scripts, a modular configuration in /etc/aide/aide.conf.d/ and a daily systemd timer.
sudo apt install aide
# build the first baseline (takes minutes; prompts before replacing aide.db)
sudo aideinit
# the daily job: 01:50 plus a random delay of up to 2 hours
systemctl list-timers dailyaidecheck.timer
# run a check by hand
sudo aide --config /etc/aide/aide.conf --check
aideinit writes /var/lib/aide/aide.db.new and offers to copy it to /var/lib/aide/aide.db, the file checks compare against. Since AIDE 0.18 the daily job runs as an unprivileged _aide user with read-only access to all files.
No mail by default. On systemd systems the daily job no longer mails the report unless a local mail transfer agent and s-nail are installed; otherwise it only writes a warning to the journal. Install them and set MAILTO in /etc/default/aide, or forward the journal (Logging and monitoring), so someone actually reads the result.
By default /etc/default/aide sets COMMAND=update and COPYNEWDB=no: each daily run reports changes against the baseline and also writes a fresh aide.db.new, but does not replace the baseline. Do not set COPYNEWDB=yes; the package itself calls it "possibly dangerous" because each change is then reported only once.
RHEL 9 and 10
sudo dnf install aide
sudo aide --init
sudo mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
sudo aide --check
RHEL 9.8 and RHEL 10.2 moved to AIDE 0.19.2. Its configuration syntax differs from the AIDE 0.16 shipped earlier in RHEL 9 (for example database became database_in, and verbose was replaced by log_level and report_level). If you changed /etc/aide.conf, compare it with the new default after the update. The newer AIDE packages in CentOS Stream 9 and 10, the preview of coming RHEL minor releases, add an aide-migrate-config helper that converts old configuration on upgrade, an aide-check.timer, and extra configuration files in /etc/aide.d/. Check what your system has:
rpm -q aide
systemctl list-unit-files 'aide*'
If aide-check.timer exists, enable it with sudo systemctl enable --now aide-check.timer. If not, Red Hat's documentation suggests a daily cron line (05 4 * * * root /usr/sbin/aide --check in /etc/crontab), or create your own timer:
# /etc/systemd/system/aide-daily.service
[Unit]
Description=Daily AIDE check
[Service]
Type=oneshot
ExecStart=/usr/sbin/aide --check
Nice=19
IOSchedulingClass=idle
# /etc/systemd/system/aide-daily.timer
[Unit]
Description=Daily AIDE check
[Timer]
OnCalendar=*-*-* 04:05:00
RandomizedDelaySec=30m
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now aide-daily.timer
The report goes to the journal (journalctl -u aide-daily). AIDE's exit code adds 1 for new files, 2 for removed and 4 for changed files, so in this unit any change marks the run as failed. That makes it visible in systemctl --failed and lets you attach an OnFailure= alert unit.
After patching: update the baseline
Every package update changes binaries and libraries, and AIDE will list them. Accept them only after checking that they match what you installed:
- Run a check and save the report.
- Compare the changed paths with the update history (
/var/log/apt/history.log, ordnf history info last). Anything changed that no package explains, especially in/etc,/usr/bin,/usr/sbin,/usr/libor/root, needs investigation. - Verify changed package files against the package database (next section).
- Update the baseline and copy it off-host again.
# Debian/Ubuntu: create aide.db.new and show changes, then accept it
sudo aide --config /etc/aide/aide.conf --update
sudo cp -p /var/lib/aide/aide.db.new /var/lib/aide/aide.db
# RHEL
sudo aide --update
sudo mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
To accept changes in one area only, --limit restricts the run, for example aide --update --limit /etc. Updating right after every patch run, before an attacker has time to act, keeps reports short and meaningful.
Too much noise? Exclude files that change by design (application caches, uploads, some files in /var) instead of ignoring the report. On Debian put rules such as !/srv/www/example/cache in a new file in /etc/aide/aide.conf.d/ (use a name without dots, like 99_local_excludes); on RHEL edit /etc/aide.conf. Test with aide --config-check.
Quick checks with package checksums
Packages ship checksums of their files, so you can check installed files without any baseline:
# Debian/Ubuntu: built in, checks file contents (MD5)
sudo dpkg --verify
# Debian/Ubuntu: debsums; -s shows only problems, -a includes config files
sudo apt install debsums
sudo debsums -s
sudo debsums -c # just the list of changed files
# RHEL, Fedora, SUSE: size, digest, mode, owner, time and more
sudo rpm -Va
In rpm -Va output each position stands for one test: S size, 5 digest, M mode, U user, G group, T modification time; a . means passed and c marks a configuration file. Changed config files are normal. A changed digest on a binary such as /usr/sbin/sshd is not: reinstall the package from the repository and treat the server as suspect: it may hide a backdoor (Find backdoors).
These checks have a limit: the checksums sit on the same disk, so an attacker with root can change them too. The dpkg manual says its check "should not be considered as any kind of security verification", and debsums calls itself "of limited use as a security tool". Use them for a quick look, AIDE with an off-host baseline for real assurance.
Protect the baseline
A baseline kept only on the monitored server can be rebuilt by an intruder to hide their changes. Red Hat recommends keeping the database, the configuration and the aide binary in a secure location such as read-only media. Practical ways to do that:
- After each
--initor--update, copyaide.db(oraide.db.gz) and the configuration to another machine. Let that machine pull the files, so the server holds no credentials for it. - Record the database's
sha256sumoff-host. Before trusting a report, check that the local database still has the same hash. - For a check you can fully trust, restore the off-host database and run AIDE from rescue media or a known-good system against the server's disk.
- Keep the reports off-host too (mail or central logs), so a quiet report cannot simply be deleted.
Sources
- AIDE project home page
- aide(1) manual page (trixie), Debian
- aide.conf(5) manual page (trixie), Debian
- aideinit(8) manual page (trixie), Debian
- aide-common README.Debian, Debian
- aide NEWS for Debian
- /etc/default/aide, Debian aide package
- aide-common file list (noble), Ubuntu
- Checking integrity with AIDE, RHEL 9 documentation, Red Hat
- Checking integrity with AIDE, RHEL 10 documentation, Red Hat
- RHEL 9.8 release notes: new features, Red Hat
- RHBA-2026:18308 aide bug fix and enhancement update (RHEL 10), Red Hat
- aide.spec (c10s branch), CentOS Stream package sources mirrored by AlmaLinux
- dpkg(1) manual page (trixie), Debian
- debsums(1) manual page (trixie), Debian
- rpm(8) manual page, RPM project