TLS works \o/

So, the issues were:
- webpki does not support IP addresses as DNS names in URLs,
  so I hacked the HttpsConnector to always provide a fixed string
  as the DNS name for server certificate validation
- the certificate requied a SAN section which was complicated to build
  but eventually the solution is there in genkeys.sh
This commit is contained in:
Alex Auvolat
2020-04-12 19:00:30 +02:00
parent d1e8f78b2c
commit d2814b5c33
6 changed files with 190 additions and 105 deletions
+5 -6
View File
@@ -8,7 +8,6 @@ use futures::stream::StreamExt;
use futures_util::future::FutureExt;
use hyper::client::{Client, HttpConnector};
use hyper::{Body, Method, Request, StatusCode};
use hyper_rustls::HttpsConnector;
use crate::data::*;
use crate::error::Error;
@@ -93,7 +92,7 @@ pub async fn rpc_call(
pub enum RpcClient {
HTTP(Client<HttpConnector, hyper::Body>),
HTTPS(Client<HttpsConnector<HttpConnector>, hyper::Body>),
HTTPS(Client<tls_util::HttpsConnectorFixedDnsname<HttpConnector>, hyper::Body>),
}
impl RpcClient {
@@ -109,12 +108,11 @@ impl RpcClient {
config.root_store.add(crt)?;
}
config.set_single_client_cert([&ca_certs[..], &node_certs[..]].concat(), node_key)?;
config.set_single_client_cert([&node_certs[..], &ca_certs[..]].concat(), node_key)?;
// config.dangerous().set_certificate_verifier(Arc::new(tls_util::NoHostnameCertVerifier));
let mut http_connector = HttpConnector::new();
http_connector.enforce_http(false);
let connector =
HttpsConnector::<HttpConnector>::from((http_connector, Arc::new(config)));
tls_util::HttpsConnectorFixedDnsname::<HttpConnector>::new(config, "garage");
Ok(RpcClient::HTTPS(Client::builder().build(connector)))
} else {
@@ -161,3 +159,4 @@ impl RpcClient {
}
}
}
+1 -1
View File
@@ -120,7 +120,7 @@ pub async fn run_rpc_server(
let mut config =
rustls::ServerConfig::new(rustls::AllowAnyAuthenticatedClient::new(ca_store));
config.set_single_cert([&ca_certs[..], &node_certs[..]].concat(), node_key)?;
config.set_single_cert([&node_certs[..], &ca_certs[..]].concat(), node_key)?;
let tls_acceptor = Arc::new(TlsAcceptor::from(Arc::new(config)));
let mut listener = TcpListener::bind(&bind_addr).await?;
+152
View File
@@ -1,6 +1,20 @@
use std::{fs, io};
use core::task::{Poll, Context};
use std::pin::Pin;
use std::sync::Arc;
use core::future::Future;
use futures_util::future::*;
use tokio::io::{AsyncRead, AsyncWrite};
use rustls::internal::pemfile;
use rustls::*;
use hyper::client::HttpConnector;
use hyper::client::connect::Connection;
use hyper::service::Service;
use hyper::Uri;
use hyper_rustls::MaybeHttpsStream;
use tokio_rustls::TlsConnector;
use webpki::DNSNameRef;
use crate::error::Error;
@@ -44,3 +58,141 @@ pub fn load_private_key(filename: &str) -> Result<rustls::PrivateKey, Error> {
}
Ok(keys[0].clone())
}
// ---- AWFUL COPYPASTA FROM rustls/verifier.rs
// ---- USED TO ALLOW TO VERIFY SERVER CERTIFICATE VALIDITY IN CHAIN
// ---- BUT DISREGARD HOSTNAME PARAMETER
pub struct NoHostnameCertVerifier;
type SignatureAlgorithms = &'static [&'static webpki::SignatureAlgorithm];
static SUPPORTED_SIG_ALGS: SignatureAlgorithms = &[
&webpki::ECDSA_P256_SHA256,
&webpki::ECDSA_P256_SHA384,
&webpki::ECDSA_P384_SHA256,
&webpki::ECDSA_P384_SHA384,
&webpki::RSA_PSS_2048_8192_SHA256_LEGACY_KEY,
&webpki::RSA_PSS_2048_8192_SHA384_LEGACY_KEY,
&webpki::RSA_PSS_2048_8192_SHA512_LEGACY_KEY,
&webpki::RSA_PKCS1_2048_8192_SHA256,
&webpki::RSA_PKCS1_2048_8192_SHA384,
&webpki::RSA_PKCS1_2048_8192_SHA512,
&webpki::RSA_PKCS1_3072_8192_SHA384
];
impl rustls::ServerCertVerifier for NoHostnameCertVerifier {
fn verify_server_cert(&self,
roots: &RootCertStore,
presented_certs: &[Certificate],
_dns_name: webpki::DNSNameRef,
_ocsp_response: &[u8]) -> Result<rustls::ServerCertVerified, TLSError> {
if presented_certs.is_empty() {
return Err(TLSError::NoCertificatesPresented);
}
let cert = webpki::EndEntityCert::from(&presented_certs[0].0)
.map_err(TLSError::WebPKIError)?;
let chain = presented_certs.iter()
.skip(1)
.map(|cert| cert.0.as_ref())
.collect::<Vec<_>>();
let trustroots: Vec<webpki::TrustAnchor> = roots.roots
.iter()
.map(|x| x.to_trust_anchor())
.collect();
let now = webpki::Time::try_from(std::time::SystemTime::now())
.map_err( |_ | TLSError::FailedToGetCurrentTime)?;
cert.verify_is_valid_tls_server_cert(SUPPORTED_SIG_ALGS,
&webpki::TLSServerTrustAnchors(&trustroots), &chain, now)
.map_err(TLSError::WebPKIError)?;
Ok(rustls::ServerCertVerified::assertion())
}
}
// ---- AWFUL COPYPASTA FROM HYPER-RUSTLS connector.rs
// ---- ALWAYS USE `garage` AS HOSTNAME FOR TLS VERIFICATION
#[derive(Clone)]
pub struct HttpsConnectorFixedDnsname<T> {
http: T,
tls_config: Arc<rustls::ClientConfig>,
fixed_dnsname: &'static str,
}
type BoxError = Box<dyn std::error::Error + Send + Sync>;
impl HttpsConnectorFixedDnsname<HttpConnector> {
pub fn new(mut tls_config: rustls::ClientConfig, fixed_dnsname: &'static str) -> Self {
let mut http = HttpConnector::new();
http.enforce_http(false);
tls_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
Self {
http,
tls_config: Arc::new(tls_config),
fixed_dnsname,
}
}
}
impl<T> Service<Uri> for HttpsConnectorFixedDnsname<T>
where
T: Service<Uri>,
T::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static,
T::Future: Send + 'static,
T::Error: Into<BoxError>,
{
type Response = MaybeHttpsStream<T::Response>;
type Error = BoxError;
#[allow(clippy::type_complexity)]
type Future =
Pin<Box<dyn Future<Output = Result<MaybeHttpsStream<T::Response>, BoxError>> + Send>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
match self.http.poll_ready(cx) {
Poll::Ready(Ok(())) => Poll::Ready(Ok(())),
Poll::Ready(Err(e)) => Poll::Ready(Err(e.into())),
Poll::Pending => Poll::Pending,
}
}
fn call(&mut self, dst: Uri) -> Self::Future {
let is_https = dst.scheme_str() == Some("https");
if !is_https {
let connecting_future = self.http.call(dst);
let f = async move {
let tcp = connecting_future.await.map_err(Into::into)?;
Ok(MaybeHttpsStream::Http(tcp))
};
f.boxed()
} else {
let cfg = self.tls_config.clone();
let connecting_future = self.http.call(dst);
let dnsname = DNSNameRef::try_from_ascii_str(self.fixed_dnsname)
.expect("Invalid fixed dnsname");
let f = async move {
let tcp = connecting_future.await.map_err(Into::into)?;
let connector = TlsConnector::from(cfg);
let tls = connector
.connect(dnsname, tcp)
.await
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
Ok(MaybeHttpsStream::Https(tls))
};
f.boxed()
}
}
}