diff options
author | Alex Auvolat <alex@adnab.me> | 2020-04-12 19:00:30 +0200 |
---|---|---|
committer | Alex Auvolat <alex@adnab.me> | 2020-04-12 19:00:30 +0200 |
commit | d2814b5c3374f8b99a81dbb9fa3614c875cfc5e6 (patch) | |
tree | 08309e6d85dea5c28f4c12df151ed1b3bdb6bec9 | |
parent | d1e8f78b2cd28f4514ad6f7d54aae6aaa4ef3f15 (diff) | |
download | garage-d2814b5c3374f8b99a81dbb9fa3614c875cfc5e6.tar.gz garage-d2814b5c3374f8b99a81dbb9fa3614c875cfc5e6.zip |
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
-rw-r--r-- | Cargo.lock | 78 | ||||
-rw-r--r-- | Cargo.toml | 6 | ||||
-rwxr-xr-x | genkeys.sh | 46 | ||||
-rw-r--r-- | src/rpc_client.rs | 11 | ||||
-rw-r--r-- | src/rpc_server.rs | 2 | ||||
-rw-r--r-- | src/tls_util.rs | 152 |
6 files changed, 190 insertions, 105 deletions
@@ -116,20 +116,6 @@ dependencies = [ ] [[package]] -name = "core-foundation" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "core-foundation-sys" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] name = "crc32fast" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -162,14 +148,6 @@ dependencies = [ ] [[package]] -name = "ct-logs" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "digest" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -455,12 +433,10 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ct-logs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "rustls 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustls-native-certs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-rustls 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "webpki 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -640,11 +616,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "openssl-probe" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] name = "parking_lot" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -842,17 +813,6 @@ dependencies = [ ] [[package]] -name = "rustls-native-certs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustls 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", - "schannel 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "rustversion" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -868,15 +828,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "schannel" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -891,27 +842,6 @@ dependencies = [ ] [[package]] -name = "security-framework" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "security-framework-sys" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "serde" version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1321,12 +1251,9 @@ dependencies = [ "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" -"checksum core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" "checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" "checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" -"checksum ct-logs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4d3686f5fa27dbc1d76c751300376e167c5a43387f44bb451fd1c24776e49113" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum err-derive 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "82f46c91bbed409ee74495549acbfcc7fae856e712e1df15afe75d0775eedc6c" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" @@ -1376,7 +1303,6 @@ dependencies = [ "checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" "checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" "checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" "checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" "checksum pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7804a463a8d9572f13453c516a5faea534a2403d7ced2f0c7e100eeff072772c" @@ -1400,14 +1326,10 @@ dependencies = [ "checksum rmp 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)" = "0f10b46df14cf1ee1ac7baa4d2fbc2c52c0622a4b82fa8740e37bc452ac0184f" "checksum rmp-serde 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4c1ee98f14fe8b8e9c5ea13d25da7b2a1796169202c57a09d7288de90d56222b" "checksum rustls 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d4a31f5d68413404705d6982529b0e11a9aacd4839d1d6222ee3b8cb4015e1" -"checksum rustls-native-certs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a75ffeb84a6bd9d014713119542ce415db3a3e4748f0bfce1e1416cd224a23a5" "checksum rustversion 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6" "checksum ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" -"checksum schannel 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "039c25b130bd8c1321ee2d7de7fde2659fa9c2744e4bb29711cfc852ea53cd19" "checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" "checksum sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" -"checksum security-framework 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "572dfa3a0785509e7a44b5b4bebcf94d41ba34e9ed9eb9df722545c3b3c4144a" -"checksum security-framework-sys 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8ddb15a5fec93b7021b8a9e96009c5d8d51c15673569f7c0f6b7204e5b7b404f" "checksum serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" "checksum serde_bytes 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "325a073952621257820e7a3469f55ba4726d8b28657e7e36653d1c36dc2c84ae" "checksum serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" @@ -29,11 +29,11 @@ sha2 = "0.8" async-trait = "0.1.30" reduce = "0.1.2" serde_json = "1.0" + rustls = "0.17" -tokio-rustls = "0.13" +tokio-rustls = { version = "0.13", features = ["dangerous_configuration"] } +hyper-rustls = { version = "0.20", default-features = false } webpki = "0.21" -hyper-rustls = "0.20" -#hyper-rustls = { version = "0.4", default-features = false } [profile.dev] lto = "off" @@ -10,27 +10,39 @@ cd pki if [ ! -f garage-ca.key ]; then echo "Generating Garage CA keys..." openssl genrsa -out garage-ca.key 4096 - openssl req -x509 -new -key garage-ca.key -subj "/C=FR/O=Garage" -days 3650 -out garage-ca.crt + openssl req -x509 -new -nodes -key garage-ca.key -sha256 -days 3650 -out garage-ca.crt -subj "/C=FR/O=Garage" fi -if [ ! -f garage.key ]; then - echo "Generating Garage agent keys..." - openssl genrsa -out garage.key 4096 - openssl req -new -sha256 -key garage.key -subj "/C=FR/O=Garage/CN=*" -out garage.csr + +if [ ! -f garage.crt ]; then + echo "Generating Garage agent keys..." + if [ ! -f garage.key ]; then + openssl genrsa -out garage.key 4096 + fi + openssl req -new -sha256 -key garage.key -subj "/C=FR/O=Garage/CN=garage" \ + -out garage.csr openssl req -in garage.csr -noout -text openssl x509 -req -in garage.csr \ - -CA garage-ca.crt -CAkey garage-ca.key -CAcreateserial \ - -out garage.crt -days 365 -sha256 - rm garage.csr -fi + -extensions v3_req \ + -extfile <(cat <<EOF +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req +prompt = no + +[req_distinguished_name] +C = FR +O = Garage +CN = garage -if [ ! -f garage-client.key ]; then - echo "Generating Garage client key..." - openssl genrsa -out garage-client.key 4096 - openssl req -new -sha256 -key garage-client.key -subj "/C=FR/O=Garage" -out garage-client.csr - openssl req -in garage-client.csr -noout -text - openssl x509 -req -in garage-client.csr \ +[v3_req] +keyUsage = keyEncipherment, dataEncipherment +extendedKeyUsage = serverAuth, clientAuth +subjectAltName = @alt_names +[alt_names] +DNS.1 = garage +EOF +) \ -CA garage-ca.crt -CAkey garage-ca.key -CAcreateserial \ - -out garage-client.crt -days 365 -sha256 - rm garage-client.csr + -out garage.crt -days 365 fi diff --git a/src/rpc_client.rs b/src/rpc_client.rs index 247f114e..255eb958 100644 --- a/src/rpc_client.rs +++ b/src/rpc_client.rs @@ -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 { } } } + diff --git a/src/rpc_server.rs b/src/rpc_server.rs index f42d54ac..17da6f86 100644 --- a/src/rpc_server.rs +++ b/src/rpc_server.rs @@ -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?; diff --git a/src/tls_util.rs b/src/tls_util.rs index a9e16c53..5a17d380 100644 --- a/src/tls_util.rs +++ b/src/tls_util.rs @@ -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() + } + } +} |