Skip to main content

max / makenotwork

3.6 KB · 85 lines History Blame Raw
1 use tokio::task::JoinHandle;
2 use tracing::info;
3
4 use pom::alerts::Alerter;
5 use pom::checks::tls;
6 use pom::config::Config;
7 use pom::db;
8
9 pub(crate) fn spawn_tls_tasks(
10 config: &Config,
11 pool: &sqlx::SqlitePool,
12 cancel: &tokio_util::sync::CancellationToken,
13 alerter: &Option<Alerter>,
14 ) -> Vec<JoinHandle<()>> {
15 let tls_interval_secs = config.serve.tls_check_interval_secs;
16 let mut handles = Vec::new();
17
18 for name in config.target_names() {
19 let target_config = config.get_target(&name).unwrap().clone();
20 if let Some(tls_config) = target_config.tls {
21 let pool = pool.clone();
22 let name = name.clone();
23 let label = target_config.label.clone();
24 let alerter = alerter.clone();
25 let warn_days = tls_config.warn_days;
26 let cancel = cancel.clone();
27
28 info!("{name}: TLS check every {tls_interval_secs}s (host={})", tls_config.host);
29
30 handles.push(tokio::spawn(async move {
31 let mut interval = tokio::time::interval(
32 std::time::Duration::from_secs(tls_interval_secs),
33 );
34 interval.tick().await; // consume immediate first tick
35 loop {
36 tokio::select! {
37 _ = cancel.cancelled() => break,
38 _ = interval.tick() => {}
39 }
40 let previous = db::get_latest_tls_check(&pool, &name).await.ok().flatten();
41 let status = tls::check_tls(&name, &tls_config).await;
42 info!("{}: TLS {} — {}d remaining", name, if status.valid { "valid" } else { "invalid" }, status.days_remaining);
43 if let Err(e) = db::insert_tls_check(&pool, &status).await {
44 tracing::error!("{name}: failed to store TLS check: {e}");
45 }
46
47 // Fire alerts on TLS state transitions
48 if let Some(ref alerter) = alerter {
49 let was_ok = previous.as_ref().is_none_or(|p| p.valid && p.error.is_none());
50 let now_warn = status.valid && status.days_remaining <= warn_days as i64;
51 let now_error = !status.valid || status.error.is_some();
52
53 if was_ok && now_error {
54 alerter.send_tls_error_alert(
55 &name,
56 &tls_config.host,
57 status.error.as_deref().unwrap_or("certificate invalid"),
58 ).await;
59 } else if was_ok && now_warn {
60 alerter.send_tls_expiry_alert(
61 &name,
62 &tls_config.host,
63 status.days_remaining,
64 &status.not_after,
65 ).await;
66 } else if let Some(ref prev) = previous {
67 let was_bad = !prev.valid || prev.error.is_some() || prev.days_remaining <= warn_days as i64;
68 let now_ok = status.valid && status.error.is_none() && status.days_remaining > warn_days as i64;
69 if was_bad && now_ok {
70 alerter.send_tls_recovery(
71 &name,
72 &label,
73 status.days_remaining,
74 ).await;
75 }
76 }
77 }
78 }
79 }));
80 }
81 }
82
83 handles
84 }
85