Add tamper-evidence: package-DB integrity, self-integrity, dead-man's switch
Answers two hard questions: "what guards the hashes FIM trusts?" and "how do we make the sensor itself hard to silently disable?" — within the honest limit that a root attacker who shares your privileges can't be fully stopped on-box, only made loud. - pkgdb.py: guards the package DB that `pacman -Qkk` trusts. Anchors a fingerprint of /var/lib/pacman/local (refreshed ONLY by the pacman hook) and cross-checks pacman.log; a DB change with no logged transaction is flagged pkgdb_tamper (CRITICAL, sid 100021) — catches an attacker rewriting a stored checksum to mask a modified binary. Verified end-to-end against a simulated hash-overwrite. - selfprotect.py: Sentinel's own binaries/config/units/hook are always in the FIM watch set (self-integrity); a heartbeat is written each loop; an external `watchdog` command polls a remote dashboard and pushes if the sensor goes silent or unreachable — silence becomes the alarm. - daemon: heartbeat + slow-cadence pkgdb check; DB anchor re-anchored in build_fim_baseline so the pacman hook refreshes it after each transaction - web: /api/status now reports heartbeat_age/stale; dashboard shows it - cli: pkgdb-check, watchdog (--url/--token/--max-age, bypasses min-severity) - config: pkgdb_verify/_interval, heartbeat_max_age - README: threat model (trust-anchor problem) + hardening layers (immutability, signed-package + external anchor); reframes "anti-rootkit" as tamper-evidence - tests: +8 (DB fingerprint, anchor/transaction logic, pacman.log parse, heartbeat + watchdog verdicts). 80/80 pass Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
5d577b624f
commit
34bc09041a
13 changed files with 465 additions and 9 deletions
|
|
@ -68,6 +68,13 @@ def main(argv: list[str] | None = None) -> int:
|
|||
fc = sub.add_parser("fim-check", help="scan monitored files and report changes")
|
||||
fc.add_argument("--packages", action="store_true",
|
||||
help="also verify package-owned files via pacman -Qkk")
|
||||
sub.add_parser("pkgdb-check", help="check the package DB for out-of-band tampering")
|
||||
wd = sub.add_parser("watchdog",
|
||||
help="poll a remote dashboard and push if Sentinel is silent")
|
||||
wd.add_argument("--url", required=True, help="dashboard base URL")
|
||||
wd.add_argument("--token", default="", help="dashboard bearer token")
|
||||
wd.add_argument("--max-age", type=int, default=120,
|
||||
help="heartbeat staleness threshold (seconds)")
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
cfg = Config.load(args.config)
|
||||
|
|
@ -93,9 +100,38 @@ def main(argv: list[str] | None = None) -> int:
|
|||
return 0
|
||||
if args.cmd == "fim-check":
|
||||
return _cmd_fim_check(cfg, args.packages)
|
||||
if args.cmd == "pkgdb-check":
|
||||
from . import pkgdb
|
||||
alert = pkgdb.check(cfg)
|
||||
if alert:
|
||||
print(f"[CRITICAL] {alert.detail}")
|
||||
return 1
|
||||
print("Package DB: consistent with the anchor (no out-of-band changes).")
|
||||
return 0
|
||||
if args.cmd == "watchdog":
|
||||
return _cmd_watchdog(cfg, args.url, args.token, args.max_age)
|
||||
return _cmd_run(cfg)
|
||||
|
||||
|
||||
def _cmd_watchdog(cfg: Config, url: str, token: str, max_age: int) -> int:
|
||||
from . import notify
|
||||
from .selfprotect import poll_status, watchdog_verdict
|
||||
ok, msg = watchdog_verdict(poll_status(url, token), max_age)
|
||||
print(("OK: " if ok else "ALERT: ") + msg)
|
||||
if not ok:
|
||||
n = notify.Notification(
|
||||
severity=notify.Severity.CRITICAL, host=url,
|
||||
signatures=(f"dead-man's-switch — {msg}",), sids=(),
|
||||
snapshot_name="-", count=1, dashboard_url=url)
|
||||
# bypass the min-severity gate — a silent sensor is always worth a page
|
||||
for backend in notify.enabled_backends(cfg):
|
||||
try:
|
||||
backend.send(cfg, n)
|
||||
except Exception:
|
||||
pass
|
||||
return 0 if ok else 1
|
||||
|
||||
|
||||
def _cmd_fim_check(cfg: Config, packages: bool) -> int:
|
||||
from . import fim
|
||||
sentinel = Sentinel(cfg)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue