Skip to content

Commit

Permalink
Support building with rustls but native certificates (#3551)
Browse files Browse the repository at this point in the history
This adds an alternative TLS configuration that relies on rustls-native-certs,
for users who cannot bundle the MPL-licensed webpki-roots.

The approach is copied from reqwest:

* https://github.com/seanmonstar/reqwest/blob/3ad6e02cd/Cargo.toml#L48
* https://github.com/seanmonstar/reqwest/blob/3ad6e02cd/src/async_impl/client.rs#L513

— except error handling is relaxed to accommodate for tls_config.root_cert_path.
  • Loading branch information
IlyaBizyaev authored Nov 28, 2024
1 parent 3e140ba commit 35c78f5
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 14 deletions.
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ runtime-tokio = ["_rt-tokio", "sqlx-core/_rt-tokio", "sqlx-macros?/_rt-tokio"]
tls-native-tls = ["sqlx-core/_tls-native-tls", "sqlx-macros?/_tls-native-tls"]
tls-rustls = ["tls-rustls-ring"] # For backwards compatibility
tls-rustls-aws-lc-rs = ["sqlx-core/_tls-rustls-aws-lc-rs", "sqlx-macros?/_tls-rustls-aws-lc-rs"]
tls-rustls-ring = ["sqlx-core/_tls-rustls-ring", "sqlx-macros?/_tls-rustls-ring"]
tls-rustls-ring = ["tls-rustls-ring-webpki"] # For backwards compatibility
tls-rustls-ring-webpki = ["sqlx-core/_tls-rustls-ring-webpki", "sqlx-macros?/_tls-rustls-ring-webpki"]
tls-rustls-ring-native-roots = ["sqlx-core/_tls-rustls-ring-native-roots", "sqlx-macros?/_tls-rustls-ring-native-roots"]

# No-op feature used by the workflows to compile without TLS enabled. Not meant for general use.
tls-none = []
Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,17 +136,21 @@ SQLx is compatible with the [`async-std`], [`tokio`], and [`actix`] runtimes; an
sqlx = { version = "0.8", features = [ "runtime-tokio" ] }
# tokio + native-tls
sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-native-tls" ] }
# tokio + rustls with ring
sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-rustls-ring" ] }
# tokio + rustls with ring and WebPKI CA certificates
sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-rustls-ring-webpki" ] }
# tokio + rustls with ring and platform's native CA certificates
sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-rustls-ring-native-roots" ] }
# tokio + rustls with aws-lc-rs
sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-rustls-aws-lc-rs" ] }

# async-std (no TLS)
sqlx = { version = "0.8", features = [ "runtime-async-std" ] }
# async-std + native-tls
sqlx = { version = "0.8", features = [ "runtime-async-std", "tls-native-tls" ] }
# async-std + rustls with ring
sqlx = { version = "0.8", features = [ "runtime-async-std", "tls-rustls-ring" ] }
# async-std + rustls with ring and WebPKI CA certificates
sqlx = { version = "0.8", features = [ "runtime-async-std", "tls-rustls-ring-webpki" ] }
# async-std + rustls with ring and platform's native CA certificates
sqlx = { version = "0.8", features = [ "runtime-async-std", "tls-rustls-ring-native-roots" ] }
# async-std + rustls with aws-lc-rs
sqlx = { version = "0.8", features = [ "runtime-async-std", "tls-rustls-aws-lc-rs" ] }
```
Expand Down
8 changes: 5 additions & 3 deletions sqlx-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ json = ["serde", "serde_json"]
_rt-async-std = ["async-std", "async-io"]
_rt-tokio = ["tokio", "tokio-stream"]
_tls-native-tls = ["native-tls"]
_tls-rustls-aws-lc-rs = ["_tls-rustls", "rustls/aws-lc-rs"]
_tls-rustls-ring = ["_tls-rustls", "rustls/ring"]
_tls-rustls = ["rustls", "rustls-pemfile", "webpki-roots"]
_tls-rustls-aws-lc-rs = ["_tls-rustls", "rustls/aws-lc-rs", "webpki-roots"]
_tls-rustls-ring-webpki = ["_tls-rustls", "rustls/ring", "webpki-roots"]
_tls-rustls-ring-native-roots = ["_tls-rustls", "rustls/ring", "rustls-native-certs"]
_tls-rustls = ["rustls", "rustls-pemfile"]
_tls-none = []

# support offline/decoupled building (enables serialization of `Describe`)
Expand All @@ -41,6 +42,7 @@ native-tls = { version = "0.2.10", optional = true }
rustls = { version = "0.23.11", default-features = false, features = ["std", "tls12"], optional = true }
rustls-pemfile = { version = "2", optional = true }
webpki-roots = { version = "0.26", optional = true }
rustls-native-certs = { version = "0.8.0", optional = true }

# Type Integrations
bit-vec = { workspace = true, optional = true }
Expand Down
39 changes: 35 additions & 4 deletions sqlx-core/src/net/tls/tls_rustls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,16 @@ pub async fn handshake<S>(socket: S, tls_config: TlsConfig<'_>) -> Result<Rustls
where
S: Socket,
{
#[cfg(all(feature = "_tls-rustls-aws-lc-rs", not(feature = "_tls-rustls-ring")))]
#[cfg(all(
feature = "_tls-rustls-aws-lc-rs",
not(feature = "_tls-rustls-ring-webpki"),
not(feature = "_tls-rustls-ring-native-roots")
))]
let provider = Arc::new(rustls::crypto::aws_lc_rs::default_provider());
#[cfg(feature = "_tls-rustls-ring")]
#[cfg(any(
feature = "_tls-rustls-ring-webpki",
feature = "_tls-rustls-ring-native-roots"
))]
let provider = Arc::new(rustls::crypto::ring::default_provider());

// Unwrapping is safe here because we use a default provider.
Expand Down Expand Up @@ -127,8 +134,10 @@ where
.with_no_client_auth()
}
} else {
let mut cert_store = RootCertStore::empty();
cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
#[cfg(any(feature = "_tls-rustls-aws-lc-rs", feature = "_tls-rustls-ring-webpki"))]
let mut cert_store = certs_from_webpki();
#[cfg(feature = "_tls-rustls-ring-native-roots")]
let mut cert_store = certs_from_native_store();

if let Some(ca) = tls_config.root_cert_path {
let data = ca.data().await?;
Expand Down Expand Up @@ -204,6 +213,28 @@ fn private_key_from_pem(pem: Vec<u8>) -> Result<PrivateKeyDer<'static>, Error> {
}
}

#[cfg(any(feature = "_tls-rustls-aws-lc-rs", feature = "_tls-rustls-ring-webpki"))]
fn certs_from_webpki() -> RootCertStore {
RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned())
}

#[cfg(feature = "_tls-rustls-ring-native-roots")]
fn certs_from_native_store() -> RootCertStore {
let mut root_cert_store = RootCertStore::empty();

let load_results = rustls_native_certs::load_native_certs();
for e in load_results.errors {
log::warn!("Error loading native certificates: {e:?}");
}
for cert in load_results.certs {
if let Err(e) = root_cert_store.add(cert.into()) {
log::warn!("rustls failed to parse native certificate: {e:?}");
}
}

root_cert_store
}

#[derive(Debug)]
struct DummyTlsVerifier {
provider: Arc<CryptoProvider>,
Expand Down
3 changes: 2 additions & 1 deletion sqlx-macros-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ _rt-tokio = ["tokio", "sqlx-core/_rt-tokio"]

_tls-native-tls = ["sqlx-core/_tls-native-tls"]
_tls-rustls-aws-lc-rs = ["sqlx-core/_tls-rustls-aws-lc-rs"]
_tls-rustls-ring = ["sqlx-core/_tls-rustls-ring"]
_tls-rustls-ring-webpki = ["sqlx-core/_tls-rustls-ring-webpki"]
_tls-rustls-ring-native-roots = ["sqlx-core/_tls-rustls-ring-native-roots"]

_sqlite = []

Expand Down
3 changes: 2 additions & 1 deletion sqlx-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ _rt-tokio = ["sqlx-macros-core/_rt-tokio"]

_tls-native-tls = ["sqlx-macros-core/_tls-native-tls"]
_tls-rustls-aws-lc-rs = ["sqlx-macros-core/_tls-rustls-aws-lc-rs"]
_tls-rustls-ring = ["sqlx-macros-core/_tls-rustls-ring"]
_tls-rustls-ring-webpki = ["sqlx-macros-core/_tls-rustls-ring-webpki"]
_tls-rustls-ring-native-roots = ["sqlx-macros-core/_tls-rustls-ring-native-roots"]

# SQLx features
derive = ["sqlx-macros-core/derive"]
Expand Down

0 comments on commit 35c78f5

Please sign in to comment.