feat(audit): off-box chain-head export, verify-heads, maintenance login (ScadaBridge#107)
Item 1 (owner decision: no signing). AuditLog:HashChain:HeadExport (default
off): both centrals periodically read every month's chain head (LastSeq,
LastHash, purge anchor, chained rows, tombstones; one snapshot under the
chain lock SHARED) and append the changed heads to a sink the DB login cannot
write - File (append-only JSONL per node per day, local or UNC) or Syslog
(RFC 5424 over UDP / TCP octet counting). `audit verify-heads --path` reads
those records back (file or syslog-collector lines) and the new
VerifyAuditChainHeads command checks each against the database: Matches /
Extended / Purged, or Mismatch / Regressed / MissingHead / MissingLink /
CountMismatch (exit 3). AuditChainHeadStoreTests forges a chain with the
production hasher: verify-chain says Intact, verify-heads says Mismatch.
Item 3. AuditLog:MaintenanceConnectionString (${secret:}-capable, unset =
today's single login) runs the chain job, chain verify / head export, the
partition switch-out + archive and the per-channel purge on
AuditMaintenanceContext (keyed audit-maintenance repository/archive, purge
actor prefers them). AuditMaintenanceLoginTests runs the hardened setup on
SQL Server: writer (datareader/datawriter/audit_writer) ingests and is
refused chaining and deleting; the chainer+purger login does the full cycle.
Two-login grants documented in Component-AuditLog.md.
Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
@@ -304,7 +304,7 @@ The `scadabridge audit` group targets the centralized Audit Log component (#23)
|
||||
exposes the UI-equivalent operational audit surface. Permissions follow the same
|
||||
read-vs-export split the Central UI uses (see Component-AuditLog.md, Security &
|
||||
Tamper-Evidence, and Security & Auth #10): `audit query`, `audit tree`, and
|
||||
`audit verify-chain` and `audit archive list|verify` require the `OperationalAudit`
|
||||
`audit verify-chain`, `audit verify-heads` and `audit archive list|verify` require the `OperationalAudit`
|
||||
permission; `audit export`
|
||||
additionally requires `AuditExport`; `audit backfill-source-node` requires the
|
||||
`Admin` role (maintenance path only). The server enforces permission checks and
|
||||
@@ -316,6 +316,7 @@ scadabridge audit export --since <t> --until <t> --format csv|jsonl|parquet --ou
|
||||
scadabridge audit tree --execution-id <guid> [--format table|json]
|
||||
scadabridge audit backfill-source-node --before <ISO-8601-UTC> [--sentinel <value>] [--batch <n>]
|
||||
scadabridge audit verify-chain --month <YYYY-MM>
|
||||
scadabridge audit verify-heads --path <file-or-directory>
|
||||
scadabridge audit archive list
|
||||
scadabridge audit archive verify --name <archive>
|
||||
```
|
||||
@@ -350,6 +351,18 @@ scadabridge audit archive verify --name <archive>
|
||||
`0` intact or not chained, `3` broken, `1` error, `2` authorization failure. Requires
|
||||
`OperationalAudit` and a caller not scoped to specific sites. See
|
||||
Component-AuditLog.md, Security & Tamper-Evidence.
|
||||
- `audit verify-heads --path <file-or-directory>` (ScadaBridge#107) — reads the AuditLog
|
||||
chain heads exported off-box by `AuditLog:HashChain:HeadExport` (JSON lines from the file
|
||||
sink, or any log a syslog collector wrote — each line's JSON object is taken from its first
|
||||
`{` to its last `}`; a directory is read recursively for `*.jsonl`/`*.log`/`*.txt`),
|
||||
collapses duplicates and sends them in the `VerifyAuditChainHeads` management command. The
|
||||
server checks each exported head against the database chain — `Matches`, `Extended` (chain
|
||||
grew; the link at the exported sequence still carries the exported hash), `Purged` (removed
|
||||
by a partition purge; verify the archive), or the failures `Mismatch` (rewritten chain),
|
||||
`Regressed`, `MissingHead`, `MissingLink`, `CountMismatch`. Exit `0` verified, `3` a
|
||||
failure, `1` error (no records found, bad input, `CHAIN_BUSY`), `2` authorization failure.
|
||||
Same gate as `verify-chain`. Run it together with `verify-chain`: a forger who rewrites rows
|
||||
and re-hashes the chain inside SQL Server passes `verify-chain` but not `verify-heads`.
|
||||
- `audit archive list` / `audit archive verify --name <archive>` (ScadaBridge#42) —
|
||||
send the `ListAuditArchives` / `VerifyAuditArchive` management commands for the
|
||||
AuditLog Parquet cold archives the retention purge writes (`AuditLog:Archive`,
|
||||
|
||||
Reference in New Issue
Block a user