//! Whether this host has usable outbound TLS trust anchors. //! //! mt ships none of its own. `reqwest` verifies through //! `rustls-platform-verifier`, which on Linux loads the host store with //! `rustls_native_certs::load_native_certs`, and the AWS client used by //! `s3-storage` calls the same crate directly. Neither offers bundled roots, so //! a thin or unreadable store takes out all three outbound paths at once: the //! OAuth token exchange that logs users in, link previews, and S3 media. //! //! Left alone, that failure is invisible until someone tries to log in, on a box //! that boots fine and passes a database-only health check. Probing the same call //! the verifier will make turns it into a boot-time log line and an //! `/api/health` field instead. The precondition itself is documented in //! `deploy/README.md`. use std::sync::OnceLock; static ANCHORS_OK: OnceLock = OnceLock::new(); /// Whether the host trust store yielded any usable anchors. /// /// Probed once per process and cached, so `main` can force the boot-time log and /// the health handler can read the answer on every poll without reloading the /// store. Called from the health handler, which is why it is not `main`-only. pub fn anchors_ok() -> bool { *ANCHORS_OK.get_or_init(probe) } /// Load the host store and judge it the way the verifier does. /// /// Emptiness is the only failure. A store that yields some anchors and some /// parse errors is what the platform verifier itself accepts (it logs the /// ignored certificates and builds anyway), so treating partial success as a /// failure here would report a problem mt does not have. fn probe() -> bool { let result = rustls_native_certs::load_native_certs(); for error in &result.errors { tracing::warn!("trust store read error: {error}"); } if result.certs.is_empty() { tracing::error!( "no CA certificates loaded from the host trust store. Every outbound \ TLS request will fail, including the OAuth token exchange that logs \ users in. Install or repair ca-certificates on this host; see \ deploy/README.md." ); false } else { tracing::info!( anchors = result.certs.len(), "loaded CA certificates from the host trust store" ); true } } #[cfg(test)] mod tests { use super::anchors_ok; /// Any developer or CI machine has a trust store, so this asserts the probe /// reads one rather than asserting a hardcoded answer. It would fail on a /// host that cannot make an outbound HTTPS request at all, which is the /// condition worth failing on. #[test] fn probe_finds_anchors_on_this_host() { assert!(anchors_ok(), "no CA anchors loaded from the host store"); } /// The answer is cached, so repeated reads must agree and must not reload. #[test] fn repeated_reads_agree() { assert_eq!(anchors_ok(), anchors_ok()); } }