mirror of
https://github.com/deuxfleurs-org/garage.git
synced 2026-08-21 10:26:36 +00:00
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:
+5
-6
@@ -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
@@ -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
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user