| 1 | #!/usr/bin/env bash |
| 2 | # SPDX-License-Identifier: AGPL-3.0-or-later |
| 3 | # |
| 4 | # Nightly AIDE wrapper. Runs `aide --check` and: |
| 5 | # - on no changes: silent (cron mail isn't sent) |
| 6 | # - on changes: appends the diff to /var/log/shithub/aide.log |
| 7 | # AND emits a tagged systemd journal record so the |
| 8 | # operator can `journalctl -t shithub-aide`. |
| 9 | # |
| 10 | # Email delivery is intentionally not wired up yet: the droplet has |
| 11 | # no MTA + the project's outbound SMTP (Postmark) is approval-gated. |
| 12 | # Once Postmark is approved end-to-end, swap the journal emit for a |
| 13 | # curl POST to https://api.postmarkapp.com/email — see the runbook |
| 14 | # at docs/internal/runbooks/aide.md. |
| 15 | |
| 16 | set -uo pipefail # NOT -e: aide --check exits non-zero on diff, which |
| 17 | # is the expected, non-fatal "you have alerts" signal. |
| 18 | |
| 19 | LOG=/var/log/shithub/aide.log |
| 20 | mkdir -p "$(dirname "$LOG")" |
| 21 | |
| 22 | ts() { date -u +%Y-%m-%dT%H:%M:%SZ; } |
| 23 | |
| 24 | # --config is mandatory on AIDE 0.18+ (Ubuntu 24); the binary won't |
| 25 | # pick up /etc/aide/aide.conf implicitly. Match the path the package |
| 26 | # ships and that aideinit / dailyaidecheck use. |
| 27 | OUT="$(aide --config=/etc/aide/aide.conf --check 2>&1)" |
| 28 | RC=$? |
| 29 | |
| 30 | case "$RC" in |
| 31 | 0) |
| 32 | # No changes — be silent. Touch a heartbeat file so the |
| 33 | # operator can confirm the cron actually ran today. |
| 34 | date -u +%Y-%m-%dT%H:%M:%SZ > /var/run/shithub-aide.last-clean |
| 35 | exit 0 |
| 36 | ;; |
| 37 | 1|2|3|4|5|6|7) |
| 38 | # AIDE encodes which categories changed in the exit code |
| 39 | # (added/removed/changed file bits OR'd together). Any |
| 40 | # non-zero is operator-visible by design. |
| 41 | { |
| 42 | echo "[$(ts)] aide reported changes (rc=$RC)" |
| 43 | echo "----------------------------------------" |
| 44 | echo "$OUT" |
| 45 | echo "----------------------------------------" |
| 46 | } >> "$LOG" |
| 47 | # systemd-cat tags the journal so `journalctl -t shithub-aide` |
| 48 | # filters cleanly. Priority warning so it shows up in |
| 49 | # default `journalctl --priority=warning` queries. |
| 50 | printf '%s\n' "$OUT" \ |
| 51 | | systemd-cat -t shithub-aide -p warning |
| 52 | exit 0 |
| 53 | ;; |
| 54 | *) |
| 55 | # Anything else: AIDE itself failed (missing DB, IO error, |
| 56 | # config parse error). That's a different class — fail loud. |
| 57 | { |
| 58 | echo "[$(ts)] aide RUN FAILED rc=$RC" |
| 59 | echo "$OUT" |
| 60 | } >> "$LOG" |
| 61 | printf 'aide run failed (rc=%s)\n%s\n' "$RC" "$OUT" \ |
| 62 | | systemd-cat -t shithub-aide -p err |
| 63 | exit "$RC" |
| 64 | ;; |
| 65 | esac |