Skip to main content

max / makenotwork

pom: ring + webpki-roots for all outbound TLS, drop aws-lc-rs Deepaudit F1/F2 (reqwest 0.13 fallout): - Trust-anchor split: reqwest health/cors/route/peer/alert probes now trust the bundled webpki (Mozilla) root set via use_preconfigured_tls, the same anchor checks/tls.rs reports against. A host with an extra local root can no longer make the HTTP check and the cert check disagree. New crate::tls holds the shared rustls config + client builder. - Crypto provider: install ring (pure Rust) as the process default instead of aws-lc-rs, and pin rustls/tokio-rustls to default-features=false + ring. aws-lc-sys is now GONE from the tree (cargo tree -i aws-lc-sys: no match), taking RUSTSEC-2026-0044/0048 and the cmake/C build dep with it, and honoring the pure-Rust crypto-provider preference. reqwest is now built rustls-no-provider; crate::tls installs ring via a Once so a client can't be built before a provider exists (covers tests too). 489 tests green, clippy clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-24 20:53 UTC
Commit: d5c85b6ae59d8fbde55aadd35517c13d2d048353
Parent: f83d78d
16 files changed, +83 insertions, -271 deletions
M pom/Cargo.lock +10 -231
@@ -154,28 +154,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
154 154 checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
155 155
156 156 [[package]]
157 - name = "aws-lc-rs"
158 - version = "1.16.1"
159 - source = "registry+https://github.com/rust-lang/crates.io-index"
160 - checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf"
161 - dependencies = [
162 - "aws-lc-sys",
163 - "zeroize",
164 - ]
165 -
166 - [[package]]
167 - name = "aws-lc-sys"
168 - version = "0.38.0"
169 - source = "registry+https://github.com/rust-lang/crates.io-index"
170 - checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e"
171 - dependencies = [
172 - "cc",
173 - "cmake",
174 - "dunce",
175 - "fs_extra",
176 - ]
177 -
178 - [[package]]
179 157 name = "axum"
180 158 version = "0.8.8"
181 159 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -283,8 +261,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
283 261 checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
284 262 dependencies = [
285 263 "find-msvc-tools",
286 - "jobserver",
287 - "libc",
288 264 "shlex",
289 265 ]
290 266
@@ -295,12 +271,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
295 271 checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
296 272
297 273 [[package]]
298 - name = "cfg_aliases"
299 - version = "0.2.1"
300 - source = "registry+https://github.com/rust-lang/crates.io-index"
301 - checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
302 -
303 - [[package]]
304 274 name = "chacha20"
305 275 version = "0.10.1"
306 276 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -308,7 +278,7 @@ checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
308 278 dependencies = [
309 279 "cfg-if",
310 280 "cpufeatures 0.3.0",
311 - "rand_core 0.10.1",
281 + "rand_core",
312 282 ]
313 283
314 284 [[package]]
@@ -366,15 +336,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
366 336 checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
367 337
368 338 [[package]]
369 - name = "cmake"
370 - version = "0.1.57"
371 - source = "registry+https://github.com/rust-lang/crates.io-index"
372 - checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d"
373 - dependencies = [
374 - "cc",
375 - ]
376 -
377 - [[package]]
378 339 name = "cmov"
379 340 version = "0.5.4"
380 341 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -654,12 +615,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
654 615 checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
655 616
656 617 [[package]]
657 - name = "dunce"
658 - version = "1.0.5"
659 - source = "registry+https://github.com/rust-lang/crates.io-index"
660 - checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
661 -
662 - [[package]]
663 618 name = "dyn-clone"
664 619 version = "1.0.20"
665 620 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -750,12 +705,6 @@ dependencies = [
750 705 ]
751 706
752 707 [[package]]
753 - name = "fs_extra"
754 - version = "1.3.0"
755 - source = "registry+https://github.com/rust-lang/crates.io-index"
756 - checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
757 -
758 - [[package]]
759 708 name = "futures"
760 709 version = "0.3.32"
761 710 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -871,24 +820,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
871 820 checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
872 821 dependencies = [
873 822 "cfg-if",
874 - "js-sys",
875 823 "libc",
876 824 "wasi",
877 - "wasm-bindgen",
878 - ]
879 -
880 - [[package]]
881 - name = "getrandom"
882 - version = "0.3.4"
883 - source = "registry+https://github.com/rust-lang/crates.io-index"
884 - checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
885 - dependencies = [
886 - "cfg-if",
887 - "js-sys",
888 - "libc",
889 - "r-efi 5.3.0",
890 - "wasip2",
891 - "wasm-bindgen",
892 825 ]
893 826
894 827 [[package]]
@@ -899,8 +832,8 @@ checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
899 832 dependencies = [
900 833 "cfg-if",
901 834 "libc",
902 - "r-efi 6.0.0",
903 - "rand_core 0.10.1",
835 + "r-efi",
836 + "rand_core",
904 837 "wasip2",
905 838 "wasip3",
906 839 ]
@@ -962,7 +895,7 @@ dependencies = [
962 895 "idna",
963 896 "ipnet",
964 897 "jni",
965 - "rand 0.10.2",
898 + "rand",
966 899 "thiserror",
967 900 "tinyvec",
968 901 "tokio",
@@ -982,7 +915,7 @@ dependencies = [
982 915 "jni",
983 916 "once_cell",
984 917 "prefix-trie",
985 - "rand 0.10.2",
918 + "rand",
986 919 "ring",
987 920 "thiserror",
988 921 "tinyvec",
@@ -1007,7 +940,7 @@ dependencies = [
1007 940 "ndk-context",
1008 941 "once_cell",
1009 942 "parking_lot",
1010 - "rand 0.10.2",
943 + "rand",
1011 944 "resolv-conf",
1012 945 "smallvec",
1013 946 "system-configuration",
@@ -1403,16 +1336,6 @@ dependencies = [
1403 1336 ]
1404 1337
1405 1338 [[package]]
1406 - name = "jobserver"
1407 - version = "0.1.34"
1408 - source = "registry+https://github.com/rust-lang/crates.io-index"
1409 - checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
1410 - dependencies = [
1411 - "getrandom 0.3.4",
1412 - "libc",
1413 - ]
1414 -
1415 - [[package]]
1416 1339 name = "js-sys"
1417 1340 version = "0.3.91"
1418 1341 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1482,12 +1405,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1482 1405 checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
1483 1406
1484 1407 [[package]]
1485 - name = "lru-slab"
1486 - version = "0.1.2"
1487 - source = "registry+https://github.com/rust-lang/crates.io-index"
1488 - checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
1489 -
1490 - [[package]]
1491 1408 name = "matchers"
1492 1409 version = "0.2.0"
1493 1410 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1747,6 +1664,7 @@ dependencies = [
1747 1664 "rcgen",
1748 1665 "reqwest",
1749 1666 "rmcp",
1667 + "rustls",
1750 1668 "rustls-pki-types",
1751 1669 "rustls-platform-verifier",
1752 1670 "schemars",
@@ -1789,15 +1707,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1789 1707 checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
1790 1708
1791 1709 [[package]]
1792 - name = "ppv-lite86"
1793 - version = "0.2.21"
1794 - source = "registry+https://github.com/rust-lang/crates.io-index"
1795 - checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
1796 - dependencies = [
1797 - "zerocopy",
1798 - ]
1799 -
1800 - [[package]]
1801 1710 name = "prefix-trie"
1802 1711 version = "0.8.4"
1803 1712 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1828,62 +1737,6 @@ dependencies = [
1828 1737 ]
1829 1738
1830 1739 [[package]]
1831 - name = "quinn"
1832 - version = "0.11.9"
1833 - source = "registry+https://github.com/rust-lang/crates.io-index"
1834 - checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
1835 - dependencies = [
1836 - "bytes",
1837 - "cfg_aliases",
1838 - "pin-project-lite",
1839 - "quinn-proto",
1840 - "quinn-udp",
1841 - "rustc-hash",
1842 - "rustls",
1843 - "socket2 0.6.3",
1844 - "thiserror",
1845 - "tokio",
1846 - "tracing",
1847 - "web-time",
1848 - ]
1849 -
1850 - [[package]]
1851 - name = "quinn-proto"
1852 - version = "0.11.14"
1853 - source = "registry+https://github.com/rust-lang/crates.io-index"
1854 - checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
1855 - dependencies = [
1856 - "aws-lc-rs",
1857 - "bytes",
1858 - "getrandom 0.3.4",
1859 - "lru-slab",
1860 - "rand 0.9.2",
1861 - "ring",
1862 - "rustc-hash",
1863 - "rustls",
1864 - "rustls-pki-types",
1865 - "slab",
1866 - "thiserror",
1867 - "tinyvec",
1868 - "tracing",
1869 - "web-time",
1870 - ]
1871 -
1872 - [[package]]
1873 - name = "quinn-udp"
1874 - version = "0.5.14"
1875 - source = "registry+https://github.com/rust-lang/crates.io-index"
1876 - checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
1877 - dependencies = [
1878 - "cfg_aliases",
1879 - "libc",
1880 - "once_cell",
1881 - "socket2 0.6.3",
1882 - "tracing",
1883 - "windows-sys 0.52.0",
1884 - ]
1885 -
1886 - [[package]]
1887 1740 name = "quote"
1888 1741 version = "1.0.45"
1889 1742 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1894,54 +1747,19 @@ dependencies = [
1894 1747
1895 1748 [[package]]
1896 1749 name = "r-efi"
1897 - version = "5.3.0"
1898 - source = "registry+https://github.com/rust-lang/crates.io-index"
1899 - checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
1900 -
1901 - [[package]]
1902 - name = "r-efi"
1903 1750 version = "6.0.0"
1904 1751 source = "registry+https://github.com/rust-lang/crates.io-index"
1905 1752 checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
1906 1753
1907 1754 [[package]]
1908 1755 name = "rand"
1909 - version = "0.9.2"
1910 - source = "registry+https://github.com/rust-lang/crates.io-index"
1911 - checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
1912 - dependencies = [
1913 - "rand_chacha",
1914 - "rand_core 0.9.5",
1915 - ]
1916 -
1917 - [[package]]
1918 - name = "rand"
1919 1756 version = "0.10.2"
1920 1757 source = "registry+https://github.com/rust-lang/crates.io-index"
1921 1758 checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
1922 1759 dependencies = [
1923 1760 "chacha20",
1924 1761 "getrandom 0.4.2",
1925 - "rand_core 0.10.1",
1926 - ]
1927 -
1928 - [[package]]
1929 - name = "rand_chacha"
1930 - version = "0.9.0"
1931 - source = "registry+https://github.com/rust-lang/crates.io-index"
1932 - checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
1933 - dependencies = [
1934 - "ppv-lite86",
1935 - "rand_core 0.9.5",
1936 - ]
1937 -
1938 - [[package]]
1939 - name = "rand_core"
1940 - version = "0.9.5"
1941 - source = "registry+https://github.com/rust-lang/crates.io-index"
1942 - checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
1943 - dependencies = [
1944 - "getrandom 0.3.4",
1762 + "rand_core",
1945 1763 ]
1946 1764
1947 1765 [[package]]
@@ -2040,7 +1858,6 @@ dependencies = [
2040 1858 "log",
2041 1859 "percent-encoding",
2042 1860 "pin-project-lite",
2043 - "quinn",
2044 1861 "rustls",
2045 1862 "rustls-pki-types",
2046 1863 "rustls-platform-verifier",
@@ -2114,12 +1931,6 @@ dependencies = [
2114 1931 ]
2115 1932
2116 1933 [[package]]
2117 - name = "rustc-hash"
2118 - version = "2.1.1"
2119 - source = "registry+https://github.com/rust-lang/crates.io-index"
2120 - checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
2121 -
2122 - [[package]]
2123 1934 name = "rustc_version"
2124 1935 version = "0.4.1"
2125 1936 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2143,9 +1954,9 @@ version = "0.23.37"
2143 1954 source = "registry+https://github.com/rust-lang/crates.io-index"
2144 1955 checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4"
2145 1956 dependencies = [
2146 - "aws-lc-rs",
2147 1957 "log",
2148 1958 "once_cell",
1959 + "ring",
2149 1960 "rustls-pki-types",
2150 1961 "rustls-webpki",
2151 1962 "subtle",
@@ -2170,7 +1981,6 @@ version = "1.14.0"
2170 1981 source = "registry+https://github.com/rust-lang/crates.io-index"
2171 1982 checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
2172 1983 dependencies = [
2173 - "web-time",
2174 1984 "zeroize",
2175 1985 ]
2176 1986
@@ -2207,7 +2017,6 @@ version = "0.103.13"
2207 2017 source = "registry+https://github.com/rust-lang/crates.io-index"
2208 2018 checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
2209 2019 dependencies = [
2210 - "aws-lc-rs",
2211 2020 "ring",
2212 2021 "rustls-pki-types",
2213 2022 "untrusted",
@@ -2643,7 +2452,7 @@ dependencies = [
2643 2452 "log",
2644 2453 "md-5",
2645 2454 "memchr",
2646 - "rand 0.10.2",
2455 + "rand",
2647 2456 "serde",
2648 2457 "serde_json",
2649 2458 "sha2 0.11.0",
@@ -3321,16 +3130,6 @@ dependencies = [
3321 3130 ]
3322 3131
3323 3132 [[package]]
3324 - name = "web-time"
3325 - version = "1.1.0"
3326 - source = "registry+https://github.com/rust-lang/crates.io-index"
3327 - checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
3328 - dependencies = [
3329 - "js-sys",
3330 - "wasm-bindgen",
3331 - ]
3332 -
3333 - [[package]]
3334 3133 name = "webpki-root-certs"
3335 3134 version = "1.0.9"
3336 3135 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3737,26 +3536,6 @@ dependencies = [
3737 3536 ]
3738 3537
3739 3538 [[package]]
3740 - name = "zerocopy"
3741 - version = "0.8.42"
3742 - source = "registry+https://github.com/rust-lang/crates.io-index"
3743 - checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3"
3744 - dependencies = [
3745 - "zerocopy-derive",
3746 - ]
3747 -
3748 - [[package]]
3749 - name = "zerocopy-derive"
3750 - version = "0.8.42"
3751 - source = "registry+https://github.com/rust-lang/crates.io-index"
3752 - checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f"
3753 - dependencies = [
3754 - "proc-macro2",
3755 - "quote",
3756 - "syn 2.0.117",
3757 - ]
3758 -
3759 - [[package]]
3760 3539 name = "zerofrom"
3761 3540 version = "0.1.6"
3762 3541 source = "registry+https://github.com/rust-lang/crates.io-index"
M pom/Cargo.toml +9 -4
@@ -22,8 +22,11 @@ clap = { version = "4", features = ["derive"] }
22 22 # Async runtime
23 23 tokio = { version = "1", features = ["rt-multi-thread", "macros", "io-std", "io-util", "process", "signal"] }
24 24
25 - # HTTP client
26 - reqwest = { version = "0.13", default-features = false, features = ["json", "rustls"] }
25 + # HTTP client. `rustls-no-provider` keeps aws-lc-rs out of the tree; the process
26 + # installs the ring provider (main.rs). Each client is handed a webpki-roots
27 + # rustls config via use_preconfigured_tls (see crate::tls), so every outbound
28 + # probe trusts the same Mozilla root set the TLS-expiry check uses.
29 + reqwest = { version = "0.13", default-features = false, features = ["json", "rustls-no-provider"] }
27 30
28 31 # HTTP server (API in serve mode)
29 32 axum = { version = "0.8", default-features = false, features = ["json", "tokio", "http1", "query"] }
@@ -61,9 +64,11 @@ hostname = "0.4"
61 64 # DNS resolution
62 65 hickory-resolver = "0.26"
63 66
64 - # TLS certificate checking
67 + # TLS certificate checking. Pure-Rust ring provider only, no aws-lc-rs (keeps the
68 + # C/asm build dep and the aws-lc-sys advisory surface off every native build host).
65 69 x509-parser = "0.18"
66 - tokio-rustls = "0.26"
70 + rustls = { version = "0.23", default-features = false, features = ["std", "tls12", "logging", "ring"] }
71 + tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "tls12", "ring"] }
67 72 rustls-pki-types = "1"
68 73 webpki-roots = "1"
69 74
@@ -163,7 +163,7 @@ impl Alerter {
163 163 // reqwest 0.13's builder can fail (platform trust-store load) and
164 164 // `Client::new()`/`unwrap_or_default()` panic on that same failure. Return
165 165 // the error so the caller can disable alerting rather than crash at startup.
166 - let client = reqwest::Client::builder()
166 + let client = crate::tls::https_client_builder()
167 167 .timeout(std::time::Duration::from_secs(10))
168 168 .build()?;
169 169 let wam_url = config.wam_url.clone();
@@ -14,7 +14,7 @@ pub async fn check_cors(target: &str, checks: &[CorsCheck]) -> Vec<CorsCheckResu
14 14 // monitoring for the target with no alert that the check died (fuzz-2026-07-06
15 15 // HIGH). `Client::new()` panics on the same failure, so surface it as a failed
16 16 // result per check instead.
17 - let client = match reqwest::Client::builder()
17 + let client = match crate::tls::https_client_builder()
18 18 .timeout(std::time::Duration::from_secs(10))
19 19 .redirect(reqwest::redirect::Policy::none())
20 20 .build()
@@ -21,7 +21,7 @@ pub async fn check_health(
21 21 // reqwest 0.13's builder can genuinely fail (platform trust-store load), and
22 22 // `Client::new()` panics on that same failure, so return an Unreachable result
23 23 // instead of a fallback client that discards the timeout or panics.
24 - let client = match reqwest::Client::builder()
24 + let client = match crate::tls::https_client_builder()
25 25 .timeout(std::time::Duration::from_secs(config.timeout_secs))
26 26 .build()
27 27 {
@@ -131,7 +131,7 @@ mod tests {
131 131
132 132 #[tokio::test]
133 133 async fn check_routes_handles_unreachable() {
134 - let client = reqwest::Client::builder()
134 + let client = crate::tls::https_client_builder()
135 135 .timeout(Duration::from_millis(100))
136 136 .build()
137 137 .unwrap();
@@ -154,7 +154,7 @@ mod tests {
154 154
155 155 #[tokio::test]
156 156 async fn check_routes_empty_paths_returns_empty() {
157 - let client = reqwest::Client::builder().build().unwrap();
157 + let client = crate::tls::https_client_builder().build().unwrap();
158 158 let results = check_routes(
159 159 &client,
160 160 "test",
@@ -56,7 +56,7 @@ pub async fn check_scan_pipeline(
56 56 base_url.trim_end_matches('/')
57 57 );
58 58
59 - let client = match reqwest::Client::builder()
59 + let client = match crate::tls::https_client_builder()
60 60 .timeout(Duration::from_secs(timeout_secs))
61 61 .build()
62 62 {
@@ -30,25 +30,21 @@ enum TrustStore {
30 30 Platform,
31 31 }
32 32
33 - /// The crypto provider backing both probes. `main` installs aws-lc-rs as the
34 - /// process default; fall back to constructing it directly so the checks still
35 - /// work if that install is ever removed or reordered.
33 + /// The crypto provider backing both probes. `main` installs ring as the process
34 + /// default; fall back to constructing it directly so the checks still work if
35 + /// that install is ever removed or reordered.
36 36 fn crypto_provider() -> Arc<rustls::crypto::CryptoProvider> {
37 37 rustls::crypto::CryptoProvider::get_default()
38 38 .cloned()
39 - .unwrap_or_else(|| Arc::new(rustls::crypto::aws_lc_rs::default_provider()))
39 + .unwrap_or_else(|| Arc::new(rustls::crypto::ring::default_provider()))
40 40 }
41 41
42 42 /// Build a client config pinned to one trust store.
43 43 fn client_config(store: TrustStore) -> Result<rustls::ClientConfig, String> {
44 44 match store {
45 - TrustStore::Webpki => {
46 - let mut root_store = rustls::RootCertStore::empty();
47 - root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
48 - Ok(rustls::ClientConfig::builder()
49 - .with_root_certificates(root_store)
50 - .with_no_client_auth())
51 - }
45 + // Shared with every outbound HTTP probe (see crate::tls) so the HTTP
46 + // reachability verdict and the cert-expiry verdict use one root set.
47 + TrustStore::Webpki => Ok(crate::tls::webpki_client_config()),
52 48 TrustStore::Platform => {
53 49 let verifier = rustls_platform_verifier::Verifier::new(crypto_provider())
54 50 .map_err(|e| format!("platform verifier unavailable: {e}"))?;
@@ -236,7 +236,7 @@ pub(crate) async fn cmd_mesh(config: &Config, json: bool) -> Result<()> {
236 236 let listen = &config.serve.listen;
237 237 let url = format!("http://{listen}/api/mesh");
238 238
239 - let client = reqwest::Client::builder()
239 + let client = pom::tls::https_client_builder()
240 240 .timeout(std::time::Duration::from_secs(5))
241 241 .build()?;
242 242
@@ -51,7 +51,7 @@ pub(crate) fn spawn_route_tasks(
51 51 // reqwest 0.13's builder can fail and `unwrap_or_default()` panics on
52 52 // the same failure, silently killing this spawned route-check task. Log
53 53 // and exit visibly instead.
54 - let client = match reqwest::Client::builder()
54 + let client = match pom::tls::https_client_builder()
55 55 .redirect(reqwest::redirect::Policy::none())
56 56 .build()
57 57 {
@@ -18,5 +18,6 @@ pub mod display;
18 18 pub mod error;
19 19 pub mod peer;
20 20 pub mod status;
21 + pub mod tls;
21 22 pub mod tools;
22 23 pub mod types;
M pom/src/main.rs +7 -13
@@ -85,19 +85,13 @@ enum Commands {
85 85
86 86 #[tokio::main]
87 87 async fn main() -> Result<()> {
88 - // Install the default rustls crypto provider before any TLS operations. This
89 - // provider verifies signatures for every certificate pom validates (reqwest 0.13
90 - // reads the process default via CryptoProvider::get_default).
91 - //
92 - // reqwest 0.13's `rustls` feature pulls aws-lc-rs and is the only provider
93 - // feature enabled (`ring` enters only as an rcgen dev-dependency), so this
94 - // install_default() is belt-and-braces rather than load-bearing for
95 - // auto-selection. WATCHED PIN: aws-lc-sys 0.38.0 carries RUSTSEC-2026-0044
96 - // (Name Constraints bypass) and RUSTSEC-2026-0048 (CRL scope check), both X.509
97 - // validation defects on this path. The upgrade is upstream-blocked and reqwest
98 - // pulls aws-lc-rs regardless, so reverting the provider alone would leave two
99 - // providers reachable. Re-check when reqwest/rustls-webpki advance the pin.
100 - let _ = tokio_rustls::rustls::crypto::aws_lc_rs::default_provider().install_default();
88 + // Install the pure-Rust ring crypto provider before any TLS operation. It
89 + // verifies signatures for every certificate pom validates: reqwest (built
90 + // rustls-no-provider) and the TLS-expiry check both read the process default
91 + // via CryptoProvider::get_default. aws-lc-rs is kept out of the dependency
92 + // tree entirely (see Cargo.toml), so its C/asm build dep and the aws-lc-sys
93 + // advisory surface never reach a build host or the cert-verification path.
94 + pom::tls::install_crypto_provider();
101 95
102 96 let cli = Cli::parse();
103 97
M pom/src/peer.rs +1 -1
@@ -236,7 +236,7 @@ async fn heartbeat_loop(
236 236 };
237 237 let base = peer_base_url(&address);
238 238
239 - let client = match reqwest::Client::builder()
239 + let client = match crate::tls::https_client_builder()
240 240 .timeout(std::time::Duration::from_secs(10))
241 241 .build()
242 242 {
@@ -0,0 +1,37 @@
1 + //! Shared TLS configuration for pom's outbound HTTP.
2 + //!
3 + //! Every probe (health, CORS, routes, peer, alerts) trusts the bundled Mozilla
4 + //! root set via `webpki-roots`, the same anchor the TLS-expiry check reports
5 + //! against, so a host with an extra local root cannot make the HTTP check and
6 + //! the certificate check disagree about one target. The rustls provider is ring
7 + //! (pure Rust), installed process-wide by [`install_crypto_provider`] at startup.
8 +
9 + /// Install ring as the process-wide rustls crypto provider. Idempotent (guarded
10 + /// by a `Once`); safe to call from `main` at startup and from every config
11 + /// builder, so a TLS client can never be built before a provider is installed.
12 + pub fn install_crypto_provider() {
13 + static INSTALLED: std::sync::Once = std::sync::Once::new();
14 + INSTALLED.call_once(|| {
15 + let _ = rustls::crypto::ring::default_provider().install_default();
16 + });
17 + }
18 +
19 + /// A rustls client config pinned to the bundled webpki (Mozilla) roots.
20 + pub fn webpki_client_config() -> rustls::ClientConfig {
21 + // `ClientConfig::builder()` reads the process-default provider, so make sure
22 + // ring is installed even if this runs before `main`'s explicit install (tests).
23 + install_crypto_provider();
24 + let mut roots = rustls::RootCertStore::empty();
25 + roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
26 + rustls::ClientConfig::builder()
27 + .with_root_certificates(roots)
28 + .with_no_client_auth()
29 + }
30 +
31 + /// A `reqwest` client builder that trusts the webpki root set. Callers add their
32 + /// own timeout / redirect policy, then `.build()`. reqwest is compiled
33 + /// `rustls-no-provider`, so without this preconfigured config it would fall back
34 + /// to the OS platform verifier, a different trust model than the cert check uses.
35 + pub fn https_client_builder() -> reqwest::ClientBuilder {
36 + reqwest::Client::builder().use_preconfigured_tls(webpki_client_config())
37 + }
@@ -218,7 +218,7 @@ impl PomServer {
218 218 let listen = &self.config.serve.listen;
219 219 let url = format!("http://{listen}/api/mesh");
220 220
221 - let client = reqwest::Client::builder()
221 + let client = crate::tls::https_client_builder()
222 222 .timeout(std::time::Duration::from_secs(5))
223 223 .build()?;
224 224
@@ -2342,8 +2342,8 @@ async fn check_tls_with_test_cert() {
2342 2342 use rcgen::generate_simple_self_signed;
2343 2343 use tokio_rustls::rustls;
2344 2344
2345 - // Install crypto provider (tests don't go through main())
2346 - let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
2345 + // Install crypto provider (tests don't go through main()).
2346 + pom::tls::install_crypto_provider();
2347 2347
2348 2348 // Generate a self-signed cert
2349 2349 let subject_alt_names = vec!["localhost".to_string()];