faaleoleo Log Audit
Cryptographically signed, hash-chained audit logs for Bacula
What it is
Audit logs that prove they haven’t been touched
Bacula records what happened. faaleoleo Log Audit proves the record has not been altered.
Every event written by faaleoleo Log Audit carries two cryptographic guarantees. A SHA-256 hash chain links each entry to the one before it — deleting, inserting, or modifying any entry breaks every subsequent hash. An ed25519 signature over each entry’s hash proves authenticity: only the private key on the Bacula server could have produced it.
The result is a log where tampering is not just detectable — it is provably detectable by anyone holding the public key, offline, without any access to your infrastructure.
# Last 4 entries ● [1042] 2026-06-21 08:32 warning system auth_failure 192.168.1.71 ● [1043] 2026-06-21 08:33 warning system auth_failure 192.168.1.71 ● [1044] 2026-06-21 09:15 info ops-team console_connect bacula-dir ● [1045] 2026-06-21 09:17 info ops-team config_reload bacula-dir # Verifying the same log against the public key ✓ VERIFIED — 1,045 entries, chain intact, all signatures valid.
Compliance
What the standards actually require
Standard Bacula logging satisfies none of these protection requirements. faaleoleo Log Audit satisfies all of them.
NIS2 — Article 21
Essential and important entities must implement monitoring and detection measures and demonstrate they were in effect. A log that can be silently edited does not satisfy this requirement.
GDPR — Article 5(2) & 32
The accountability principle places the burden of proof on the controller. Records of backup access and restore operations must be indisputable to satisfy supervisory authority requests.
DORA — Article 9 & 10
Financial entities must maintain ICT event detection capabilities and trustworthy logs for incident investigations. Regulators expect those logs to be tamper-evident, not just present.
ISO 27001 — A.8.15
Event logs must be "produced, stored, protected, and analysed." Protection means the log cannot be altered without detection — exactly what the hash chain and signature guarantee.
Incident tracking
The log doesn’t just record events — it tracks your deadlines
Every entry at error/critical severity, or carrying a correlation ID, is grouped into a candidate incident. NIS2 (Article 23) and DORA (Article 17) reporting deadlines are computed against it automatically — early warning, notification, and final report windows, all tracked from the moment of detection.
An overdue tag is a clock check, not a verdict: the classifier is deliberately blunt, so every candidate still needs a human to read the entry behind it. Acknowledging a stage appends a permanent, signed, attributable entry to the same log — who reviewed it and why — so status is always recomputed from the signed record, never a separate database.
This closes a gap the standards themselves don’t stop at: NIS2, DORA, GDPR, and ISO 27001 all scope event detection and accountability across an organisation’s systems generally, not only the backup layer. See the July 2026 update for the full detail, including the security hardening pass covered below.
# Candidate incident, computed deadlines seq-376 severity=error entries=1 detected=2026-07-11T19:52:09 [OVERDUE] actions: ssh_event resources: bacula-dir nis2 early_warning=2026-07-12T19:52:09 (OVERDUE) notification=... (pending) final=... (pending) dora notification=2026-07-12T19:52:09 (OVERDUE) final=... (pending)
How it works
Two guarantees on every entry
faaleoleo Log Audit runs alongside Bacula without modifying it. It reads events, signs them, and writes to a separate append-only file.
Hash chain
Each entry carries the SHA-256 hash of the entry before it. Delete, insert, or reorder any entry and the chain breaks at that point — detectable on the very next verification run.
ed25519 signature
Each entry is signed with a private key that never leaves the Bacula server. Verification requires only the public key — auditors can verify offline with any ed25519 library.
Canonical JSON
The hash is computed over a deterministic JSON serialisation (sorted keys, no whitespace). Cross-tool verification is possible in any language without faaleoleo tooling.
Monotonic sequence numbers
Every entry is numbered. Gaps are detected immediately during verification — a log with entries 1–99 and 101 onwards proves entry 100 was deleted.
Append-only JSONL
One event per line. Human-readable and parseable by any JSON tool, SIEM, or log aggregator. No proprietary format, no vendor lock-in.
Non-intrusive
All integration modes are strictly read-only with respect to Bacula. No configuration changes to the Director are required for the community edition.
Multi-source intake
A host-wide socket intake mode lets any other service write signed entries into the same chain without ever holding the private key, and a systemd journal bridge folds any unit’s log straight in — one tamper-evident trail, not just for Bacula.
Hardened signing core
Private key pages locked into RAM and zeroized on drop, atomic key file creation, symlink-safe log access, and kernel-level sandboxing on every service unit.
In practice
What a verification run looks like
Two commands: tail to inspect the log, verify to prove it has not been altered. The auditor needs only the public key — no access to the Bacula server.
Clean log
1,045 entries spanning two weeks. Every hash matches, every signature is valid. Exit code 0.
# Last 8 entries ● [1038] 2026-06-21 01:00 info scheduler job_complete NightlyFull / db01-fd ● [1042] 2026-06-21 08:32 warning system auth_failure 192.168.1.71 ● [1044] 2026-06-21 09:15 info ops-team console_connect bacula-dir ● [1045] 2026-06-21 09:17 info ops-team config_reload bacula-dir # Verifying against the public key ✓ VERIFIED — 1,045 entries, chain intact, all signatures valid. Timespan: 2026-06-07T00:00:01Z → 2026-06-21T09:17:33Z
Tampered log
Entry 3 was modified after signing. The hash mismatch is detected immediately, and the broken chain flags every subsequent entry.
# Verifying against the public key ✗ VERIFICATION FAILED 2 error(s) found. Timespan: 2026-06-07T00:00:01Z → 2026-06-21T09:17:33Z Errors: ✗ [seq=3] Hash mismatch: stored=a1a279b8fd5e0654... computed=9f3c8b12d0e7a123... ✗ [seq=4] Hash chain broken: prev_hash=a1a279b8fd5e0654... expected=9f3c8b12d0e7a123...
Included in every faaleoleo plan — no add-on, no extra cost.
faaleoleo Log Audit is configured and running from day one on every managed Bacula deployment we operate. Every customer receives the public key for their installation at setup.
Talk to us