feat(tls): upgrade to tokio-rustls 0.23 (rustls 0.20) (#859)

This commit is contained in:
Dirkjan Ochtman
2022-01-12 21:09:30 +01:00
committed by GitHub
parent daf406dd2b
commit 4548997080
6 changed files with 87 additions and 128 deletions
+5 -4
View File
@@ -27,7 +27,7 @@ codegen = ["async-trait"]
compression = ["flate2"]
default = ["transport", "codegen", "prost"]
prost = ["prost1", "prost-derive"]
tls = ["transport", "tokio-rustls"]
tls = ["rustls-pemfile", "transport", "tokio-rustls"]
tls-roots = ["tls-roots-common", "rustls-native-certs"]
tls-roots-common = ["tls"]
tls-webpki-roots = ["tls-roots-common", "webpki-roots"]
@@ -79,9 +79,10 @@ tower = {version = "0.4.7", features = ["balance", "buffer", "discover", "limit"
tracing-futures = {version = "0.2", optional = true}
# rustls
rustls-native-certs = {version = "0.5", optional = true}
tokio-rustls = {version = "0.22", optional = true}
webpki-roots = {version = "0.21.1", optional = true}
rustls-pemfile = { version = "0.2.1", optional = true }
rustls-native-certs = { version = "0.6.1", optional = true }
tokio-rustls = { version = "0.23.1", optional = true }
webpki-roots = { version = "0.22.1", optional = true }
# compression
flate2 = {version = "1.0", optional = true}
+1 -22
View File
@@ -14,7 +14,6 @@ pub struct ClientTlsConfig {
domain: Option<String>,
cert: Option<Certificate>,
identity: Option<Identity>,
rustls_raw: Option<tokio_rustls::rustls::ClientConfig>,
}
#[cfg(feature = "tls")]
@@ -36,7 +35,6 @@ impl ClientTlsConfig {
domain: None,
cert: None,
identity: None,
rustls_raw: None,
}
}
@@ -49,8 +47,6 @@ impl ClientTlsConfig {
}
/// Sets the CA Certificate against which to verify the server's TLS certificate.
///
/// This has no effect if `rustls_client_config` is used to configure Rustls.
pub fn ca_certificate(self, ca_certificate: Certificate) -> Self {
ClientTlsConfig {
cert: Some(ca_certificate),
@@ -59,8 +55,6 @@ impl ClientTlsConfig {
}
/// Sets the client identity to present to the server.
///
/// This has no effect if `rustls_client_config` is used to configure Rustls.
pub fn identity(self, identity: Identity) -> Self {
ClientTlsConfig {
identity: Some(identity),
@@ -68,26 +62,11 @@ impl ClientTlsConfig {
}
}
/// Use options specified by the given `ClientConfig` to configure TLS.
///
/// This overrides all other TLS options set via other means.
pub fn rustls_client_config(self, config: tokio_rustls::rustls::ClientConfig) -> Self {
ClientTlsConfig {
rustls_raw: Some(config),
..self
}
}
pub(crate) fn tls_connector(&self, uri: Uri) -> Result<TlsConnector, crate::Error> {
let domain = match &self.domain {
None => uri.host().ok_or_else(Error::new_invalid_uri)?.to_string(),
Some(domain) => domain.clone(),
};
match &self.rustls_raw {
None => {
TlsConnector::new_with_rustls_cert(self.cert.clone(), self.identity.clone(), domain)
}
Some(c) => TlsConnector::new_with_rustls_raw(c.clone(), domain),
}
TlsConnector::new(self.cert.clone(), self.identity.clone(), domain)
}
}
+3 -3
View File
@@ -7,7 +7,7 @@ use crate::transport::Certificate;
#[cfg(feature = "tls")]
use std::sync::Arc;
#[cfg(feature = "tls")]
use tokio_rustls::{rustls::Session, server::TlsStream};
use tokio_rustls::server::TlsStream;
/// Trait that connected IO resources implement and use to produce info about the connection.
///
@@ -115,10 +115,10 @@ where
let (inner, session) = self.get_ref();
let inner = inner.connect_info();
let certs = if let Some(certs) = session.get_peer_certificates() {
let certs = if let Some(certs) = session.peer_certificates() {
let certs = certs
.into_iter()
.map(|c| Certificate::from_pem(c.0))
.map(|c| Certificate::from_pem(c))
.collect();
Some(Arc::new(certs))
} else {
+1 -20
View File
@@ -11,7 +11,6 @@ use std::fmt;
pub struct ServerTlsConfig {
identity: Option<Identity>,
client_ca_root: Option<Certificate>,
rustls_raw: Option<tokio_rustls::rustls::ServerConfig>,
}
#[cfg(feature = "tls")]
@@ -28,7 +27,6 @@ impl ServerTlsConfig {
ServerTlsConfig {
identity: None,
client_ca_root: None,
rustls_raw: None,
}
}
@@ -48,24 +46,7 @@ impl ServerTlsConfig {
}
}
/// Use options specified by the given `ServerConfig` to configure TLS.
///
/// This overrides all other TLS options set via other means.
pub fn rustls_server_config(
&mut self,
config: tokio_rustls::rustls::ServerConfig,
) -> &mut Self {
self.rustls_raw = Some(config);
self
}
pub(crate) fn tls_acceptor(&self) -> Result<TlsAcceptor, crate::Error> {
match &self.rustls_raw {
None => TlsAcceptor::new_with_rustls_identity(
self.identity.clone().unwrap(),
self.client_ca_root.clone(),
),
Some(config) => TlsAcceptor::new_with_rustls_raw(config.clone()),
}
TlsAcceptor::new(self.identity.clone().unwrap(), self.client_ca_root.clone())
}
}
+10 -12
View File
@@ -3,6 +3,8 @@ use super::io::BoxedIo;
#[cfg(feature = "tls")]
use super::tls::TlsConnector;
use http::Uri;
#[cfg(feature = "tls-roots-common")]
use std::convert::TryInto;
use std::task::{Context, Poll};
use tower::make::MakeConnection;
use tower_service::Service;
@@ -39,22 +41,18 @@ impl<C> Connector<C> {
#[cfg(feature = "tls-roots-common")]
fn tls_or_default(&self, scheme: Option<&str>, host: Option<&str>) -> Option<TlsConnector> {
use tokio_rustls::webpki::DNSNameRef;
if self.tls.is_some() {
return self.tls.clone();
}
match (scheme, host) {
(Some("https"), Some(host)) => {
if DNSNameRef::try_from_ascii(host.as_bytes()).is_ok() {
TlsConnector::new_with_rustls_cert(None, None, host.to_owned()).ok()
} else {
None
}
}
_ => None,
}
let host = match (scheme, host) {
(Some("https"), Some(host)) => host,
_ => return None,
};
host.try_into()
.ok()
.and_then(|dns| TlsConnector::new(None, None, dns).ok())
}
}
+67 -67
View File
@@ -5,12 +5,13 @@ use crate::transport::{
};
#[cfg(feature = "tls-roots")]
use rustls_native_certs;
#[cfg(feature = "tls")]
use std::convert::TryInto;
use std::{fmt, sync::Arc};
use tokio::io::{AsyncRead, AsyncWrite};
#[cfg(feature = "tls")]
use tokio_rustls::{
rustls::{ClientConfig, NoClientAuth, ServerConfig, Session},
webpki::DNSNameRef,
rustls::{ClientConfig, RootCertStore, ServerConfig, ServerName},
TlsAcceptor as RustlsAcceptor, TlsConnector as RustlsConnector,
};
@@ -31,58 +32,59 @@ enum TlsError {
#[derive(Clone)]
pub(crate) struct TlsConnector {
config: Arc<ClientConfig>,
domain: Arc<String>,
domain: Arc<ServerName>,
}
impl TlsConnector {
#[cfg(feature = "tls")]
pub(crate) fn new_with_rustls_cert(
pub(crate) fn new(
ca_cert: Option<Certificate>,
identity: Option<Identity>,
domain: String,
) -> Result<Self, crate::Error> {
let mut config = ClientConfig::new();
config.set_protocols(&[Vec::from(ALPN_H2)]);
if let Some(identity) = identity {
let (client_cert, client_key) = rustls_keys::load_identity(identity)?;
config.set_single_client_cert(client_cert, client_key)?;
}
let builder = ClientConfig::builder().with_safe_defaults();
let mut roots = RootCertStore::empty();
#[cfg(feature = "tls-roots")]
{
config.root_store = match rustls_native_certs::load_native_certs() {
Ok(store) | Err((Some(store), _)) => store,
Err((None, error)) => return Err(error.into()),
match rustls_native_certs::load_native_certs() {
Ok(certs) => roots.add_parsable_certificates(
&certs.into_iter().map(|cert| cert.0).collect::<Vec<_>>(),
),
Err(error) => return Err(error.into()),
};
}
#[cfg(feature = "tls-webpki-roots")]
{
config
.root_store
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
use tokio_rustls::rustls::OwnedTrustAnchor;
roots.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| {
OwnedTrustAnchor::from_subject_spki_name_constraints(
ta.subject,
ta.spki,
ta.name_constraints,
)
}));
}
if let Some(cert) = ca_cert {
let mut buf = std::io::Cursor::new(&cert.pem[..]);
config.root_store.add_pem_file(&mut buf).unwrap();
rustls_keys::add_certs_from_pem(std::io::Cursor::new(&cert.pem[..]), &mut roots)?;
}
Ok(Self {
config: Arc::new(config),
domain: Arc::new(domain),
})
}
let builder = builder.with_root_certificates(roots);
let mut config = match identity {
Some(identity) => {
let (client_cert, client_key) = rustls_keys::load_identity(identity)?;
builder.with_single_cert(client_cert, client_key)?
}
None => builder.with_no_client_auth(),
};
#[cfg(feature = "tls")]
pub(crate) fn new_with_rustls_raw(
config: tokio_rustls::rustls::ClientConfig,
domain: String,
) -> Result<Self, crate::Error> {
config.alpn_protocols.push(ALPN_H2.as_bytes().to_vec());
Ok(Self {
config: Arc::new(config),
domain: Arc::new(domain),
domain: Arc::new(domain.as_str().try_into()?),
})
}
@@ -91,15 +93,13 @@ impl TlsConnector {
I: AsyncRead + AsyncWrite + Send + Unpin + 'static,
{
let tls_io = {
let dns = DNSNameRef::try_from_ascii_str(self.domain.as_str())?.to_owned();
let io = RustlsConnector::from(self.config.clone())
.connect(dns.as_ref(), io)
.connect(self.domain.as_ref().to_owned(), io)
.await?;
let (_, session) = io.get_ref();
match session.get_alpn_protocol() {
match session.alpn_protocol() {
Some(b) if b == b"h2" => (),
_ => return Err(TlsError::H2NotNegotiated.into()),
};
@@ -124,39 +124,26 @@ pub(crate) struct TlsAcceptor {
impl TlsAcceptor {
#[cfg(feature = "tls")]
pub(crate) fn new_with_rustls_identity(
pub(crate) fn new(
identity: Identity,
client_ca_root: Option<Certificate>,
) -> Result<Self, crate::Error> {
let (cert, key) = rustls_keys::load_identity(identity)?;
let builder = ServerConfig::builder().with_safe_defaults();
let mut config = match client_ca_root {
None => ServerConfig::new(NoClientAuth::new()),
let builder = match client_ca_root {
None => builder.with_no_client_auth(),
Some(cert) => {
let mut cert = std::io::Cursor::new(&cert.pem[..]);
let mut client_root_cert_store = tokio_rustls::rustls::RootCertStore::empty();
if client_root_cert_store.add_pem_file(&mut cert).is_err() {
return Err(Box::new(TlsError::CertificateParseError));
}
let client_auth =
tokio_rustls::rustls::AllowAnyAuthenticatedClient::new(client_root_cert_store);
ServerConfig::new(client_auth)
use tokio_rustls::rustls::server::AllowAnyAuthenticatedClient;
let mut roots = RootCertStore::empty();
rustls_keys::add_certs_from_pem(std::io::Cursor::new(&cert.pem[..]), &mut roots)?;
builder.with_client_cert_verifier(AllowAnyAuthenticatedClient::new(roots))
}
};
config.set_single_cert(cert, key)?;
config.set_protocols(&[Vec::from(ALPN_H2)]);
Ok(Self {
inner: Arc::new(config),
})
}
let (cert, key) = rustls_keys::load_identity(identity)?;
let mut config = builder.with_single_cert(cert, key)?;
#[cfg(feature = "tls")]
pub(crate) fn new_with_rustls_raw(
config: tokio_rustls::rustls::ServerConfig,
) -> Result<Self, crate::Error> {
config.alpn_protocols.push(ALPN_H2.as_bytes().to_vec());
Ok(Self {
inner: Arc::new(config),
})
@@ -194,7 +181,9 @@ impl std::error::Error for TlsError {}
#[cfg(feature = "tls")]
mod rustls_keys {
use tokio_rustls::rustls::{internal::pemfile, Certificate, PrivateKey};
use std::io::Cursor;
use tokio_rustls::rustls::{Certificate, PrivateKey, RootCertStore};
use crate::transport::service::tls::TlsError;
use crate::transport::Identity;
@@ -203,17 +192,17 @@ mod rustls_keys {
mut cursor: std::io::Cursor<&[u8]>,
) -> Result<PrivateKey, crate::Error> {
// First attempt to load the private key assuming it is PKCS8-encoded
if let Ok(mut keys) = pemfile::pkcs8_private_keys(&mut cursor) {
if !keys.is_empty() {
return Ok(keys.remove(0));
if let Ok(mut keys) = rustls_pemfile::pkcs8_private_keys(&mut cursor) {
if let Some(key) = keys.pop() {
return Ok(PrivateKey(key));
}
}
// If it not, try loading the private key as an RSA key
cursor.set_position(0);
if let Ok(mut keys) = pemfile::rsa_private_keys(&mut cursor) {
if !keys.is_empty() {
return Ok(keys.remove(0));
if let Ok(mut keys) = rustls_pemfile::rsa_private_keys(&mut cursor) {
if let Some(key) = keys.pop() {
return Ok(PrivateKey(key));
}
}
@@ -226,8 +215,8 @@ mod rustls_keys {
) -> Result<(Vec<Certificate>, PrivateKey), crate::Error> {
let cert = {
let mut cert = std::io::Cursor::new(&identity.cert.pem[..]);
match pemfile::certs(&mut cert) {
Ok(certs) => certs,
match rustls_pemfile::certs(&mut cert) {
Ok(certs) => certs.into_iter().map(Certificate).collect(),
Err(_) => return Err(Box::new(TlsError::CertificateParseError)),
}
};
@@ -244,4 +233,15 @@ mod rustls_keys {
Ok((cert, key))
}
pub(crate) fn add_certs_from_pem(
mut certs: Cursor<&[u8]>,
roots: &mut RootCertStore,
) -> Result<(), crate::Error> {
let (_, ignored) = roots.add_parsable_certificates(&rustls_pemfile::certs(&mut certs)?);
match ignored == 0 {
true => Ok(()),
false => Err(Box::new(TlsError::CertificateParseError)),
}
}
}