diff --git a/examples/src/tls_client_auth/server.rs b/examples/src/tls_client_auth/server.rs index be36abce4..c32c17b9f 100644 --- a/examples/src/tls_client_auth/server.rs +++ b/examples/src/tls_client_auth/server.rs @@ -5,7 +5,7 @@ pub mod pb { use futures::Stream; use pb::{EchoRequest, EchoResponse}; use std::pin::Pin; -use tonic::transport::{Certificate, Identity, Server, ServerTlsConfig}; +use tonic::transport::{Certificate, Identity, Server, ServerTlsConfig, ClientTlsAuth}; use tonic::{Request, Response, Status}; type EchoResult = Result, Status>; @@ -67,7 +67,7 @@ async fn main() -> Result<(), Box> { let tls = ServerTlsConfig::new() .identity(server_identity) - .client_ca_root(client_ca_cert); + .client_auth(ClientTlsAuth::Mandatory(client_ca_cert)); Server::builder() .tls_config(tls)? diff --git a/tonic/src/transport/mod.rs b/tonic/src/transport/mod.rs index e83b4c424..b0e559b75 100644 --- a/tonic/src/transport/mod.rs +++ b/tonic/src/transport/mod.rs @@ -115,6 +115,9 @@ pub use self::channel::ClientTlsConfig; pub use self::server::ServerTlsConfig; #[cfg(feature = "tls")] #[cfg_attr(docsrs, doc(cfg(feature = "tls")))] +pub use self::server::ClientTlsAuth; +#[cfg(feature = "tls")] +#[cfg_attr(docsrs, doc(cfg(feature = "tls")))] pub use self::tls::Identity; type BoxFuture = diff --git a/tonic/src/transport/server/mod.rs b/tonic/src/transport/server/mod.rs index 5fa003afa..2c612123e 100644 --- a/tonic/src/transport/server/mod.rs +++ b/tonic/src/transport/server/mod.rs @@ -14,6 +14,8 @@ pub use crate::server::NamedService; pub use conn::{Connected, TcpConnectInfo}; #[cfg(feature = "tls")] pub use tls::ServerTlsConfig; +#[cfg(feature = "tls")] +pub use tls::ClientTlsAuth; #[cfg(feature = "tls")] pub use conn::TlsConnectInfo; diff --git a/tonic/src/transport/server/tls.rs b/tonic/src/transport/server/tls.rs index b6c7ec974..de7bedbc9 100644 --- a/tonic/src/transport/server/tls.rs +++ b/tonic/src/transport/server/tls.rs @@ -10,7 +10,7 @@ use std::fmt; #[derive(Clone, Default)] pub struct ServerTlsConfig { identity: Option, - client_ca_root: Option, + client_auth: ClientTlsAuth, } #[cfg(feature = "tls")] @@ -26,7 +26,7 @@ impl ServerTlsConfig { pub fn new() -> Self { ServerTlsConfig { identity: None, - client_ca_root: None, + client_auth: ClientTlsAuth::None, } } @@ -38,15 +38,29 @@ impl ServerTlsConfig { } } - /// Sets a certificate against which to validate client TLS certificates. - pub fn client_ca_root(self, cert: Certificate) -> Self { + /// Sets the [`ClientTlsAuth`] approach. + pub fn client_auth(self, client_auth: ClientTlsAuth) -> Self { ServerTlsConfig { - client_ca_root: Some(cert), + client_auth, ..self } } pub(crate) fn tls_acceptor(&self) -> Result { - TlsAcceptor::new(self.identity.clone().unwrap(), self.client_ca_root.clone()) + TlsAcceptor::new(self.identity.clone().unwrap(), self.client_auth.clone()) } } + +/// Client TLS authentication options. +#[cfg(feature = "tls")] +#[cfg_attr(docsrs, doc(cfg(feature = "tls")))] +#[derive(Clone, Debug, Default)] +pub enum ClientTlsAuth { + /// No client authentication. + #[default] + None, + /// Optional client authentication with a provided root CA certificate. + Optional(Certificate), + /// Mandatory client authentication with a provided root CA certificate. + Mandatory(Certificate), +} diff --git a/tonic/src/transport/service/tls.rs b/tonic/src/transport/service/tls.rs index 38512985d..8ac1d054c 100644 --- a/tonic/src/transport/service/tls.rs +++ b/tonic/src/transport/service/tls.rs @@ -1,6 +1,6 @@ use super::io::BoxedIo; use crate::transport::{ - server::{Connected, TlsStream}, + server::{Connected, TlsStream, ClientTlsAuth}, Certificate, Identity, }; #[cfg(feature = "tls-roots")] @@ -126,13 +126,19 @@ impl TlsAcceptor { #[cfg(feature = "tls")] pub(crate) fn new( identity: Identity, - client_ca_root: Option, + client_auth: ClientTlsAuth ) -> Result { let builder = ServerConfig::builder().with_safe_defaults(); - let builder = match client_ca_root { - None => builder.with_no_client_auth(), - Some(cert) => { + let builder = match client_auth { + ClientTlsAuth::None => builder.with_no_client_auth(), + ClientTlsAuth::Optional(cert) => { + use tokio_rustls::rustls::server::AllowAnyAnonymousOrAuthenticatedClient; + let mut roots = RootCertStore::empty(); + rustls_keys::add_certs_from_pem(std::io::Cursor::new(&cert.pem[..]), &mut roots)?; + builder.with_client_cert_verifier(AllowAnyAnonymousOrAuthenticatedClient::new(roots)) + } + ClientTlsAuth::Mandatory(cert) => { 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)?;