Skip to main content

max / makenotwork

3.2 KB · 93 lines History Blame Raw
1 //! Local CA-bundle freshness task. Reads the trust-anchor package and bundle on
2 //! the host PoM runs on, and alerts on ok -> not-ok transitions.
3
4 use tokio::task::JoinHandle;
5 use tracing::{info, warn};
6
7 use pom::alerts::Alerter;
8 use pom::checks::ca_bundle;
9 use pom::config::Config;
10 use pom::db;
11
12 use super::{CheckInterval, configured_targets};
13
14 pub(crate) fn spawn_ca_bundle_tasks(
15 config: &Config,
16 pool: &sqlx::SqlitePool,
17 cancel: &tokio_util::sync::CancellationToken,
18 alerter: Option<&Alerter>,
19 ) -> Vec<JoinHandle<()>> {
20 let mut handles = Vec::new();
21
22 for (name, target_config) in configured_targets(config) {
23 let Some(ca_config) = target_config.ca_bundle else {
24 continue;
25 };
26
27 let name = name.clone();
28 let label = target_config.label.clone();
29 let alerter = alerter.cloned();
30 let pool = pool.clone();
31 let cancel = cancel.clone();
32
33 info!(
34 "{name}: CA bundle check every {}s ({}, floor {} certs)",
35 ca_config.interval_secs, ca_config.package, ca_config.min_certs,
36 );
37
38 handles.push(tokio::spawn(async move {
39 let mut ticks = CheckInterval::new(ca_config.interval_secs, cancel);
40
41 // Seed from the ledger for the same reason systemd's task does: a
42 // restart while already stale must not re-fire the alert.
43 let mut previous_status: Option<String> =
44 db::get_latest_alert_matching(&pool, &format!("ca_bundle:{name}"), "ca_bundle_%")
45 .await
46 .ok()
47 .flatten()
48 .filter(|a| a.alert_type == "ca_bundle_stale")
49 .and_then(|a| a.to_status);
50
51 while ticks.next().await {
52 let result = ca_bundle::check_ca_bundle(&name, &ca_config).await;
53
54 if result.status == "ok" {
55 info!(
56 "{name}: CA bundle current ({} {})",
57 result.package,
58 result.installed.as_deref().unwrap_or("unknown"),
59 );
60 } else {
61 warn!(
62 target = %name,
63 status = %result.status,
64 issues = ?result.issues,
65 "CA bundle not current"
66 );
67 }
68
69 if let Err(e) = db::insert_ca_bundle_check(&pool, &result).await {
70 tracing::error!("{name}: failed to store CA bundle check: {e}");
71 }
72
73 if let Some(ref alerter) = alerter {
74 let prev_ok = previous_status.as_deref().is_none_or(|s| s == "ok");
75 let now_ok = result.status == "ok";
76
77 if prev_ok && !now_ok {
78 alerter
79 .send_ca_bundle_alert(&name, &label, &result.status, &result.issues)
80 .await;
81 } else if !prev_ok && now_ok {
82 alerter.send_ca_bundle_recovery(&name, &label).await;
83 }
84 }
85
86 previous_status = Some(result.status);
87 }
88 }));
89 }
90
91 handles
92 }
93