faaleoleo Log Audit: From Tamper-Evident Records to Incident Deadlines

Back in June we introduced faaleoleo Log Audit — hash-chained, Ed25519-signed audit logging for Bacula, included in every managed plan at no extra cost. Since then it has gone through six releases, driven by real usage across our fleet and a full internal security review. It still does everything the original announcement promised. It also now does considerably more — two changes stand out above the rest.

First, the log can now take input from more than just Bacula: any other service on the host can write into the same signed chain, and any systemd unit's journal can be bridged straight in. Second, the log doesn't just record events anymore — it classifies them into candidate incidents and computes NIS2 and DORA reporting deadlines against each one, automatically. Both are covered in detail below, alongside a full security hardening pass the signing and verification core went through in the same stretch of releases.

From records to deadlines

A tamper-evident log proves what happened. It does not, by itself, tell you whether you are still inside your regulatory reporting window. That gap is now closed.

The log now groups entries into candidate incidents — anything at error/critical severity, or carrying a correlation ID — and computes NIS2 and DORA reporting deadlines against each one:

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)

An [OVERDUE] tag means exactly one thing: today is past the computed deadline with no matching acknowledgment on file. It is a clock check, not a verdict. The classifier is deliberately blunt — any error-level line qualifies, including entirely mundane ones — so the next step is always to read the entry behind the tag before acting on it:

$ grep '"seq":376,' audit.jsonl | python3 -m json.tool

In this particular case the entry turned out to be an SSH client resetting the connection during key exchange, before authentication was even attempted — one of the most common lines in any internet-facing sshd log, and not an incident by any reasonable reading. Once a human forms that judgment, acknowledging the stage appends a permanent, signed entry to the same log — who reviewed it, which framework and stage, and a note explaining the call: "benign: TCP reset during SSH kex_exchange_identification before auth attempted".

There is no separate incident-status database — status is always recomputed by scanning the log for matching acknowledgments, so the signed record stays the single source of truth. And acknowledgment is intentionally not something we let anyone script into a blanket "clear all overdue" job: each entry is a deliberate, attributable decision, not a rubber stamp. NIS2 Article 23's 24-hour early warning and 72-hour notification windows, and DORA's severity-tiered initial/notification deadlines, are the built-in defaults — all overridable per organization, since legal applicability is always your own determination.

One audit trail, not just for Bacula

faaleoleo Log Audit was built to watch Bacula. It no longer only watches Bacula.

A new socket-based intake mode lets any other service on the same host write signed entries into the same hash chain without ever holding the private key themselves — the intake process is the only thing that touches the key, and it signs, sequences, and appends each entry on the client's behalf.

A companion mode goes further: point it at any systemd unit and it bridges that unit's journal straight into the signed log, no custom integration required. The seq-376 entry above is a real instance of this — it's an sshd line, not anything Bacula wrote, captured the same way any other service's journal would be.

The result is one tamper-evident, independently verifiable trail for a whole host's worth of services, not a Bacula-only log with everything else left to plain syslog.

This matters for the whole compliance list from our original announcement, not only Bacula-specific requirements within it. DORA Article 9 and Article 10 scope ICT risk management and event detection across a financial entity's ICT systems generally. NIS2 Article 21 requires monitoring and detection measures across an essential or important entity's network and information systems as a whole. GDPR's Article 5(2) accountability principle applies to any system that touches personal data, not just the backup layer. ISO 27001's Control A.8.15 requires event logs to be protected full stop — it does not say "only the backup ones." None of the four stop at Bacula, so a tamper-evident record limited to Bacula only ever covered part of that surface. Being able to fold SSH, authentication, and other host-level events into the same signed chain closes that gap without standing up a second logging system just to satisfy the same regulations.

Hardened again, top to bottom

The signing and verification core that everything above depends on went through a dedicated security pass: private signing key pages are now locked into RAM via mlock and zeroized on drop so they never reach swap; the private key file is created atomically with O_CREAT|O_EXCL at mode 0600, removing any transient readable window; the audit log itself is opened with O_NOFOLLOW to close off symlink attacks; and log rotation uses a random suffix instead of a check-then-rename sequence, eliminating a TOCTOU race. Every service unit now runs under the same kernel-level sandboxing we apply elsewhere — memory limits, restricted syscalls, no new privileges — with restart-loop caps so a misbehaving instance fails safe instead of spinning.

The socket-based ingestion mode also gained peer authentication: socket-server --allowed-users now checks each connecting process's real UID via SO_PEERCRED, in addition to the socket's own filesystem permissions. Previously, any local process that could reach the socket could inject an entry under any claimed source or user — and it would be faithfully signed into the tamper-evident chain as if legitimate. That gap is closed for anyone running the multi-service ingestion mode.

Still included, still no extra cost

None of this changes what faaleoleo Log Audit costs: nothing, on every managed Bacula plan we operate. What has changed is how much the log can now do for you — it takes input from any service on the host, not just Bacula, and it turns that into a running account of exactly which regulatory clock you're on across NIS2, DORA, GDPR, and ISO 27001, and how much time is left.

If audit readiness is part of your evaluation criteria for a managed Bacula provider, talk to us.

The Backup Factory

See it for yourself

We are happy to walk you through exactly what the pipeline does and what the output looks like. No commitment required.

Talk to us