Private
Public Access
1
0
Files
linux_patch_manager/crates/pm-worker/src/audit_verifier.rs
Echo 297bf1bd83 feat(M11+M12): Email notifications, audit hardening, deployment packaging, backup/DR, integration testing
M11 - Email Notifications + Audit Logging Hardening:
- Email notifier (lettre crate) with templates for patch failure, job completion, maintenance reminders
- Audit log hash chaining (prev_hash + row_hash) for tamper-evident logging
- Periodic + on-demand audit integrity verification
- Audit logging for all config changes and certificate operations
- Frontend: email settings integration, audit integrity verification action

M12 - Deployment Packaging, Backup/DR, Integration Testing:
- scripts/backup.sh: Nightly pg_dump, CA backup (GPG), config backup (secrets excluded unless encrypted)
- scripts/setup.sh: Enhanced with backup dir, seed migration, backup cron, systemd target install
- systemd units: Restart=always, WatchdogSec, ReadWritePaths, security hardening
- systemd/patch-manager.target: Service target for coordinated lifecycle
- docs/runbooks/restore.md: Full DR runbook with RPO 24h / RTO 4h targets
- scripts/integration-test.sh: 9 test suites covering full API lifecycle
- scripts/performance-test.sh: NFR validation (dashboard <5s, CIDR /22 <10s, API <2s)
- docs/security-review.md: Comprehensive security control verification
- docs/compliance-mapping.md: HIPAA (6 sections) + PCI-DSS v4.0 (9 requirements) mapped
2026-04-24 00:45:51 +00:00

87 lines
2.5 KiB
Rust

//! Periodic audit log integrity verification.
//!
//! Runs every 24 hours, walks the audit_log rows ordered by id,
//! verifies each row_hash matches the recomputed hash, and logs the
//! result as an `AuditIntegrityVerified` event. If tampering is
//! detected, logs an error and creates an alert.
use std::sync::Arc;
use std::time::Duration;
use sqlx::PgPool;
use pm_core::audit::{log_event, verify_integrity, AuditAction};
use pm_core::config::AppConfig;
/// Run the audit integrity verifier every 24 hours.
pub async fn run_audit_verifier(pool: PgPool, _config: Arc<AppConfig>) {
tracing::info!("Audit integrity verifier started");
// Run immediately on startup
verify_once(&pool).await;
let mut interval = tokio::time::interval(Duration::from_secs(24 * 60 * 60));
loop {
interval.tick().await;
tracing::info!("Running scheduled audit integrity verification");
verify_once(&pool).await;
}
}
/// Run a single integrity verification pass.
async fn verify_once(pool: &PgPool) {
let result = verify_integrity(pool).await;
if result.intact {
tracing::info!(
rows_checked = result.rows_checked,
"Audit integrity verification passed"
);
} else {
tracing::error!(
rows_checked = result.rows_checked,
error_count = result.errors.len(),
"Audit integrity verification FAILED — tampering detected!"
);
for err in &result.errors {
tracing::error!(
row_id = err.row_id,
expected_hash = %err.expected_hash,
actual_hash = %err.actual_hash,
"Audit chain integrity error"
);
}
}
// Log the verification event
log_event(
pool,
AuditAction::AuditIntegrityVerified,
None,
None,
Some("audit_log"),
None,
serde_json::json!({
"intact": result.intact,
"rows_checked": result.rows_checked,
"error_count": result.errors.len(),
"errors": result.errors.iter().take(10).map(|e| serde_json::json!({
"row_id": e.row_id,
"expected_hash": e.expected_hash,
"actual_hash": e.actual_hash,
})).collect::<Vec<_>>(),
}),
None,
None,
)
.await;
// Update last verified timestamp
let _ = sqlx::query(
"UPDATE system_config SET value = NOW()::text, updated_at = NOW() WHERE key = 'audit_integrity_last_verified'",
)
.execute(pool)
.await;
}