Skip to content

Commit 4b49811

Browse files
committed
Add client TLS builder method to add trust anchors
As per #101, it is sometimes desirable to use standard web PKI roots for gRPC clients. This commit adds a method to ClientTlsConfig to allow this. The behaviour differs per TLS library: - OpenSSL uses `openssl-probe` to search the system for roots and add them. - Rustls adds the Mozilla-supplied roots from the `webpki-roots` crate. This is not feature flagged, as there appears to be no convenient way to gate a dependency on multiple conditions.
1 parent 46bb2bf commit 4b49811

File tree

3 files changed

+75
-0
lines changed

3 files changed

+75
-0
lines changed

tonic/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ transport = [
3434
]
3535
openssl = ["openssl1", "tokio-openssl", "tls"]
3636
rustls = ["tokio-rustls", "tls"]
37+
openssl-roots = ["openssl-probe"]
38+
rustls-roots = ["webpki-roots"]
3739
tls = []
3840

3941
[[bench]]
@@ -73,9 +75,11 @@ tower-load = { version = "=0.3.0-alpha.2", optional = true }
7375
# openssl
7476
tokio-openssl = { version = "=0.4.0-alpha.6", optional = true }
7577
openssl1 = { package = "openssl", version = "0.10", optional = true }
78+
openssl-probe = { version = "0.1", optional = true }
7679

7780
# rustls
7881
tokio-rustls = { version = "=0.12.0-alpha.5", optional = true }
82+
webpki-roots = { version = "0.18", optional = true }
7983

8084
[dev-dependencies]
8185
static_assertions = "1.0"

tonic/src/transport/endpoint.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ pub struct ClientTlsConfig {
213213
domain: Option<String>,
214214
cert: Option<Certificate>,
215215
identity: Option<Identity>,
216+
#[cfg(feature = "openssl-roots")]
217+
#[cfg(feature = "rustls-roots")]
218+
add_trust_anchors: bool,
216219
#[cfg(feature = "openssl")]
217220
openssl_raw: Option<openssl1::ssl::SslConnector>,
218221
#[cfg(feature = "rustls")]
@@ -251,6 +254,9 @@ impl ClientTlsConfig {
251254
domain: None,
252255
cert: None,
253256
identity: None,
257+
#[cfg(feature = "openssl-roots")]
258+
#[cfg(feature = "rustls-roots")]
259+
add_trust_anchors: false,
254260
#[cfg(feature = "openssl")]
255261
openssl_raw: None,
256262
#[cfg(feature = "rustls")]
@@ -259,18 +265,47 @@ impl ClientTlsConfig {
259265
}
260266

261267
/// Sets the domain name against which to verify the server's TLS certificate.
268+
///
269+
/// This has no effect if `rustls_client_config` or `openssl_connector` is used to configure
270+
/// Rustls or OpenSSL respectively.
262271
pub fn domain_name(&mut self, domain_name: impl Into<String>) -> &mut Self {
263272
self.domain = Some(domain_name.into());
264273
self
265274
}
266275

276+
/// Add the Mozilla trust anchors from the `webpki_roots` crate to the TLS connector
277+
/// configuration.
278+
///
279+
/// This has no effect if `rustls_client_config` is used to configure Rustls.
280+
#[cfg(feature = "rustls-roots")]
281+
pub fn add_mozilla_trust_anchors(&mut self) -> &mut Self {
282+
self.add_trust_anchors = true;
283+
self
284+
}
285+
286+
/// Add the system trust anchors (discovered via the `openssl_roots` crate) to the
287+
/// TLS connector configuration.
288+
///
289+
/// This has no effect if `openssl_connector` is used to configure Rustls.
290+
#[cfg(feature = "openssl-roots")]
291+
pub fn add_system_trust_anchors(&mut self) -> &mut Self {
292+
self.add_trust_anchors = true;
293+
self
294+
}
295+
267296
/// Sets the CA Certificate against which to verify the server's TLS certificate.
297+
///
298+
/// This has no effect if `rustls_client_config` or `openssl_connector` is used to configure
299+
/// Rustls or OpenSSL respectively.
268300
pub fn ca_certificate(&mut self, ca_certificate: Certificate) -> &mut Self {
269301
self.cert = Some(ca_certificate);
270302
self
271303
}
272304

273305
/// Sets the client identity to present to the server.
306+
///
307+
/// This has no effect if `rustls_client_config` or `openssl_connector` is used to configure
308+
/// Rustls or OpenSSL respectively.
274309
pub fn identity(&mut self, identity: Identity) -> &mut Self {
275310
self.identity = Some(identity);
276311
self
@@ -309,6 +344,8 @@ impl ClientTlsConfig {
309344
self.cert.clone(),
310345
self.identity.clone(),
311346
domain,
347+
#[cfg(feature = "openssl-roots")]
348+
self.add_trust_anchors,
312349
),
313350
Some(r) => TlsConnector::new_with_openssl_raw(r.clone(), domain),
314351
},
@@ -318,6 +355,8 @@ impl ClientTlsConfig {
318355
self.cert.clone(),
319356
self.identity.clone(),
320357
domain,
358+
#[cfg(feature = "rustls-roots")]
359+
self.add_trust_anchors,
321360
),
322361
Some(c) => TlsConnector::new_with_rustls_raw(c.clone(), domain),
323362
},

tonic/src/transport/service/tls.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use openssl1::{
66
ssl::{select_next_proto, AlpnError, SslAcceptor, SslConnector, SslMethod, SslVerifyMode},
77
x509::{store::X509StoreBuilder, X509},
88
};
9+
#[cfg(feature = "openssl-roots")]
10+
use openssl_probe;
911
use std::{fmt, sync::Arc};
1012
use tokio::net::TcpStream;
1113
#[cfg(feature = "rustls")]
@@ -14,6 +16,8 @@ use tokio_rustls::{
1416
webpki::DNSNameRef,
1517
TlsAcceptor as RustlsAcceptor, TlsConnector as RustlsConnector,
1618
};
19+
#[cfg(feature = "rustls-roots")]
20+
use webpki_roots;
1721

1822
/// h2 alpn in wire format for openssl.
1923
#[cfg(feature = "openssl")]
@@ -37,6 +41,8 @@ enum TlsError {
3741
CertificateParseError,
3842
#[cfg(feature = "rustls")]
3943
PrivateKeyParseError,
44+
#[cfg(feature = "openssl-roots")]
45+
TrustAnchorsConfigurationError(openssl1::error::ErrorStack),
4046
}
4147

4248
#[derive(Clone)]
@@ -59,10 +65,22 @@ impl TlsConnector {
5965
cert: Option<Certificate>,
6066
identity: Option<Identity>,
6167
domain: String,
68+
add_trust_anchors: bool,
6269
) -> Result<Self, crate::Error> {
6370
let mut config = SslConnector::builder(SslMethod::tls())?;
6471
config.set_alpn_protos(ALPN_H2_WIRE)?;
6572

73+
#[cfg(feature = "openssl-roots")]
74+
{
75+
if add_trust_anchors {
76+
openssl_probe::init_ssl_cert_env_vars();
77+
match config.cert_store_mut().set_default_paths() {
78+
Ok(()) => (),
79+
Err(e) => return Err(Box::new(TlsError::TrustAnchorsConfigurationError(e))),
80+
};
81+
}
82+
}
83+
6684
if let Some(cert) = cert {
6785
let ca = X509::from_pem(&cert.pem[..])?;
6886
config.cert_store_mut().add_cert(ca)?;
@@ -97,6 +115,7 @@ impl TlsConnector {
97115
ca_cert: Option<Certificate>,
98116
identity: Option<Identity>,
99117
domain: String,
118+
add_trust_anchors: bool,
100119
) -> Result<Self, crate::Error> {
101120
let mut config = ClientConfig::new();
102121
config.set_protocols(&[Vec::from(&ALPN_H2[..])]);
@@ -106,6 +125,15 @@ impl TlsConnector {
106125
config.set_single_client_cert(client_cert, client_key);
107126
}
108127

128+
#[cfg(feature = "rustls-roots")]
129+
{
130+
if add_trust_anchors {
131+
config
132+
.root_store
133+
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
134+
}
135+
}
136+
109137
if let Some(cert) = ca_cert {
110138
let mut buf = std::io::Cursor::new(&cert.pem[..]);
111139
config.root_store.add_pem_file(&mut buf).unwrap();
@@ -336,6 +364,10 @@ impl fmt::Display for TlsError {
336364
f,
337365
"Error parsing TLS private key - no RSA or PKCS8-encoded keys found."
338366
),
367+
#[cfg(feature = "openssl-roots")]
368+
TlsError::TrustAnchorsConfigurationError(stack) => {
369+
f.write_fmt(format_args!("Error adding trust anchors - {}", stack))
370+
}
339371
}
340372
}
341373
}

0 commit comments

Comments
 (0)