Skip to content

Commit

Permalink
Add client TLS builder method to add trust anchors
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jen20 committed Nov 4, 2019
1 parent 46bb2bf commit 4b49811
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 0 deletions.
4 changes: 4 additions & 0 deletions tonic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ transport = [
]
openssl = ["openssl1", "tokio-openssl", "tls"]
rustls = ["tokio-rustls", "tls"]
openssl-roots = ["openssl-probe"]
rustls-roots = ["webpki-roots"]
tls = []

[[bench]]
Expand Down Expand Up @@ -73,9 +75,11 @@ tower-load = { version = "=0.3.0-alpha.2", optional = true }
# openssl
tokio-openssl = { version = "=0.4.0-alpha.6", optional = true }
openssl1 = { package = "openssl", version = "0.10", optional = true }
openssl-probe = { version = "0.1", optional = true }

# rustls
tokio-rustls = { version = "=0.12.0-alpha.5", optional = true }
webpki-roots = { version = "0.18", optional = true }

[dev-dependencies]
static_assertions = "1.0"
Expand Down
39 changes: 39 additions & 0 deletions tonic/src/transport/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ pub struct ClientTlsConfig {
domain: Option<String>,
cert: Option<Certificate>,
identity: Option<Identity>,
#[cfg(feature = "openssl-roots")]
#[cfg(feature = "rustls-roots")]
add_trust_anchors: bool,
#[cfg(feature = "openssl")]
openssl_raw: Option<openssl1::ssl::SslConnector>,
#[cfg(feature = "rustls")]
Expand Down Expand Up @@ -251,6 +254,9 @@ impl ClientTlsConfig {
domain: None,
cert: None,
identity: None,
#[cfg(feature = "openssl-roots")]
#[cfg(feature = "rustls-roots")]
add_trust_anchors: false,
#[cfg(feature = "openssl")]
openssl_raw: None,
#[cfg(feature = "rustls")]
Expand All @@ -259,18 +265,47 @@ impl ClientTlsConfig {
}

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

/// Add the Mozilla trust anchors from the `webpki_roots` crate to the TLS connector
/// configuration.
///
/// This has no effect if `rustls_client_config` is used to configure Rustls.
#[cfg(feature = "rustls-roots")]
pub fn add_mozilla_trust_anchors(&mut self) -> &mut Self {
self.add_trust_anchors = true;
self
}

/// Add the system trust anchors (discovered via the `openssl_roots` crate) to the
/// TLS connector configuration.
///
/// This has no effect if `openssl_connector` is used to configure Rustls.
#[cfg(feature = "openssl-roots")]
pub fn add_system_trust_anchors(&mut self) -> &mut Self {
self.add_trust_anchors = true;
self
}

/// Sets the CA Certificate against which to verify the server's TLS certificate.
///
/// This has no effect if `rustls_client_config` or `openssl_connector` is used to configure
/// Rustls or OpenSSL respectively.
pub fn ca_certificate(&mut self, ca_certificate: Certificate) -> &mut Self {
self.cert = Some(ca_certificate);
self
}

/// Sets the client identity to present to the server.
///
/// This has no effect if `rustls_client_config` or `openssl_connector` is used to configure
/// Rustls or OpenSSL respectively.
pub fn identity(&mut self, identity: Identity) -> &mut Self {
self.identity = Some(identity);
self
Expand Down Expand Up @@ -309,6 +344,8 @@ impl ClientTlsConfig {
self.cert.clone(),
self.identity.clone(),
domain,
#[cfg(feature = "openssl-roots")]
self.add_trust_anchors,
),
Some(r) => TlsConnector::new_with_openssl_raw(r.clone(), domain),
},
Expand All @@ -318,6 +355,8 @@ impl ClientTlsConfig {
self.cert.clone(),
self.identity.clone(),
domain,
#[cfg(feature = "rustls-roots")]
self.add_trust_anchors,
),
Some(c) => TlsConnector::new_with_rustls_raw(c.clone(), domain),
},
Expand Down
32 changes: 32 additions & 0 deletions tonic/src/transport/service/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use openssl1::{
ssl::{select_next_proto, AlpnError, SslAcceptor, SslConnector, SslMethod, SslVerifyMode},
x509::{store::X509StoreBuilder, X509},
};
#[cfg(feature = "openssl-roots")]
use openssl_probe;
use std::{fmt, sync::Arc};
use tokio::net::TcpStream;
#[cfg(feature = "rustls")]
Expand All @@ -14,6 +16,8 @@ use tokio_rustls::{
webpki::DNSNameRef,
TlsAcceptor as RustlsAcceptor, TlsConnector as RustlsConnector,
};
#[cfg(feature = "rustls-roots")]
use webpki_roots;

/// h2 alpn in wire format for openssl.
#[cfg(feature = "openssl")]
Expand All @@ -37,6 +41,8 @@ enum TlsError {
CertificateParseError,
#[cfg(feature = "rustls")]
PrivateKeyParseError,
#[cfg(feature = "openssl-roots")]
TrustAnchorsConfigurationError(openssl1::error::ErrorStack),
}

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

#[cfg(feature = "openssl-roots")]
{
if add_trust_anchors {
openssl_probe::init_ssl_cert_env_vars();
match config.cert_store_mut().set_default_paths() {
Ok(()) => (),
Err(e) => return Err(Box::new(TlsError::TrustAnchorsConfigurationError(e))),
};
}
}

if let Some(cert) = cert {
let ca = X509::from_pem(&cert.pem[..])?;
config.cert_store_mut().add_cert(ca)?;
Expand Down Expand Up @@ -97,6 +115,7 @@ impl TlsConnector {
ca_cert: Option<Certificate>,
identity: Option<Identity>,
domain: String,
add_trust_anchors: bool,
) -> Result<Self, crate::Error> {
let mut config = ClientConfig::new();
config.set_protocols(&[Vec::from(&ALPN_H2[..])]);
Expand All @@ -106,6 +125,15 @@ impl TlsConnector {
config.set_single_client_cert(client_cert, client_key);
}

#[cfg(feature = "rustls-roots")]
{
if add_trust_anchors {
config
.root_store
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
}
}

if let Some(cert) = ca_cert {
let mut buf = std::io::Cursor::new(&cert.pem[..]);
config.root_store.add_pem_file(&mut buf).unwrap();
Expand Down Expand Up @@ -336,6 +364,10 @@ impl fmt::Display for TlsError {
f,
"Error parsing TLS private key - no RSA or PKCS8-encoded keys found."
),
#[cfg(feature = "openssl-roots")]
TlsError::TrustAnchorsConfigurationError(stack) => {
f.write_fmt(format_args!("Error adding trust anchors - {}", stack))
}
}
}
}
Expand Down

0 comments on commit 4b49811

Please sign in to comment.