diff options
4 files changed, 749 insertions, 909 deletions
diff --git a/pkgs/by-name/st/stalwart-mail-patched/mail-send.nix b/pkgs/by-name/st/stalwart-mail-patched/mail-send.nix deleted file mode 100644 index e0d8c57..0000000 --- a/pkgs/by-name/st/stalwart-mail-patched/mail-send.nix +++ /dev/null @@ -1,20 +0,0 @@ -{ - stdenv, - fetchFromGitHub, -}: -stdenv.mkDerivation (finalAttrs: { - pname = "mail-send"; - version = "0.5.0"; - - src = fetchFromGitHub { - owner = "stalwartlabs"; - repo = "mail-send"; - tag = "v${finalAttrs.version}"; - hash = "sha256-uDD4GLwjRpNqjtXPMask0twGW2Gcm1PFyDGXcfPS0F4="; - }; - - installPhase = '' - mkdir --parents "$out" - cp --recursive ./. "$out/" - ''; -}) diff --git a/pkgs/by-name/st/stalwart-mail-patched/package.nix b/pkgs/by-name/st/stalwart-mail-patched/package.nix index 5b1c409..9058f2c 100644 --- a/pkgs/by-name/st/stalwart-mail-patched/package.nix +++ b/pkgs/by-name/st/stalwart-mail-patched/package.nix @@ -4,8 +4,6 @@ nixLib, }: let spamfilter = callPackage ./spam-filter.nix {}; - - mail-send = callPackage ./mail-send.nix {}; in stalwart-mail.overrideAttrs (final: prev: { pname = "stalwart-mail-patched"; @@ -28,21 +26,15 @@ in # "elastic" # "s3" "redis" + "tls-native-roots" ]; - postUnpack = - (prev.postUnpack or "") - + '' - cp --recursive "${mail-send}" ./source/crates/mail-send - chmod --recursive +w "./source/crates/mail-send" - ''; - cargoPatches = (prev.cargoPatches or []) ++ [ # `stalwart-mail` uses their bundled store, which makes it impossible to use our # own CA certificate (e.g., for tests). Thus use a native version. - ./patches/crates-Use-the-platform-CA-bundle-instead-of-the-bun.patch + ./patches/use-platform-ca-roots.patch ]; # Check that the enterprise feature is really disabled. diff --git a/pkgs/by-name/st/stalwart-mail-patched/patches/crates-Use-the-platform-CA-bundle-instead-of-the-bun.patch b/pkgs/by-name/st/stalwart-mail-patched/patches/crates-Use-the-platform-CA-bundle-instead-of-the-bun.patch deleted file mode 100644 index e6c3d4b..0000000 --- a/pkgs/by-name/st/stalwart-mail-patched/patches/crates-Use-the-platform-CA-bundle-instead-of-the-bun.patch +++ /dev/null @@ -1,879 +0,0 @@ -From 6825a35213d604a7149265af2346a69143c0853b Mon Sep 17 00:00:00 2001 -From: Benedikt Peetz <benedikt.peetz@b-peetz.de> -Date: Tue, 4 Mar 2025 19:15:06 +0100 -Subject: [PATCH] crates/*: Use the platform CA bundle instead of the - bundled certificates - ---- - Cargo.lock | 284 ++++++++++++++++++++++++++++++- - crates/cli/Cargo.toml | 2 +- - crates/common/Cargo.toml | 4 +- - crates/directory/Cargo.toml | 4 +- - crates/imap/Cargo.toml | 2 +- - crates/jmap/Cargo.toml | 4 +- - crates/mail-send/Cargo.toml | 1 + - crates/mail-send/src/smtp/tls.rs | 22 +-- - crates/managesieve/Cargo.toml | 2 +- - crates/pop3/Cargo.toml | 2 +- - crates/smtp/Cargo.toml | 4 +- - crates/spam-filter/Cargo.toml | 4 +- - crates/store/Cargo.toml | 2 +- - crates/trc/Cargo.toml | 2 +- - crates/utils/Cargo.toml | 5 +- - crates/utils/src/lib.rs | 16 +- - tests/Cargo.toml | 10 +- - 17 files changed, 314 insertions(+), 56 deletions(-) - -diff --git a/Cargo.lock b/Cargo.lock -index be36759b..eca9699f 100644 ---- a/Cargo.lock -+++ b/Cargo.lock -@@ -440,6 +440,47 @@ dependencies = [ - "url", - ] - -+[[package]] -+name = "aws-lc-fips-sys" -+version = "0.13.3" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "29003a681b2b9465c1139bfb726da452a841a8b025f35953f3bce71139f10b21" -+dependencies = [ -+ "bindgen 0.69.5", -+ "cc", -+ "cmake", -+ "dunce", -+ "fs_extra", -+ "paste", -+ "regex", -+] -+ -+[[package]] -+name = "aws-lc-rs" -+version = "1.12.5" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "5e4e8200b9a4a5801a769d50eeabc05670fec7e959a8cb7a63a93e4e519942ae" -+dependencies = [ -+ "aws-lc-fips-sys", -+ "aws-lc-sys", -+ "paste", -+ "zeroize", -+] -+ -+[[package]] -+name = "aws-lc-sys" -+version = "0.26.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "0f9dd2e03ee80ca2822dd6ea431163d2ef259f2066a4d6ccaca6d9dcb386aa43" -+dependencies = [ -+ "bindgen 0.69.5", -+ "cc", -+ "cmake", -+ "dunce", -+ "fs_extra", -+ "paste", -+] -+ - [[package]] - name = "aws-region" - version = "0.25.5" -@@ -673,12 +714,15 @@ dependencies = [ - "itertools 0.12.1", - "lazy_static", - "lazycell", -+ "log", -+ "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex", - "syn 2.0.96", -+ "which", - ] - - [[package]] -@@ -1035,6 +1079,12 @@ dependencies = [ - "smallvec", - ] - -+[[package]] -+name = "cesu8" -+version = "1.1.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" -+ - [[package]] - name = "cexpr" - version = "0.6.0" -@@ -1347,6 +1397,16 @@ dependencies = [ - "libc", - ] - -+[[package]] -+name = "core-foundation" -+version = "0.10.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" -+dependencies = [ -+ "core-foundation-sys", -+ "libc", -+] -+ - [[package]] - name = "core-foundation-sys" - version = "0.8.7" -@@ -1912,6 +1972,12 @@ dependencies = [ - "zeroize", - ] - -+[[package]] -+name = "dunce" -+version = "1.0.5" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" -+ - [[package]] - name = "dyn-clone" - version = "1.0.17" -@@ -2117,6 +2183,29 @@ dependencies = [ - "syn 2.0.96", - ] - -+[[package]] -+name = "env_filter" -+version = "0.1.3" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" -+dependencies = [ -+ "log", -+ "regex", -+] -+ -+[[package]] -+name = "env_logger" -+version = "0.11.6" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" -+dependencies = [ -+ "anstream", -+ "anstyle", -+ "env_filter", -+ "humantime", -+ "log", -+] -+ - [[package]] - name = "equivalent" - version = "1.0.1" -@@ -2423,6 +2512,12 @@ dependencies = [ - "syn 2.0.96", - ] - -+[[package]] -+name = "fs_extra" -+version = "1.3.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" -+ - [[package]] - name = "funty" - version = "2.0.0" -@@ -2974,6 +3069,12 @@ version = "0.4.3" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "9994b79e8c1a39b3166c63ae7823bb2b00831e2a96a31399c50fe69df408eaeb" - -+[[package]] -+name = "humantime" -+version = "2.1.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" -+ - [[package]] - name = "hyper" - version = "0.14.32" -@@ -3044,6 +3145,7 @@ dependencies = [ - "hyper 1.6.0", - "hyper-util", - "rustls 0.23.21", -+ "rustls-native-certs 0.8.1", - "rustls-pki-types", - "tokio", - "tokio-rustls 0.26.1", -@@ -3607,6 +3709,28 @@ dependencies = [ - "utils", - ] - -+[[package]] -+name = "jni" -+version = "0.21.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -+dependencies = [ -+ "cesu8", -+ "cfg-if", -+ "combine", -+ "jni-sys", -+ "log", -+ "thiserror 1.0.69", -+ "walkdir", -+ "windows-sys 0.45.0", -+] -+ -+[[package]] -+name = "jni-sys" -+version = "0.3.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" -+ - [[package]] - name = "jobserver" - version = "0.1.32" -@@ -3959,14 +4083,18 @@ dependencies = [ - [[package]] - name = "mail-send" - version = "0.5.0" --source = "registry+https://github.com/rust-lang/crates.io-index" --checksum = "b12277cdcacfc15af67fe9cf155f31ff68ad8c301304573ea116ed8870f192d5" - dependencies = [ - "base64 0.22.1", -+ "env_logger", - "gethostname", -+ "mail-auth", -+ "mail-builder", -+ "mail-parser", - "md5", -+ "rand 0.8.5", - "rustls 0.23.21", - "rustls-pki-types", -+ "rustls-platform-verifier", - "smtp-proto", - "tokio", - "tokio-rustls 0.26.1", -@@ -5552,6 +5680,7 @@ dependencies = [ - "pin-project-lite", - "quinn", - "rustls 0.23.21", -+ "rustls-native-certs 0.8.1", - "rustls-pemfile 2.2.0", - "rustls-pki-types", - "serde", -@@ -5920,6 +6049,8 @@ version = "0.23.21" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" - dependencies = [ -+ "aws-lc-rs", -+ "log", - "once_cell", - "ring 0.17.8", - "rustls-pki-types", -@@ -5937,7 +6068,7 @@ dependencies = [ - "openssl-probe", - "rustls-pemfile 1.0.4", - "schannel", -- "security-framework", -+ "security-framework 2.11.1", - ] - - [[package]] -@@ -5950,7 +6081,19 @@ dependencies = [ - "rustls-pemfile 2.2.0", - "rustls-pki-types", - "schannel", -- "security-framework", -+ "security-framework 2.11.1", -+] -+ -+[[package]] -+name = "rustls-native-certs" -+version = "0.8.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" -+dependencies = [ -+ "openssl-probe", -+ "rustls-pki-types", -+ "schannel", -+ "security-framework 3.2.0", - ] - - [[package]] -@@ -5980,6 +6123,33 @@ dependencies = [ - "web-time", - ] - -+[[package]] -+name = "rustls-platform-verifier" -+version = "0.5.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "e012c45844a1790332c9386ed4ca3a06def221092eda277e6f079728f8ea99da" -+dependencies = [ -+ "core-foundation 0.10.0", -+ "core-foundation-sys", -+ "jni", -+ "log", -+ "once_cell", -+ "rustls 0.23.21", -+ "rustls-native-certs 0.8.1", -+ "rustls-platform-verifier-android", -+ "rustls-webpki 0.102.8", -+ "security-framework 3.2.0", -+ "security-framework-sys", -+ "webpki-root-certs", -+ "windows-sys 0.52.0", -+] -+ -+[[package]] -+name = "rustls-platform-verifier-android" -+version = "0.1.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" -+ - [[package]] - name = "rustls-webpki" - version = "0.101.7" -@@ -5996,6 +6166,7 @@ version = "0.102.8" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" - dependencies = [ -+ "aws-lc-rs", - "ring 0.17.8", - "rustls-pki-types", - "untrusted 0.9.0", -@@ -6125,7 +6296,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" - dependencies = [ - "bitflags 2.8.0", -- "core-foundation", -+ "core-foundation 0.9.4", -+ "core-foundation-sys", -+ "libc", -+ "security-framework-sys", -+] -+ -+[[package]] -+name = "security-framework" -+version = "3.2.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" -+dependencies = [ -+ "bitflags 2.8.0", -+ "core-foundation 0.10.0", - "core-foundation-sys", - "libc", - "security-framework-sys", -@@ -6817,7 +7001,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" - dependencies = [ - "bitflags 1.3.2", -- "core-foundation", -+ "core-foundation 0.9.4", - "system-configuration-sys", - ] - -@@ -7569,6 +7753,7 @@ dependencies = [ - "rustls 0.23.21", - "rustls-pemfile 2.2.0", - "rustls-pki-types", -+ "rustls-platform-verifier", - "serde", - "serde_json", - "smtp-proto", -@@ -7764,6 +7949,15 @@ dependencies = [ - "untrusted 0.9.0", - ] - -+[[package]] -+name = "webpki-root-certs" -+version = "0.26.8" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "09aed61f5e8d2c18344b3faa33a4c837855fe56642757754775548fee21386c4" -+dependencies = [ -+ "rustls-pki-types", -+] -+ - [[package]] - name = "webpki-roots" - version = "0.25.4" -@@ -7789,6 +7983,18 @@ dependencies = [ - "once_cell", - ] - -+[[package]] -+name = "which" -+version = "4.4.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -+dependencies = [ -+ "either", -+ "home", -+ "once_cell", -+ "rustix", -+] -+ - [[package]] - name = "whoami" - version = "1.5.2" -@@ -7886,6 +8092,15 @@ dependencies = [ - "windows-targets 0.52.6", - ] - -+[[package]] -+name = "windows-sys" -+version = "0.45.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -+dependencies = [ -+ "windows-targets 0.42.2", -+] -+ - [[package]] - name = "windows-sys" - version = "0.48.0" -@@ -7913,6 +8128,21 @@ dependencies = [ - "windows-targets 0.52.6", - ] - -+[[package]] -+name = "windows-targets" -+version = "0.42.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -+dependencies = [ -+ "windows_aarch64_gnullvm 0.42.2", -+ "windows_aarch64_msvc 0.42.2", -+ "windows_i686_gnu 0.42.2", -+ "windows_i686_msvc 0.42.2", -+ "windows_x86_64_gnu 0.42.2", -+ "windows_x86_64_gnullvm 0.42.2", -+ "windows_x86_64_msvc 0.42.2", -+] -+ - [[package]] - name = "windows-targets" - version = "0.48.5" -@@ -7944,6 +8174,12 @@ dependencies = [ - "windows_x86_64_msvc 0.52.6", - ] - -+[[package]] -+name = "windows_aarch64_gnullvm" -+version = "0.42.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" -+ - [[package]] - name = "windows_aarch64_gnullvm" - version = "0.48.5" -@@ -7956,6 +8192,12 @@ version = "0.52.6" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -+[[package]] -+name = "windows_aarch64_msvc" -+version = "0.42.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" -+ - [[package]] - name = "windows_aarch64_msvc" - version = "0.48.5" -@@ -7968,6 +8210,12 @@ version = "0.52.6" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -+[[package]] -+name = "windows_i686_gnu" -+version = "0.42.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" -+ - [[package]] - name = "windows_i686_gnu" - version = "0.48.5" -@@ -7986,6 +8234,12 @@ version = "0.52.6" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -+[[package]] -+name = "windows_i686_msvc" -+version = "0.42.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" -+ - [[package]] - name = "windows_i686_msvc" - version = "0.48.5" -@@ -7998,6 +8252,12 @@ version = "0.52.6" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -+[[package]] -+name = "windows_x86_64_gnu" -+version = "0.42.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" -+ - [[package]] - name = "windows_x86_64_gnu" - version = "0.48.5" -@@ -8010,6 +8270,12 @@ version = "0.52.6" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -+[[package]] -+name = "windows_x86_64_gnullvm" -+version = "0.42.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" -+ - [[package]] - name = "windows_x86_64_gnullvm" - version = "0.48.5" -@@ -8022,6 +8288,12 @@ version = "0.52.6" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -+[[package]] -+name = "windows_x86_64_msvc" -+version = "0.42.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" -+ - [[package]] - name = "windows_x86_64_msvc" - version = "0.48.5" -diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml -index a2d19a06..76866b80 100644 ---- a/crates/cli/Cargo.toml -+++ b/crates/cli/Cargo.toml -@@ -13,7 +13,7 @@ resolver = "2" - [dependencies] - jmap-client = { version = "0.3", features = ["async"] } - mail-parser = { version = "0.10", features = ["full_encoding", "serde_support"] } --reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"]} -+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "http2"]} - tokio = { version = "1.23", features = ["full"] } - num_cpus = "1.13.1" - clap = { version = "4.1.6", features = ["derive"] } -diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml -index 3da0183f..93c49bb5 100644 ---- a/crates/common/Cargo.toml -+++ b/crates/common/Cargo.toml -@@ -16,7 +16,7 @@ sieve-rs = { version = "0.6" } - mail-parser = { version = "0.10", features = ["full_encoding"] } - mail-builder = { version = "0.4" } - mail-auth = { version = "0.6" } --mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] } -+mail-send = { path = "../mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] } - smtp-proto = { version = "0.1", features = ["serde_support"] } - dns-update = { version = "0.1" } - ahash = { version = "0.8.2", features = ["serde"] } -@@ -32,7 +32,7 @@ tokio = { version = "1.23", features = ["net", "macros"] } - tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12"] } - futures = "0.3" - rcgen = "0.12" --reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2", "stream"]} -+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "http2", "stream"]} - serde = { version = "1.0", features = ["derive"]} - serde_json = "1.0" - base64 = "0.22" -diff --git a/crates/directory/Cargo.toml b/crates/directory/Cargo.toml -index dc022e7a..10e0c00a 100644 ---- a/crates/directory/Cargo.toml -+++ b/crates/directory/Cargo.toml -@@ -12,7 +12,7 @@ trc = { path = "../trc" } - jmap_proto = { path = "../jmap-proto" } - smtp-proto = { version = "0.1" } - mail-parser = { version = "0.10", features = ["full_encoding", "serde_support"] } --mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] } -+mail-send = { path = "../mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] } - mail-builder = { version = "0.4" } - tokio = { version = "1.23", features = ["net"] } - tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12"] } -@@ -34,7 +34,7 @@ futures = "0.3" - regex = "1.7.0" - serde = { version = "1.0", features = ["derive"]} - totp-rs = { version = "5.5.1", features = ["otpauth"] } --reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"] } -+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "http2"] } - serde_json = "1.0" - base64 = "0.22" - -diff --git a/crates/imap/Cargo.toml b/crates/imap/Cargo.toml -index 640ca4fd..d91931c1 100644 ---- a/crates/imap/Cargo.toml -+++ b/crates/imap/Cargo.toml -@@ -16,7 +16,7 @@ email = { path = "../email" } - nlp = { path = "../nlp" } - utils = { path = "../utils" } - mail-parser = { version = "0.10", features = ["full_encoding"] } --mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] } -+mail-send = { path = "../mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] } - rustls = { version = "0.23.5", default-features = false, features = ["std", "ring", "tls12"] } - rustls-pemfile = "2.0" - tokio = { version = "1.23", features = ["full"] } -diff --git a/crates/jmap/Cargo.toml b/crates/jmap/Cargo.toml -index 7be56e44..ad5ed795 100644 ---- a/crates/jmap/Cargo.toml -+++ b/crates/jmap/Cargo.toml -@@ -18,7 +18,7 @@ email = { path = "../email" } - smtp-proto = { version = "0.1" } - mail-parser = { version = "0.10", features = ["full_encoding", "serde_support"] } - mail-builder = { version = "0.4" } --mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] } -+mail-send = { path = "../mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] } - mail-auth = { version = "0.6", features = ["generate"] } - sieve-rs = { version = "0.6" } - serde = { version = "1.0", features = ["derive"]} -@@ -38,7 +38,7 @@ p256 = { version = "0.13", features = ["ecdh"] } - hkdf = "0.12.3" - sha1 = "0.10" - sha2 = "0.10" --reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"]} -+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "http2"]} - tokio-tungstenite = "0.26" - tungstenite = "0.26" - chrono = "0.4" -diff --git a/crates/mail-send/Cargo.toml b/crates/mail-send/Cargo.toml -index fb5f402d..6760afab 100644 ---- a/crates/mail-send/Cargo.toml -+++ b/crates/mail-send/Cargo.toml -@@ -27,6 +27,7 @@ rustls = { version = "0.23", default-features = false, features = ["std"]} - tokio-rustls = { version = "0.26", default-features = false } - webpki-roots = { version = "0.26"} - rustls-pki-types = { version = "1" } -+rustls-platform-verifier = "0.5" - gethostname = { version = "0.5"} - - [dev-dependencies] -diff --git a/crates/mail-send/src/smtp/tls.rs b/crates/mail-send/src/smtp/tls.rs -index b15a6db8..7ddd0798 100644 ---- a/crates/mail-send/src/smtp/tls.rs -+++ b/crates/mail-send/src/smtp/tls.rs -@@ -12,9 +12,9 @@ use std::{convert::TryFrom, io, sync::Arc}; - - use rustls::{ - client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}, -- ClientConfig, ClientConnection, RootCertStore, SignatureScheme, -+ ClientConfig, ClientConnection, SignatureScheme, - }; --use rustls_pki_types::{ServerName, TrustAnchor}; -+use rustls_pki_types::ServerName; - use tokio::net::TcpStream; - use tokio_rustls::{client::TlsStream, TlsConnector}; - -@@ -78,20 +78,14 @@ impl SmtpClient<TlsStream<TcpStream>> { - } - - pub fn build_tls_connector(allow_invalid_certs: bool) -> TlsConnector { -+ use rustls_platform_verifier::BuilderVerifierExt; -+ -+ let config = ClientConfig::builder(); -+ - let config = if !allow_invalid_certs { -- let mut root_cert_store = RootCertStore::empty(); -- -- root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| TrustAnchor { -- subject: ta.subject.clone(), -- subject_public_key_info: ta.subject_public_key_info.clone(), -- name_constraints: ta.name_constraints.clone(), -- })); -- -- ClientConfig::builder() -- .with_root_certificates(root_cert_store) -- .with_no_client_auth() -+ config.with_platform_verifier().with_no_client_auth() - } else { -- ClientConfig::builder() -+ config - .dangerous() - .with_custom_certificate_verifier(Arc::new(DummyVerifier {})) - .with_no_client_auth() -diff --git a/crates/managesieve/Cargo.toml b/crates/managesieve/Cargo.toml -index 650ab23b..42738e68 100644 ---- a/crates/managesieve/Cargo.toml -+++ b/crates/managesieve/Cargo.toml -@@ -15,7 +15,7 @@ store = { path = "../store" } - utils = { path = "../utils" } - trc = { path = "../trc" } - mail-parser = { version = "0.10", features = ["full_encoding"] } --mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] } -+mail-send = { path = "../mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] } - sieve-rs = { version = "0.6" } - rustls = { version = "0.23.5", default-features = false, features = ["std", "ring", "tls12"] } - rustls-pemfile = "2.0" -diff --git a/crates/pop3/Cargo.toml b/crates/pop3/Cargo.toml -index 5f86ed00..89e7b732 100644 ---- a/crates/pop3/Cargo.toml -+++ b/crates/pop3/Cargo.toml -@@ -15,7 +15,7 @@ trc = { path = "../trc" } - jmap_proto = { path = "../jmap-proto" } - email = { path = "../email" } - mail-parser = { version = "0.10", features = ["full_encoding"] } --mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] } -+mail-send = { path = "../mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] } - rustls = { version = "0.23.5", default-features = false, features = ["std", "ring", "tls12"] } - tokio = { version = "1.23", features = ["full"] } - tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12"] } -diff --git a/crates/smtp/Cargo.toml b/crates/smtp/Cargo.toml -index 5997c1c3..5f5badc2 100644 ---- a/crates/smtp/Cargo.toml -+++ b/crates/smtp/Cargo.toml -@@ -21,7 +21,7 @@ email = { path = "../email" } - spam-filter = { path = "../spam-filter" } - trc = { path = "../trc" } - mail-auth = { version = "0.6" } --mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] } -+mail-send = { path = "../mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] } - mail-parser = { version = "0.10", features = ["full_encoding"] } - mail-builder = { version = "0.4" } - smtp-proto = { version = "0.1", features = ["serde_support"] } -@@ -47,7 +47,7 @@ blake3 = "1.3" - lru-cache = "0.1.2" - rand = "0.8.5" - x509-parser = "0.16.0" --reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"] } -+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "http2"] } - serde = { version = "1.0", features = ["derive", "rc"] } - serde_json = "1.0" - num_cpus = "1.15.0" -diff --git a/crates/spam-filter/Cargo.toml b/crates/spam-filter/Cargo.toml -index f5b63353..c9176cf6 100644 ---- a/crates/spam-filter/Cargo.toml -+++ b/crates/spam-filter/Cargo.toml -@@ -14,12 +14,12 @@ smtp-proto = { version = "0.1", features = ["serde_support"] } - mail-parser = { version = "0.10", features = ["full_encoding"] } - mail-builder = { version = "0.4" } - mail-auth = { version = "0.6" } --mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] } -+mail-send = { path = "../mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] } - tokio = { version = "1.23", features = ["net", "macros"] } - psl = "2" - hyper = { version = "1.0.1", features = ["server", "http1", "http2"] } - idna = "1.0" --reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2", "stream"]} -+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "http2", "stream"]} - decancer = "3.0.1" - unicode-security = "0.1.0" - infer = "0.16" -diff --git a/crates/store/Cargo.toml b/crates/store/Cargo.toml -index b0cf7d77..67c2d742 100644 ---- a/crates/store/Cargo.toml -+++ b/crates/store/Cargo.toml -@@ -15,7 +15,7 @@ rust-s3 = { version = "=0.35.0-alpha.2", default-features = false, features = [" - azure_core = { version = "0.21.0", optional = true } - azure_storage = { version = "0.21.0", default-features = false, features = ["enable_reqwest_rustls", "hmac_rust"], optional = true } - azure_storage_blobs = { version = "0.21.0", default-features = false, features = ["enable_reqwest_rustls", "hmac_rust"], optional = true } --reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2", "stream"]} -+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "http2", "stream"]} - tokio = { version = "1.23", features = ["sync", "fs", "io-util"] } - r2d2 = { version = "0.8.10", optional = true } - futures = { version = "0.3", optional = true } -diff --git a/crates/trc/Cargo.toml b/crates/trc/Cargo.toml -index e4f2ca7c..f294e469 100644 ---- a/crates/trc/Cargo.toml -+++ b/crates/trc/Cargo.toml -@@ -11,7 +11,7 @@ mail-parser = { version = "0.10", features = ["full_encoding"] } - base64 = "0.22.1" - serde = "1.0" - serde_json = "1.0.120" --reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"]} -+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "http2"]} - bincode = "1.3.3" - rtrb = "0.3.1" - parking_lot = "0.12.3" -diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml -index e0a7ef9d..14b1d675 100644 ---- a/crates/utils/Cargo.toml -+++ b/crates/utils/Cargo.toml -@@ -9,12 +9,13 @@ trc = { path = "../trc" } - rustls = { version = "0.23.5", default-features = false, features = ["std", "ring", "tls12"] } - rustls-pemfile = "2.0" - rustls-pki-types = { version = "1" } -+rustls-platform-verifier = "0.5" - tokio = { version = "1.23", features = ["net", "macros"] } - tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12"] } - serde = { version = "1.0", features = ["derive"]} - mail-auth = { version = "0.6" } - smtp-proto = { version = "0.1" } --mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] } -+mail-send = { path = "../mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] } - ahash = { version = "0.8" } - chrono = "0.4" - rand = "0.8.5" -@@ -23,7 +24,7 @@ ring = { version = "0.17" } - base64 = "0.22" - serde_json = "1.0" - rcgen = "0.13" --reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2", "stream"]} -+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "http2", "stream"]} - x509-parser = "0.16.0" - pem = "3.0" - parking_lot = "0.12" -diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs -index acec2f04..b2cdaf65 100644 ---- a/crates/utils/src/lib.rs -+++ b/crates/utils/src/lib.rs -@@ -18,9 +18,9 @@ use futures::StreamExt; - use reqwest::Response; - use rustls::{ - client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}, -- ClientConfig, RootCertStore, SignatureScheme, -+ ClientConfig, SignatureScheme, - }; --use rustls_pki_types::TrustAnchor; -+use rustls_platform_verifier::BuilderVerifierExt; - - pub const BLOB_HASH_LEN: usize = 32; - -@@ -280,17 +280,7 @@ pub fn rustls_client_config(allow_invalid_certs: bool) -> ClientConfig { - let config = ClientConfig::builder(); - - if !allow_invalid_certs { -- let mut root_cert_store = RootCertStore::empty(); -- -- root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| TrustAnchor { -- subject: ta.subject.clone(), -- subject_public_key_info: ta.subject_public_key_info.clone(), -- name_constraints: ta.name_constraints.clone(), -- })); -- -- config -- .with_root_certificates(root_cert_store) -- .with_no_client_auth() -+ config.with_platform_verifier().with_no_client_auth() - } else { - config - .dangerous() -diff --git a/tests/Cargo.toml b/tests/Cargo.toml -index 6aa6d35b..256a574b 100644 ---- a/tests/Cargo.toml -+++ b/tests/Cargo.toml -@@ -34,12 +34,12 @@ spam-filter = { path = "../crates/spam-filter", features = ["test_mode", "enterp - trc = { path = "../crates/trc" } - managesieve = { path = "../crates/managesieve", features = ["test_mode", "enterprise"] } - smtp-proto = { version = "0.1" } --mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] } -+mail-send = { path = "../crates/mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] } - mail-auth = { version = "0.6", features = ["test"] } --sieve-rs = { version = "0.6" } -+sieve-rs = { version = "0.6" } - utils = { path = "../crates/utils", features = ["test_mode"] } --jmap-client = { version = "0.3", features = ["websockets", "debug", "async"] } --mail-parser = { version = "0.10", features = ["full_encoding", "serde_support"] } -+jmap-client = { version = "0.3", features = ["websockets", "debug", "async"] } -+mail-parser = { version = "0.10", features = ["full_encoding", "serde_support"] } - tokio = { version = "1.23", features = ["full"] } - tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12"] } - rustls = { version = "0.23.5", default-features = false, features = ["std", "ring", "tls12"] } -@@ -50,7 +50,7 @@ rayon = { version = "1.5.1" } - flate2 = { version = "1.0.17", features = ["zlib"], default-features = false } - serde = { version = "1.0", features = ["derive"]} - serde_json = "1.0" --reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "multipart", "http2"]} -+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "multipart", "http2"]} - bytes = "1.4.0" - futures = "0.3" - ece = "2.2" --- -2.47.2 - diff --git a/pkgs/by-name/st/stalwart-mail-patched/patches/use-platform-ca-roots.patch b/pkgs/by-name/st/stalwart-mail-patched/patches/use-platform-ca-roots.patch new file mode 100644 index 0000000..392fbde --- /dev/null +++ b/pkgs/by-name/st/stalwart-mail-patched/patches/use-platform-ca-roots.patch @@ -0,0 +1,747 @@ +From 66227b07c6cb4781a38fe603c2e856c5696e0f94 Mon Sep 17 00:00:00 2001 +From: aszlig <aszlig@nix.build> +Date: Wed, 21 May 2025 19:03:55 +0200 +Subject: [PATCH] Allow to switch to operating system CA root store + +So far, we only used the root CA certificates from the "webpki-roots" +crate, which makes it very difficult if you want to run a custom CA. + +In my case I'm running automated tests of full production systems, which +also includes an ACME setup that injects a Pebble instance into the +system's CA certificates. + +Since Stalwart doesn't use the system's root CA certificate store, it's +very difficult to inject the certificate for the Pebble instance. + +Given that there's also some interest (issue #247) for doing this in an +enterprise environment with intranet CAs, I decided to generalise this +far enough that it can not only be used in my downstream setup. + +Currently, this doesn't fully address the issue, since in the long term +this might be something we'd want to configure at runtime, as per +@mdecimus's comment[1]: + +> A slightly more complex approach is required that allows the user to +> select at runtime which CA store to use. + +However, this makes it at least easier to switch to native root CA store +by simply recompiling with the "tls-native-roots" feature. + +[1]: https://github.com/stalwartlabs/stalwart/issues/247#issuecomment-2437039500 + +Signed-off-by: aszlig <aszlig@nix.build> +Issue: https://github.com/stalwartlabs/stalwart/issues/247 +--- + Cargo.lock | 4 ++ + crates/cli/Cargo.toml | 10 +++-- + crates/cli/src/main.rs | 8 ++-- + crates/common/Cargo.toml | 2 +- + crates/common/src/enterprise/llm.rs | 2 +- + crates/common/src/enterprise/mod.rs | 18 +++++--- + crates/common/src/lib.rs | 3 +- + crates/common/src/listener/acme/directory.rs | 6 +-- + crates/common/src/manager/mod.rs | 2 +- + crates/common/src/scripts/plugins/http.rs | 2 +- + crates/common/src/telemetry/webhooks/mod.rs | 2 +- + crates/directory/Cargo.toml | 2 +- + crates/directory/src/backend/oidc/lookup.rs | 4 +- + crates/jmap/Cargo.toml | 2 +- + crates/main/Cargo.toml | 1 + + crates/services/Cargo.toml | 2 +- + crates/services/src/state_manager/http.rs | 2 +- + crates/smtp/Cargo.toml | 2 +- + crates/smtp/src/inbound/hooks/client.rs | 2 +- + crates/smtp/src/outbound/mta_sts/lookup.rs | 3 +- + crates/smtp/src/reporting/tls.rs | 5 +-- + crates/spam-filter/Cargo.toml | 2 +- + crates/spam-filter/src/analysis/url.rs | 2 +- + crates/store/Cargo.toml | 2 +- + crates/store/src/backend/azure/mod.rs | 2 +- + crates/store/src/backend/http/lookup.rs | 2 +- + crates/trc/Cargo.toml | 2 +- + crates/utils/Cargo.toml | 2 + + crates/utils/src/lib.rs | 46 ++++++++++++++------ + crates/utils/src/suffixlist.rs | 7 ++- + tests/Cargo.toml | 2 +- + tests/src/jmap/auth_oauth.rs | 6 +-- + tests/src/jmap/mod.rs | 4 +- + tests/src/smtp/management/queue.rs | 2 +- + tests/src/webdav/mod.rs | 2 +- + 35 files changed, 103 insertions(+), 64 deletions(-) + +diff --git a/Cargo.lock b/Cargo.lock +index 0eeb42510..6dd394dc3 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -3372,6 +3372,7 @@ dependencies = [ + "hyper 1.6.0", + "hyper-util", + "rustls 0.23.27", ++ "rustls-native-certs 0.8.1", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.2", +@@ -6318,6 +6319,7 @@ dependencies = [ + "pin-project-lite", + "quinn", + "rustls 0.23.27", ++ "rustls-native-certs 0.8.1", + "rustls-pemfile 2.2.0", + "rustls-pki-types", + "serde", +@@ -7694,6 +7696,7 @@ dependencies = [ + "serde", + "serde_json", + "tokio", ++ "utils", + ] + + [[package]] +@@ -8795,6 +8798,7 @@ dependencies = [ + "rustls 0.23.27", + "rustls-pemfile 2.2.0", + "rustls-pki-types", ++ "rustls-platform-verifier", + "serde", + "serde_json", + "smtp-proto", +diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml +index 5573df819..719441a0c 100644 +--- a/crates/cli/Cargo.toml ++++ b/crates/cli/Cargo.toml +@@ -11,9 +11,9 @@ readme = "README.md" + resolver = "2" + + [dependencies] +-jmap-client = { version = "0.3", features = ["async"] } +-mail-parser = { version = "0.11", features = ["full_encoding", "serde"] } +-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"]} ++jmap-client = { version = "0.3", features = ["async"] } ++mail-parser = { version = "0.11", features = ["full_encoding", "serde"] } ++reqwest = { version = "0.12", default-features = false, features = ["http2"]} + tokio = { version = "1.45", features = ["full"] } + num_cpus = "1.13.1" + clap = { version = "4.1.6", features = ["derive"] } +@@ -30,3 +30,7 @@ futures = "0.3.28" + pwhash = "1.0.0" + rand = "0.9.0" + mail-auth = { version = "0.7" } ++utils.path = "../utils" ++ ++[features] ++tls-native-roots = ["utils/tls-native-roots"] +diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs +index f295c217f..f62d528c5 100644 +--- a/crates/cli/src/main.rs ++++ b/crates/cli/src/main.rs +@@ -86,7 +86,7 @@ fn parse_credentials(credentials: &str) -> Credentials { + + async fn oauth(url: &str) -> Credentials { + let metadata: HashMap<String, serde_json::Value> = serde_json::from_slice( +- &reqwest::Client::builder() ++ &utils::reqwest_client_builder() + .danger_accept_invalid_certs(is_localhost(url)) + .build() + .unwrap_or_default() +@@ -104,7 +104,7 @@ async fn oauth(url: &str) -> Credentials { + let mut params: HashMap<String, String> = + HashMap::from_iter([("client_id".to_string(), "Stalwart_CLI".to_string())]); + let response: HashMap<String, serde_json::Value> = serde_json::from_slice( +- &reqwest::Client::builder() ++ &utils::reqwest_client_builder() + .danger_accept_invalid_certs(is_localhost(url)) + .build() + .unwrap_or_default() +@@ -138,7 +138,7 @@ async fn oauth(url: &str) -> Credentials { + std::io::stdin().lock().lines().next(); + + let mut response: HashMap<String, serde_json::Value> = serde_json::from_slice( +- &reqwest::Client::builder() ++ &utils::reqwest_client_builder() + .danger_accept_invalid_certs(is_localhost(url)) + .build() + .unwrap_or_default() +@@ -230,7 +230,7 @@ impl Client { + }, + url + ); +- let mut request = reqwest::Client::builder() ++ let mut request = utils::reqwest_client_builder() + .danger_accept_invalid_certs(is_localhost(&url)) + .timeout(Duration::from_secs(self.timeout.unwrap_or(60))) + .build() +diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml +index 58028bad3..5add0a5dc 100644 +--- a/crates/common/Cargo.toml ++++ b/crates/common/Cargo.toml +@@ -33,7 +33,7 @@ tokio = { version = "1.45", features = ["net", "macros"] } + tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12"] } + futures = "0.3" + rcgen = "0.12" +-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2", "stream"]} ++reqwest = { version = "0.12", default-features = false, features = ["http2", "stream"]} + serde = { version = "1.0", features = ["derive"]} + serde_json = "1.0" + base64 = "0.22" +diff --git a/crates/common/src/enterprise/llm.rs b/crates/common/src/enterprise/llm.rs +index 8338f39d3..fe013c0f0 100644 +--- a/crates/common/src/enterprise/llm.rs ++++ b/crates/common/src/enterprise/llm.rs +@@ -125,7 +125,7 @@ impl AiApiConfig { + }; + + // Send request +- let response = reqwest::Client::builder() ++ let response = utils::reqwest_client_builder() + .timeout(self.timeout) + .danger_accept_invalid_certs(self.tls_allow_invalid_certs) + .build() +diff --git a/crates/common/src/enterprise/mod.rs b/crates/common/src/enterprise/mod.rs +index 9fc8de495..ddaf27880 100644 +--- a/crates/common/src/enterprise/mod.rs ++++ b/crates/common/src/enterprise/mod.rs +@@ -188,12 +188,18 @@ impl Server { + + let mut logo = None; + if let Some(logo_url) = logo_url { +- let response = reqwest::get(logo_url.as_str()).await.map_err(|err| { +- trc::ResourceEvent::DownloadExternal +- .into_err() +- .details("Failed to download logo") +- .reason(err) +- })?; ++ let response = utils::reqwest_client_builder() ++ .build() ++ .unwrap_or_default() ++ .get(logo_url.as_str()) ++ .send() ++ .await ++ .map_err(|err| { ++ trc::ResourceEvent::DownloadExternal ++ .into_err() ++ .details("Failed to download logo") ++ .reason(err) ++ })?; + + let content_type = response + .headers() +diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs +index ce4b41b89..185508d1f 100644 +--- a/crates/common/src/lib.rs ++++ b/crates/common/src/lib.rs +@@ -46,6 +46,8 @@ use utils::{ + snowflake::SnowflakeIdGenerator, + }; + ++pub use utils::USER_AGENT; ++ + pub mod addresses; + pub mod auth; + pub mod config; +@@ -67,7 +69,6 @@ pub use psl; + pub static VERSION_PRIVATE: &str = env!("CARGO_PKG_VERSION"); + pub static VERSION_PUBLIC: &str = "1.0.0"; + +-pub static USER_AGENT: &str = "Stalwart/1.0.0"; + pub static DAEMON_NAME: &str = concat!("Stalwart v", env!("CARGO_PKG_VERSION"),); + pub static PROD_ID: &str = "-//Stalwart Labs Ltd.//Stalwart Server//EN"; + +diff --git a/crates/common/src/listener/acme/directory.rs b/crates/common/src/listener/acme/directory.rs +index f095e1969..6f9cfa0e0 100644 +--- a/crates/common/src/listener/acme/directory.rs ++++ b/crates/common/src/listener/acme/directory.rs +@@ -7,7 +7,6 @@ use super::jose::{ + }; + use base64::Engine; + use base64::engine::general_purpose::URL_SAFE_NO_PAD; +-use hyper::header::USER_AGENT; + use rcgen::{Certificate, CustomExtension, PKCS_ECDSA_P256_SHA256}; + use reqwest::header::CONTENT_TYPE; + use reqwest::{Method, Response}; +@@ -316,7 +315,7 @@ async fn https( + body: Option<String>, + ) -> trc::Result<Response> { + let url = url.as_ref(); +- let mut builder = reqwest::Client::builder() ++ let mut builder = utils::reqwest_client_builder() + .timeout(Duration::from_secs(30)) + .http1_only(); + +@@ -330,8 +329,7 @@ async fn https( + let mut request = builder + .build() + .map_err(|err| trc::EventType::Acme(trc::AcmeEvent::Error).from_http_error(err))? +- .request(method, url) +- .header(USER_AGENT, crate::USER_AGENT); ++ .request(method, url); + + if let Some(body) = body { + request = request +diff --git a/crates/common/src/manager/mod.rs b/crates/common/src/manager/mod.rs +index 51861a82c..a59e74b56 100644 +--- a/crates/common/src/manager/mod.rs ++++ b/crates/common/src/manager/mod.rs +@@ -76,7 +76,7 @@ pub async fn fetch_resource( + .await + .map_err(|err| format!("Failed to read {path}: {err}")) + } else { +- let response = reqwest::Client::builder() ++ let response = utils::reqwest_client_builder() + .timeout(timeout) + .danger_accept_invalid_certs(is_localhost_url(url)) + .user_agent(USER_AGENT) +diff --git a/crates/common/src/scripts/plugins/http.rs b/crates/common/src/scripts/plugins/http.rs +index 42e2af553..54d906e17 100644 +--- a/crates/common/src/scripts/plugins/http.rs ++++ b/crates/common/src/scripts/plugins/http.rs +@@ -26,7 +26,7 @@ pub async fn exec_header(ctx: PluginContext<'_>) -> trc::Result<Variable> { + return Ok(Variable::from(url.split_once("/?").unwrap().1.to_string())); + } + +- reqwest::Client::builder() ++ utils::reqwest_client_builder() + .user_agent(agent.as_ref()) + .timeout(Duration::from_millis(timeout)) + .redirect(Policy::none()) +diff --git a/crates/common/src/telemetry/webhooks/mod.rs b/crates/common/src/telemetry/webhooks/mod.rs +index a70005399..2cebfd81f 100644 +--- a/crates/common/src/telemetry/webhooks/mod.rs ++++ b/crates/common/src/telemetry/webhooks/mod.rs +@@ -148,7 +148,7 @@ async fn post_webhook_events( + } + + // Send request +- let response = reqwest::Client::builder() ++ let response = utils::reqwest_client_builder() + .timeout(settings.timeout) + .danger_accept_invalid_certs(settings.tls_allow_invalid_certs) + .build() +diff --git a/crates/directory/Cargo.toml b/crates/directory/Cargo.toml +index ae8eca025..4a033834b 100644 +--- a/crates/directory/Cargo.toml ++++ b/crates/directory/Cargo.toml +@@ -35,7 +35,7 @@ futures = "0.3" + regex = "1.7.0" + serde = { version = "1.0", features = ["derive"]} + totp-rs = { version = "5.5.1", features = ["otpauth"] } +-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"] } ++reqwest = { version = "0.12", default-features = false, features = ["http2"] } + serde_json = "1.0" + base64 = "0.22" + rkyv = { version = "0.8.10", features = ["little_endian"] } +diff --git a/crates/directory/src/backend/oidc/lookup.rs b/crates/directory/src/backend/oidc/lookup.rs +index 755064115..907831a46 100644 +--- a/crates/directory/src/backend/oidc/lookup.rs ++++ b/crates/directory/src/backend/oidc/lookup.rs +@@ -36,10 +36,10 @@ impl OpenIdDirectory { + QueryBy::Credentials(Credentials::OAuthBearer { token }) => { + // Send request + #[cfg(feature = "test_mode")] +- let client = reqwest::Client::builder().danger_accept_invalid_certs(true); ++ let client = utils::reqwest_client_builder().danger_accept_invalid_certs(true); + + #[cfg(not(feature = "test_mode"))] +- let client = reqwest::Client::builder(); ++ let client = utils::reqwest_client_builder(); + + let client = client + .timeout(self.config.endpoint_timeout) +diff --git a/crates/jmap/Cargo.toml b/crates/jmap/Cargo.toml +index 9d9cfa7d7..a3a7b5003 100644 +--- a/crates/jmap/Cargo.toml ++++ b/crates/jmap/Cargo.toml +@@ -36,7 +36,7 @@ p256 = { version = "0.13", features = ["ecdh"] } + hkdf = "0.12.3" + sha1 = "0.10" + sha2 = "0.10" +-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"]} ++reqwest = { version = "0.12", default-features = false, features = ["http2"]} + tokio-tungstenite = "0.26" + tungstenite = "0.26" + chrono = "0.4" +diff --git a/crates/main/Cargo.toml b/crates/main/Cargo.toml +index 1023b73bf..4b39dae29 100644 +--- a/crates/main/Cargo.toml ++++ b/crates/main/Cargo.toml +@@ -64,3 +64,4 @@ enterprise = [ "jmap/enterprise", + "dav/enterprise", + "groupware/enterprise", + "services/enterprise" ] ++tls-native-roots = ["utils/tls-native-roots"] +diff --git a/crates/services/Cargo.toml b/crates/services/Cargo.toml +index 11bb76e5a..35aa0b6cb 100644 +--- a/crates/services/Cargo.toml ++++ b/crates/services/Cargo.toml +@@ -24,7 +24,7 @@ rsa = "0.9.2" + p256 = { version = "0.13", features = ["ecdh"] } + hkdf = "0.12.3" + sha2 = "0.10" +-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"]} ++reqwest = { version = "0.12", default-features = false, features = ["http2"]} + base64 = "0.22" + compact_str = "0.9.0" + +diff --git a/crates/services/src/state_manager/http.rs b/crates/services/src/state_manager/http.rs +index edd01865f..ee26b8482 100644 +--- a/crates/services/src/state_manager/http.rs ++++ b/crates/services/src/state_manager/http.rs +@@ -63,7 +63,7 @@ pub(crate) async fn http_request( + keys: Option<EncryptionKeys>, + push_timeout: Duration, + ) -> bool { +- let client_builder = reqwest::Client::builder().timeout(push_timeout); ++ let client_builder = utils::reqwest_client_builder().timeout(push_timeout); + + #[cfg(feature = "test_mode")] + let client_builder = client_builder.danger_accept_invalid_certs(true); +diff --git a/crates/smtp/Cargo.toml b/crates/smtp/Cargo.toml +index e4a781796..d21665c92 100644 +--- a/crates/smtp/Cargo.toml ++++ b/crates/smtp/Cargo.toml +@@ -47,7 +47,7 @@ blake3 = "1.3" + lru-cache = "0.1.2" + rand = "0.9.0" + x509-parser = "0.17.0" +-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"] } ++reqwest = { version = "0.12", default-features = false, features = ["http2"] } + serde = { version = "1.0", features = ["derive", "rc"] } + serde_json = "1.0" + num_cpus = "1.15.0" +diff --git a/crates/smtp/src/inbound/hooks/client.rs b/crates/smtp/src/inbound/hooks/client.rs +index 2e3a9e17c..6bfb40a63 100644 +--- a/crates/smtp/src/inbound/hooks/client.rs ++++ b/crates/smtp/src/inbound/hooks/client.rs +@@ -13,7 +13,7 @@ pub(super) async fn send_mta_hook_request( + mta_hook: &MTAHook, + request: Request, + ) -> Result<Response, String> { +- let response = reqwest::Client::builder() ++ let response = utils::reqwest_client_builder() + .timeout(mta_hook.timeout) + .danger_accept_invalid_certs(mta_hook.tls_allow_invalid_certs) + .build() +diff --git a/crates/smtp/src/outbound/mta_sts/lookup.rs b/crates/smtp/src/outbound/mta_sts/lookup.rs +index c8b279c2f..10f2aafeb 100644 +--- a/crates/smtp/src/outbound/mta_sts/lookup.rs ++++ b/crates/smtp/src/outbound/mta_sts/lookup.rs +@@ -67,8 +67,7 @@ impl MtaStsLookup for Server { + + // Fetch policy + #[cfg(not(feature = "test_mode"))] +- let bytes = reqwest::Client::builder() +- .user_agent(common::USER_AGENT) ++ let bytes = utils::reqwest_client_builder() + .timeout(timeout) + .redirect(reqwest::redirect::Policy::none()) + .build()? +diff --git a/crates/smtp/src/reporting/tls.rs b/crates/smtp/src/reporting/tls.rs +index 28e9fa47b..875f395c8 100644 +--- a/crates/smtp/src/reporting/tls.rs ++++ b/crates/smtp/src/reporting/tls.rs +@@ -8,7 +8,7 @@ use super::{AggregateTimestamp, SerializedSize}; + use crate::{queue::RecipientDomain, reporting::SmtpReporting}; + use ahash::AHashMap; + use common::{ +- Server, USER_AGENT, ++ Server, + config::smtp::{ + report::AggregateFrequency, + resolver::{Mode, MxPattern}, +@@ -142,8 +142,7 @@ impl TlsReporting for Server { + for uri in &rua { + match uri { + ReportUri::Http(uri) => { +- if let Ok(client) = reqwest::Client::builder() +- .user_agent(USER_AGENT) ++ if let Ok(client) = utils::reqwest_client_builder() + .timeout(Duration::from_secs(2 * 60)) + .build() + { +diff --git a/crates/spam-filter/Cargo.toml b/crates/spam-filter/Cargo.toml +index f6ec739a8..5a3df8453 100644 +--- a/crates/spam-filter/Cargo.toml ++++ b/crates/spam-filter/Cargo.toml +@@ -19,7 +19,7 @@ tokio = { version = "1.45", features = ["net", "macros"] } + psl = "2" + hyper = { version = "1.0.1", features = ["server", "http1", "http2"] } + idna = "1.0" +-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2", "stream"]} ++reqwest = { version = "0.12", default-features = false, features = ["http2", "stream"]} + decancer = "3.0.1" + unicode-security = "0.1.0" + infer = "0.19" +diff --git a/crates/spam-filter/src/analysis/url.rs b/crates/spam-filter/src/analysis/url.rs +index a0d663917..e6377f13c 100644 +--- a/crates/spam-filter/src/analysis/url.rs ++++ b/crates/spam-filter/src/analysis/url.rs +@@ -290,7 +290,7 @@ async fn http_get_header( + Ok(None) + }; + } +- reqwest::Client::builder() ++ utils::reqwest_client_builder() + .user_agent("Mozilla/5.0 (X11; Linux i686; rv:109.0) Gecko/20100101 Firefox/118.0") + .timeout(timeout) + .redirect(Policy::none()) +diff --git a/crates/store/Cargo.toml b/crates/store/Cargo.toml +index 1f8965049..3bd1a228c 100644 +--- a/crates/store/Cargo.toml ++++ b/crates/store/Cargo.toml +@@ -16,7 +16,7 @@ async-nats = { version = "0.40", default-features = false, features = ["server_2 + azure_core = { version = "0.21.0", optional = true } + azure_storage = { version = "0.21.0", default-features = false, features = ["enable_reqwest_rustls", "hmac_rust"], optional = true } + azure_storage_blobs = { version = "0.21.0", default-features = false, features = ["enable_reqwest_rustls", "hmac_rust"], optional = true } +-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2", "stream"]} ++reqwest = { version = "0.12", default-features = false, features = ["http2", "stream"]} + tokio = { version = "1.45", features = ["sync", "fs", "io-util"] } + r2d2 = { version = "0.8.10", optional = true } + futures = { version = "0.3", optional = true } +diff --git a/crates/store/src/backend/azure/mod.rs b/crates/store/src/backend/azure/mod.rs +index 8bbaea073..fbdb03eb7 100644 +--- a/crates/store/src/backend/azure/mod.rs ++++ b/crates/store/src/backend/azure/mod.rs +@@ -63,7 +63,7 @@ impl AzureStore { + let timeout = config + .property_or_default::<Duration>((&prefix, "timeout"), "30s") + .unwrap_or_else(|| Duration::from_secs(30)); +- let transport = match reqwest::Client::builder().timeout(timeout).build() { ++ let transport = match utils::reqwest_client_builder().timeout(timeout).build() { + Ok(client) => Arc::new(client), + Err(err) => { + config.new_build_error( +diff --git a/crates/store/src/backend/http/lookup.rs b/crates/store/src/backend/http/lookup.rs +index dbaa932ed..ff7cca2d8 100644 +--- a/crates/store/src/backend/http/lookup.rs ++++ b/crates/store/src/backend/http/lookup.rs +@@ -91,7 +91,7 @@ impl HttpStore { + async fn try_refresh(&self) -> trc::Result<AHashMap<String, Value<'static>>> { + let time = Instant::now(); + let agent = BROWSER_USER_AGENTS.choose(&mut rand::rng()).unwrap(); +- let response = reqwest::Client::builder() ++ let response = utils::reqwest_client_builder() + .timeout(self.config.timeout) + .user_agent(*agent) + .build() +diff --git a/crates/trc/Cargo.toml b/crates/trc/Cargo.toml +index 61d1cd69a..22d8e6599 100644 +--- a/crates/trc/Cargo.toml ++++ b/crates/trc/Cargo.toml +@@ -11,7 +11,7 @@ mail-parser = { version = "0.11", features = ["full_encoding"] } + base64 = "0.22.1" + serde = "1.0" + serde_json = "1.0.120" +-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"]} ++reqwest = { version = "0.12", default-features = false, features = ["http2"]} + rtrb = "0.3.1" + parking_lot = "0.12.3" + tokio = { version = "1.45", features = ["net", "macros"] } +diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml +index 4766c55b5..f45780d24 100644 +--- a/crates/utils/Cargo.toml ++++ b/crates/utils/Cargo.toml +@@ -9,6 +9,7 @@ trc = { path = "../trc" } + rustls = { version = "0.23.5", default-features = false, features = ["std", "ring", "tls12"] } + rustls-pemfile = "2.0" + rustls-pki-types = { version = "1" } ++rustls-platform-verifier = { version = "0.5.3", optional = true } + tokio = { version = "1.45", features = ["net", "macros", "signal"] } + tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12"] } + serde = { version = "1.0", features = ["derive"]} +@@ -45,6 +46,7 @@ privdrop = "0.5.3" + + [features] + test_mode = [] ++tls-native-roots = ["dep:rustls-platform-verifier", "reqwest/rustls-tls-native-roots"] + + [dev-dependencies] + tokio = { version = "1.45", features = ["full"] } +diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs +index 2b73df148..2efcc96d7 100644 +--- a/crates/utils/src/lib.rs ++++ b/crates/utils/src/lib.rs +@@ -21,14 +21,14 @@ use compact_str::ToCompactString; + use futures::StreamExt; + use reqwest::Response; + use rustls::{ +- ClientConfig, RootCertStore, SignatureScheme, ++ ClientConfig, SignatureScheme, + client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}, + }; +-use rustls_pki_types::TrustAnchor; + + pub use downcast_rs; + pub use erased_serde; + ++pub static USER_AGENT: &str = "Stalwart/1.0.0"; + pub const BLOB_HASH_LEN: usize = 32; + + #[derive( +@@ -294,20 +294,28 @@ pub async fn wait_for_shutdown() { + } + + pub fn rustls_client_config(allow_invalid_certs: bool) -> ClientConfig { ++ #[cfg(feature = "tls-native-roots")] ++ use rustls_platform_verifier::BuilderVerifierExt; ++ + let config = ClientConfig::builder(); + + if !allow_invalid_certs { +- let mut root_cert_store = RootCertStore::empty(); +- +- root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| TrustAnchor { +- subject: ta.subject.clone(), +- subject_public_key_info: ta.subject_public_key_info.clone(), +- name_constraints: ta.name_constraints.clone(), +- })); +- +- config +- .with_root_certificates(root_cert_store) +- .with_no_client_auth() ++ #[cfg(feature = "tls-native-roots")] ++ let config = config.with_platform_verifier(); ++ ++ #[cfg(not(feature = "tls-native-roots"))] ++ let config = config.with_root_certificates( ++ webpki_roots::TLS_SERVER_ROOTS ++ .iter() ++ .map(|ta| rustls_pki_types::TrustAnchor { ++ subject: ta.subject.clone(), ++ subject_public_key_info: ta.subject_public_key_info.clone(), ++ name_constraints: ta.name_constraints.clone(), ++ }) ++ .collect::<rustls::RootCertStore>(), ++ ); ++ ++ config.with_no_client_auth() + } else { + config + .dangerous() +@@ -316,6 +324,18 @@ pub fn rustls_client_config(allow_invalid_certs: bool) -> ClientConfig { + } + } + ++pub fn reqwest_client_builder() -> reqwest::ClientBuilder { ++ let builder = reqwest::Client::builder(); ++ ++ #[cfg(feature = "tls-native-roots")] ++ let builder = builder.tls_built_in_native_certs(true); ++ ++ #[cfg(not(feature = "tls-native-roots"))] ++ let builder = builder.tls_built_in_webpki_certs(true); ++ ++ builder.user_agent(USER_AGENT) ++} ++ + #[derive(Debug)] + struct DummyVerifier; + +diff --git a/crates/utils/src/suffixlist.rs b/crates/utils/src/suffixlist.rs +index e0921abe3..b53126ae9 100644 +--- a/crates/utils/src/suffixlist.rs ++++ b/crates/utils/src/suffixlist.rs +@@ -111,7 +111,12 @@ impl PublicSuffix { + + for (idx, value) in values.into_iter().enumerate() { + let bytes = if value.starts_with("https://") || value.starts_with("http://") { +- let result = match reqwest::get(&value).await { ++ let result = match crate::reqwest_client_builder() ++ .build() ++ .unwrap_or_default() ++ .get(&value) ++ .await ++ { + Ok(r) => { + if r.status().is_success() { + r.bytes().await +diff --git a/tests/Cargo.toml b/tests/Cargo.toml +index 386b8255a..68692e939 100644 +--- a/tests/Cargo.toml ++++ b/tests/Cargo.toml +@@ -59,7 +59,7 @@ rayon = { version = "1.5.1" } + flate2 = { version = "1.0.17", features = ["zlib"], default-features = false } + serde = { version = "1.0", features = ["derive"]} + serde_json = "1.0" +-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "multipart", "http2"]} ++reqwest = { version = "0.12", default-features = false, features = ["multipart", "http2"]} + bytes = "1.4.0" + futures = "0.3" + ece = "2.2" +diff --git a/tests/src/jmap/auth_oauth.rs b/tests/src/jmap/auth_oauth.rs +index 0d05d3a77..fff811fcf 100644 +--- a/tests/src/jmap/auth_oauth.rs ++++ b/tests/src/jmap/auth_oauth.rs +@@ -411,7 +411,7 @@ async fn post_bytes( + auth_token: Option<&str>, + params: &AHashMap<String, String>, + ) -> Bytes { +- let mut client = reqwest::Client::builder() ++ let mut client = utils::reqwest_client_builder() + .timeout(Duration::from_millis(500)) + .danger_accept_invalid_certs(true) + .build() +@@ -437,7 +437,7 @@ async fn post_json<D: DeserializeOwned>( + auth_token: Option<&str>, + body: &impl Serialize, + ) -> D { +- let mut client = reqwest::Client::builder() ++ let mut client = utils::reqwest_client_builder() + .timeout(Duration::from_millis(500)) + .danger_accept_invalid_certs(true) + .build() +@@ -473,7 +473,7 @@ async fn post_with_auth<T: DeserializeOwned>( + } + + async fn get_bytes(url: &str) -> Bytes { +- reqwest::Client::builder() ++ utils::reqwest_client_builder() + .timeout(Duration::from_millis(500)) + .danger_accept_invalid_certs(true) + .build() +diff --git a/tests/src/jmap/mod.rs b/tests/src/jmap/mod.rs +index 554026a00..ffd8d292a 100644 +--- a/tests/src/jmap/mod.rs ++++ b/tests/src/jmap/mod.rs +@@ -419,7 +419,7 @@ pub async fn jmap_raw_request(body: impl AsRef<str>, username: &str, secret: &st + }"#; + + String::from_utf8( +- reqwest::Client::builder() ++ utils::reqwest_client_builder() + .danger_accept_invalid_certs(true) + .timeout(Duration::from_millis(1000)) + .default_headers(headers) +@@ -620,7 +620,7 @@ impl ManagementApi { + query: &str, + body: Option<String>, + ) -> Result<String, String> { +- let mut request = reqwest::Client::builder() ++ let mut request = utils::reqwest_client_builder() + .timeout(Duration::from_millis(500)) + .danger_accept_invalid_certs(true) + .build() +diff --git a/tests/src/smtp/management/queue.rs b/tests/src/smtp/management/queue.rs +index e86f55169..a0d144127 100644 +--- a/tests/src/smtp/management/queue.rs ++++ b/tests/src/smtp/management/queue.rs +@@ -493,7 +493,7 @@ async fn manage_queue() { + + // Test authentication error + assert_eq!( +- reqwest::Client::builder() ++ utils::reqwest_client_builder() + .timeout(Duration::from_millis(500)) + .danger_accept_invalid_certs(true) + .build() +diff --git a/tests/src/webdav/mod.rs b/tests/src/webdav/mod.rs +index 0ed8b1888..25aab71a4 100644 +--- a/tests/src/webdav/mod.rs ++++ b/tests/src/webdav/mod.rs +@@ -302,7 +302,7 @@ impl DummyWebDavClient { + headers: impl IntoIterator<Item = (&'static str, &str)>, + body: impl Into<String>, + ) -> DavResponse { +- let mut request = reqwest::Client::builder() ++ let mut request = utils::reqwest_client_builder() + .timeout(Duration::from_millis(500)) + .danger_accept_invalid_certs(true) + .build() |