diff --git a/Cargo.toml b/Cargo.toml index 9e606cb2..0bea5212 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,11 +16,12 @@ exclude = ["/cargo_deny.sh", "/deny.toml", "/test.sh"] rust-version = "1.67" [package.metadata.docs.rs] -features = ["rustls", "native-tls", "socks-proxy", "cookies", "gzip", "brotli", "charset", "json", "_test"] +features = ["rustls", "platform-verifier", "native-tls", "socks-proxy", "cookies", "gzip", "brotli", "charset", "json", "_test"] [features] default = ["rustls", "gzip", "json"] -rustls = ["dep:rustls", "_tls", "dep:rustls-platform-verifier", "dep:webpki-roots"] +rustls = ["dep:rustls", "_tls", "dep:webpki-roots"] +platform-verifier = ["dep:rustls-platform-verifier"] native-tls = ["dep:native-tls", "dep:der", "_tls", "dep:webpki-root-certs"] socks-proxy = ["dep:socks"] cookies = ["dep:cookie_store", "_url"] diff --git a/README.md b/README.md index df80df45..ed7c541e 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,8 @@ The default enabled features are: **rustls**, **gzip** and **json**. accidentally switching on an unwanted TLS implementation, `native-tls` is never picked up as a default or used by the crate level convenience calls (`ureq::get` etc) – it must be configured on the agent. +* **platform-verifier** enables verifying the server certificates using a method native to the + platform ureq is executing on. See [rustls-platform-verifier] crate. * **socks-proxy** enables proxy config using the `socks4://`, `socks4a://`, `socks5://` and `socks://` (equal to `socks5://`) prefix. * **cookies** enables cookies. @@ -166,12 +168,12 @@ The default enabled features are: **rustls**, **gzip** and **json**. ## TLS (https) +### rustls + By default, ureq uses [`rustls` crate] with the `ring` cryptographic provider. As of Sep 2024, the `ring` provider has a higher chance of compiling successfully. If the user installs another [default provider], that choice is respected. -### rustls - ```rust // This uses rustls ureq::get("https://www.google.com/").call().unwrap(); @@ -203,6 +205,51 @@ let agent = config.new_agent(); agent.get("https://www.google.com/").call().unwrap(); ``` +### Root certificates + +#### webpki-roots + +By default, ureq uses Mozilla's root certificates via the [webpki-roots] crate. This is a static +bundle of root certificates that do not update automatically. It also circumvents whatever root +certificates are installed on the host running ureq, which might be a good or a bad thing depending +on your perspective. There is also no mechanism for +[SCT](https://en.wikipedia.org/wiki/Certificate_Transparency), +[CRLs](https://en.wikipedia.org/wiki/Certificate_revocation_list) or other revocations. +To maintain a "fresh" list of root certs, you need to bump the ureq dependency from time to time. + +The main reason for chosing this as the default is to minimize the number of dependencies. More +details about this decision can be found at [PR 818](https://github.com/algesten/ureq/pull/818) + +If your use case for ureq is talking to a limited number of servers with high trust, the +default setting is likely sufficient. If you use ureq with a high number of servers, or servers +you don't trust, we recommend using the platform verifier (see below). + +#### platform-verifier + +The [rustls-platform-verifier] crate provides access to natively checking the certificate via your OS. +To use this verifier, you need to enable it using feature flag **platform-verifier** as well as +configure an agent to use it. + +```rust +use ureq::Agent; +use ureq::tls::{TlsConfig, RootCerts}; + +let agent = Agent::config_builder() + .tls_config( + TlsConfig::builder() + .root_certs(RootCerts::PlatformVerifier) + .build() + ) + .build() + .new_agent(); + +let response = agent.get("https://httpbin.org/get").call()?; +``` + +Setting `RootCerts::PlatformVerifier` together with `TlsProvider::NativeTls` means +also native-tls will use the OS roots instead of [webpki-roots] crate. Whether that +results in a config that has CRLs and revocations is up to whatever native-tls links to. + ## JSON By enabling the **json** feature, the library supports serde json. @@ -322,6 +369,8 @@ Proxies settings are configured on an [Agent]. All request sent through the agen [`rustls` crate]: https://crates.io/crates/rustls [default provider]: https://docs.rs/rustls/latest/rustls/crypto/struct.CryptoProvider.html#method.install_default [`native-tls`]: https://crates.io/crates/native-tls +[rustls-platform-verifier]: https://crates.io/crates/rustls-platform-verifier +[webpki-roots]: https://crates.io/crates/webpki-roots ### Example using HTTP diff --git a/src/lib.rs b/src/lib.rs index 60ccf1d2..7213c35e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -146,6 +146,8 @@ //! accidentally switching on an unwanted TLS implementation, `native-tls` is never picked up as //! a default or used by the crate level convenience calls (`ureq::get` etc) – it must be configured //! on the agent. +//! * **platform-verifier** enables verifying the server certificates using a method native to the +//! platform ureq is executing on. See [rustls-platform-verifier] crate. //! * **socks-proxy** enables proxy config using the `socks4://`, `socks4a://`, `socks5://` //! and `socks://` (equal to `socks5://`) prefix. //! * **cookies** enables cookies. @@ -158,12 +160,12 @@ //! //! # TLS (https) //! +//! ## rustls +//! //! By default, ureq uses [`rustls` crate] with the `ring` cryptographic provider. //! As of Sep 2024, the `ring` provider has a higher chance of compiling successfully. If the user //! installs another [default provider], that choice is respected. //! -//! ## rustls -//! //! ``` //! # #[cfg(feature = "rustls")] //! # { @@ -201,6 +203,54 @@ //! # } Ok::<_, ureq::Error>(()) //! ``` //! +//! ## Root certificates +//! +//! ### webpki-roots +//! +//! By default, ureq uses Mozilla's root certificates via the [webpki-roots] crate. This is a static +//! bundle of root certificates that do not update automatically. It also circumvents whatever root +//! certificates are installed on the host running ureq, which might be a good or a bad thing depending +//! on your perspective. There is also no mechanism for +//! [SCT](https://en.wikipedia.org/wiki/Certificate_Transparency), +//! [CRLs](https://en.wikipedia.org/wiki/Certificate_revocation_list) or other revocations. +//! To maintain a "fresh" list of root certs, you need to bump the ureq dependency from time to time. +//! +//! The main reason for chosing this as the default is to minimize the number of dependencies. More +//! details about this decision can be found at [PR 818](https://github.com/algesten/ureq/pull/818) +//! +//! If your use case for ureq is talking to a limited number of servers with high trust, the +//! default setting is likely sufficient. If you use ureq with a high number of servers, or servers +//! you don't trust, we recommend using the platform verifier (see below). +//! +//! ### platform-verifier +//! +//! The [rustls-platform-verifier] crate provides access to natively checking the certificate via your OS. +//! To use this verifier, you need to enable it using feature flag **platform-verifier** as well as +//! configure an agent to use it. +//! +//! ``` +//! # #[cfg(all(feature = "rustls", feature="platform-verifier"))] +//! # { +//! use ureq::Agent; +//! use ureq::tls::{TlsConfig, RootCerts}; +//! +//! let agent = Agent::config_builder() +//! .tls_config( +//! TlsConfig::builder() +//! .root_certs(RootCerts::PlatformVerifier) +//! .build() +//! ) +//! .build() +//! .new_agent(); +//! +//! let response = agent.get("https://httpbin.org/get").call()?; +//! # } Ok::<_, ureq::Error>(()) +//! ``` +//! +//! Setting `RootCerts::PlatformVerifier` together with `TlsProvider::NativeTls` means +//! also native-tls will use the OS roots instead of [webpki-roots] crate. Whether that +//! results in a config that has CRLs and revocations is up to whatever native-tls links to. +//! //! # JSON //! //! By enabling the **json** feature, the library supports serde json. @@ -321,6 +371,8 @@ //! [`rustls` crate]: https://crates.io/crates/rustls //! [default provider]: https://docs.rs/rustls/latest/rustls/crypto/struct.CryptoProvider.html#method.install_default //! [`native-tls`]: https://crates.io/crates/native-tls +//! [rustls-platform-verifier]: https://crates.io/crates/rustls-platform-verifier +//! [webpki-roots]: https://crates.io/crates/webpki-roots //! //! ## Example using HTTP //! diff --git a/src/tls/mod.rs b/src/tls/mod.rs index 90baa2f5..d7433d89 100644 --- a/src/tls/mod.rs +++ b/src/tls/mod.rs @@ -81,7 +81,7 @@ pub struct TlsConfig { /// The set of trusted root certificates to use to validate server certificates. /// - /// Defaults to `PlatformVerifier` to use the platform default root certs. + /// Defaults to `WebPki`. pub(crate) root_certs: RootCerts, /// Whether to send SNI (Server Name Indication) to the remote server. @@ -132,7 +132,7 @@ impl TlsConfigBuilder { /// The set of trusted root certificates to use to validate server certificates. /// - /// Defaults to `PlatformVerifier` to use the platform default root certs. + /// Defaults to `WebPki`. pub fn root_certs(mut self, v: RootCerts) -> Self { self.config.root_certs = v; self @@ -184,7 +184,8 @@ pub enum RootCerts { /// Use the platform's verifier. /// - /// * For **rustls**, this uses the `rustls-platform-verifier` crate. + /// * For **rustls**, this uses the `rustls-platform-verifier` crate. It requires + /// the feature **platform-verifier**. /// * For **native-tls**, this uses the roots that native-tls loads by default. PlatformVerifier, @@ -192,6 +193,8 @@ pub enum RootCerts { /// /// This is useful when you can't trust the system roots, such as in /// environments where TLS is intercepted and decrypted by a proxy (MITM attack). + /// + /// This is the default value. WebPki, } @@ -214,7 +217,7 @@ impl Default for TlsConfig { Self { provider, client_cert: None, - root_certs: RootCerts::PlatformVerifier, + root_certs: RootCerts::WebPki, use_sni: true, disable_verification: false, } diff --git a/src/tls/rustls.rs b/src/tls/rustls.rs index eb05d8ad..5dc701a6 100644 --- a/src/tls/rustls.rs +++ b/src/tls/rustls.rs @@ -114,6 +114,11 @@ fn build_config(tls_config: &TlsConfig) -> Arc { builder.with_root_certificates(root_store) } + #[cfg(not(feature = "platform-verifier"))] + RootCerts::PlatformVerifier => { + panic!("Rustls + PlatformVerifier requires feature: platform-verifier"); + } + #[cfg(feature = "platform-verifier")] RootCerts::PlatformVerifier => builder // This actually not dangerous. The rustls_platform_verifier is safe. .dangerous()