From 8b3bf760450008e16ca400a48f28c75fb283fc8e Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Wed, 27 Sep 2023 10:01:53 -0400 Subject: [PATCH 1/7] deps: upgrade tungstenite (#1067) --- Cargo.toml | 2 +- src/filters/ws.rs | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 002de45d2..e7533895d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ tokio-stream = "0.1.1" tokio-util = { version = "0.7", features = ["io"] } tracing = { version = "0.1.21", default-features = false, features = ["log", "std"] } tower-service = "0.3" -tokio-tungstenite = { version = "0.18", optional = true } +tokio-tungstenite = { version = "0.20", optional = true } percent-encoding = "2.1" pin-project = "1.0" tokio-rustls = { version = "0.24", optional = true } diff --git a/src/filters/ws.rs b/src/filters/ws.rs index e5ae62c43..81a6273cb 100644 --- a/src/filters/ws.rs +++ b/src/filters/ws.rs @@ -92,11 +92,21 @@ impl Ws { // config - /// Set the size of the internal message send queue. - pub fn max_send_queue(mut self, max: usize) -> Self { + /// Does nothing. + /// + /// # Deprecated + /// + /// Use `max_write_buffer_size()` instead. + #[deprecated = "use max_write_buffer_size instead"] + pub fn max_send_queue(self, _max: usize) -> Self { + self + } + + /// The max size of the write buffer, in bytes. + pub fn max_write_buffer_size(mut self, max: usize) -> Self { self.config .get_or_insert_with(WebSocketConfig::default) - .max_send_queue = Some(max); + .max_write_buffer_size = max; self } From 634b3ba93f7f7b42f381bfeee8368675737bd495 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Wed, 27 Sep 2023 10:10:33 -0400 Subject: [PATCH 2/7] v0.3.6 --- CHANGELOG.md | 10 ++++++++++ Cargo.toml | 2 +- src/lib.rs | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 139a90b83..c0ecb85fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +### v0.3.6 (September 27, 2023) + +- **Features**: + - Add ability to pass `None` to `multipart::form().max_length()`. + - Implement `Reply` for `Result`. + - Make `multipart::Part::content_type()` return the full mime string. + - Add `TlsServer::try_bind_with_graceful_shutdown()`. +- **Fixes**: + - Updated tungstenite and rustls dependencies for security fixes. + ### v0.3.5 (April 28, 2023) - **Fixes**: diff --git a/Cargo.toml b/Cargo.toml index e7533895d..d675ea06f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "warp" -version = "0.3.5" # don't forget to update html_root_url +version = "0.3.6" # don't forget to update html_root_url description = "serve the web at warp speeds" authors = ["Sean McArthur "] license = "MIT" diff --git a/src/lib.rs b/src/lib.rs index 6bc4cf0c9..35ed0dcd2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![doc(html_root_url = "https://docs.rs/warp/0.3.5")] +#![doc(html_root_url = "https://docs.rs/warp/0.3.6")] #![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(rust_2018_idioms)] From 9d081461ae1167eb321585ce424f4fef6cf0092b Mon Sep 17 00:00:00 2001 From: tottoto Date: Thu, 28 Sep 2023 23:25:09 +0900 Subject: [PATCH 3/7] deps: move rustls-pemfile crate to tls feature (#1069) --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d675ea06f..cad28de0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ tokio-tungstenite = { version = "0.20", optional = true } percent-encoding = "2.1" pin-project = "1.0" tokio-rustls = { version = "0.24", optional = true } -rustls-pemfile = "1.0" +rustls-pemfile = { version = "1.0", optional = true } [dev-dependencies] pretty_env_logger = "0.5" @@ -57,7 +57,7 @@ listenfd = "1.0" default = ["multipart", "websocket"] multipart = ["multer"] websocket = ["tokio-tungstenite"] -tls = ["tokio-rustls"] +tls = ["tokio-rustls", "rustls-pemfile"] # Enable compression-related filters compression = ["compression-brotli", "compression-gzip"] From efe8548a19172e69918396d0fdbc369df9d0eb17 Mon Sep 17 00:00:00 2001 From: Jose Quintana <1700322+joseluisq@users.noreply.github.com> Date: Mon, 9 Oct 2023 03:38:39 +0200 Subject: [PATCH 4/7] feat: ecc private keys support for `tls` feature (#1048) It adds support for `ECC` private keys when enabling the `tls` feature. This is particularly useful when generating private keys for example using clients like `Lego ACME` which defaults to `EC256` keys changelog: - ecc private keys support - refactor `TlsConfigError` to reflect intended changes - tokio-rustls 0.24 and subsequent `client_auth` updates --- examples/tls.rs | 4 ++ examples/tls/cert.ecc.pem | 12 ++++++ examples/tls/key.ecc | 5 +++ src/tls.rs | 83 +++++++++++++++++++++++++-------------- 4 files changed, 75 insertions(+), 29 deletions(-) create mode 100644 examples/tls/cert.ecc.pem create mode 100644 examples/tls/key.ecc diff --git a/examples/tls.rs b/examples/tls.rs index 7d28e03a3..3103de9c1 100644 --- a/examples/tls.rs +++ b/examples/tls.rs @@ -13,8 +13,12 @@ async fn main() { warp::serve(routes) .tls() + // RSA .cert_path("examples/tls/cert.pem") .key_path("examples/tls/key.rsa") + // ECC + // .cert_path("examples/tls/cert.ecc.pem") + // .key_path("examples/tls/key.ecc") .run(([127, 0, 0, 1], 3030)) .await; } diff --git a/examples/tls/cert.ecc.pem b/examples/tls/cert.ecc.pem new file mode 100644 index 000000000..f661a6382 --- /dev/null +++ b/examples/tls/cert.ecc.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBtDCCAVoCCQDFz95/8CeJaDAKBggqhkjOPQQDAjBiMQswCQYDVQQGEwJERTEQ +MA4GA1UECAwHR2VybWFueTEQMA4GA1UEBwwHTGVpcHppZzESMBAGA1UEAwwJbG9j +YWwuZGV2MRswGQYJKoZIhvcNAQkBFgxoaUBsb2NhbC5kZXYwHhcNMjMwNTI4MTk0 +NzA4WhcNMjYwNTI3MTk0NzA4WjBiMQswCQYDVQQGEwJERTEQMA4GA1UECAwHR2Vy +bWFueTEQMA4GA1UEBwwHTGVpcHppZzESMBAGA1UEAwwJbG9jYWwuZGV2MRswGQYJ +KoZIhvcNAQkBFgxoaUBsb2NhbC5kZXYwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC +AATZR4F60X+iHjeD6kySZfXljNckDb22QYQ76Ts4GFYWkdDstU6yehxyER+MZWsm +UnTE/Gy3mnpSmMzoSBfoKRmHMAoGCCqGSM49BAMCA0gAMEUCIQChOTwbAYlx6zg0 +yc3Oc+zrNY8Yd8oRUD+cG/wdz+gN/wIgP199zXAPXiYUFFd1CnIYmWJSglaOUbYj +ZP/ixZR9HQs= +-----END CERTIFICATE----- diff --git a/examples/tls/key.ecc b/examples/tls/key.ecc new file mode 100644 index 000000000..9287db76a --- /dev/null +++ b/examples/tls/key.ecc @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIPwp3LAnLEyWe2lLz66Y3QCCJ/BEMJheTM0shZnnSw6toAoGCCqGSM49 +AwEHoUQDQgAE2UeBetF/oh43g+pMkmX15YzXJA29tkGEO+k7OBhWFpHQ7LVOsnoc +chEfjGVrJlJ0xPxst5p6UpjM6EgX6CkZhw== +-----END EC PRIVATE KEY----- diff --git a/src/tls.rs b/src/tls.rs index 96b6ed74e..da324a1c9 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -25,10 +25,12 @@ pub(crate) enum TlsConfigError { Io(io::Error), /// An Error parsing the Certificate CertParseError, - /// An Error parsing a Pkcs8 key - Pkcs8ParseError, - /// An Error parsing a Rsa key - RsaParseError, + /// Identity PEM is invalid + InvalidIdentityPem, + /// Identity PEM is missing a private key such as RSA, ECC or PKCS8 + MissingPrivateKey, + /// Unknown private key format + UnknownPrivateKeyFormat, /// An error from an empty key EmptyKey, /// An error from an invalid key @@ -40,8 +42,12 @@ impl fmt::Display for TlsConfigError { match self { TlsConfigError::Io(err) => err.fmt(f), TlsConfigError::CertParseError => write!(f, "certificate parse error"), - TlsConfigError::Pkcs8ParseError => write!(f, "pkcs8 parse error"), - TlsConfigError::RsaParseError => write!(f, "rsa parse error"), + TlsConfigError::UnknownPrivateKeyFormat => write!(f, "unknown private key format"), + TlsConfigError::MissingPrivateKey => write!( + f, + "Identity PEM is missing a private key such as RSA, ECC or PKCS8" + ), + TlsConfigError::InvalidIdentityPem => write!(f, "identity PEM is invalid"), TlsConfigError::EmptyKey => write!(f, "key contains no private key"), TlsConfigError::InvalidKey(err) => write!(f, "key contains an invalid key, {}", err), } @@ -175,32 +181,30 @@ impl TlsConfigBuilder { .map(Certificate) .collect(); - let key = { - // convert it to Vec to allow reading it again if key is RSA - let mut key_vec = Vec::new(); - self.key - .read_to_end(&mut key_vec) - .map_err(TlsConfigError::Io)?; + let mut key_vec = Vec::new(); + self.key + .read_to_end(&mut key_vec) + .map_err(TlsConfigError::Io)?; - if key_vec.is_empty() { - return Err(TlsConfigError::EmptyKey); - } - - let mut pkcs8 = rustls_pemfile::pkcs8_private_keys(&mut key_vec.as_slice()) - .map_err(|_e| TlsConfigError::Pkcs8ParseError)?; - - if !pkcs8.is_empty() { - PrivateKey(pkcs8.remove(0)) - } else { - let mut rsa = rustls_pemfile::rsa_private_keys(&mut key_vec.as_slice()) - .map_err(|_e| TlsConfigError::RsaParseError)?; + if key_vec.is_empty() { + return Err(TlsConfigError::EmptyKey); + } - if !rsa.is_empty() { - PrivateKey(rsa.remove(0)) - } else { - return Err(TlsConfigError::EmptyKey); - } + let mut key_opt = None; + let mut key_cur = std::io::Cursor::new(key_vec); + for item in rustls_pemfile::read_all(&mut key_cur) + .map_err(|_e| TlsConfigError::InvalidIdentityPem)? + { + match item { + rustls_pemfile::Item::RSAKey(k) => key_opt = Some(PrivateKey(k)), + rustls_pemfile::Item::PKCS8Key(k) => key_opt = Some(PrivateKey(k)), + rustls_pemfile::Item::ECKey(k) => key_opt = Some(PrivateKey(k)), + _ => return Err(TlsConfigError::UnknownPrivateKeyFormat), } + } + let key = match key_opt { + Some(v) => v, + _ => return Err(TlsConfigError::MissingPrivateKey), }; fn read_trust_anchor( @@ -409,4 +413,25 @@ mod tests { .build() .unwrap(); } + + #[test] + fn file_ecc_cert_key() { + TlsConfigBuilder::new() + .key_path("examples/tls/key.ecc") + .cert_path("examples/tls/cert.ecc.pem") + .build() + .unwrap(); + } + + #[test] + fn bytes_ecc_cert_key() { + let key = include_str!("../examples/tls/key.ecc"); + let cert = include_str!("../examples/tls/cert.ecc.pem"); + + TlsConfigBuilder::new() + .key(key.as_bytes()) + .cert(cert.as_bytes()) + .build() + .unwrap(); + } } From 9d1cd3c525a084bf9566a730cb3638f97e574897 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Thu, 23 Nov 2023 16:27:22 +0100 Subject: [PATCH 5/7] Fix `cargo minimal-versions check` (#1078) --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cad28de0a..c4ec7a8fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ async-compression = { version = "0.3.7", features = ["tokio"], optional = true } bytes = "1.0" futures-util = { version = "0.3", default-features = false, features = ["sink"] } futures-channel = { version = "0.3.17", features = ["sink"]} -headers = "0.3" +headers = "0.3.5" http = "0.2" hyper = { version = "0.14", features = ["stream", "server", "http1", "http2", "tcp", "client"] } log = "0.4" @@ -31,10 +31,10 @@ multer = { version = "2.1.0", optional = true } scoped-tls = "1.0" serde = "1.0" serde_json = "1.0" -serde_urlencoded = "0.7" +serde_urlencoded = "0.7.1" tokio = { version = "1.0", features = ["fs", "sync", "time"] } tokio-stream = "0.1.1" -tokio-util = { version = "0.7", features = ["io"] } +tokio-util = { version = "0.7.1", features = ["io"] } tracing = { version = "0.1.21", default-features = false, features = ["log", "std"] } tower-service = "0.3" tokio-tungstenite = { version = "0.20", optional = true } From ea61813819888f8517a8c68c6e53fc3d43bb50f1 Mon Sep 17 00:00:00 2001 From: tottoto Date: Mon, 4 Dec 2023 22:37:28 +0900 Subject: [PATCH 6/7] deps: remove unused tokio-stream dependency (#1079) --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c4ec7a8fc..eafbcc658 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,6 @@ serde = "1.0" serde_json = "1.0" serde_urlencoded = "0.7.1" tokio = { version = "1.0", features = ["fs", "sync", "time"] } -tokio-stream = "0.1.1" tokio-util = { version = "0.7.1", features = ["io"] } tracing = { version = "0.1.21", default-features = false, features = ["log", "std"] } tower-service = "0.3" From 2c3581e8387e29bab2ac1aa5f9ae9602fe62339f Mon Sep 17 00:00:00 2001 From: tottoto Date: Wed, 6 Dec 2023 21:36:39 +0900 Subject: [PATCH 7/7] deps: update to rustls 0.22 (#1081) --- Cargo.toml | 4 ++-- src/tls.rs | 62 ++++++++++++++++++++++++++++++------------------------ 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index eafbcc658..72e9e6d1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,8 +39,8 @@ tower-service = "0.3" tokio-tungstenite = { version = "0.20", optional = true } percent-encoding = "2.1" pin-project = "1.0" -tokio-rustls = { version = "0.24", optional = true } -rustls-pemfile = { version = "1.0", optional = true } +tokio-rustls = { version = "0.25", optional = true } +rustls-pemfile = { version = "2.0", optional = true } [dev-dependencies] pretty_env_logger = "0.5" diff --git a/src/tls.rs b/src/tls.rs index da324a1c9..aa7438752 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -12,12 +12,10 @@ use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use futures_util::ready; use hyper::server::accept::Accept; use hyper::server::conn::{AddrIncoming, AddrStream}; +use tokio_rustls::rustls::server::WebPkiClientVerifier; +use tokio_rustls::rustls::{Error as TlsError, RootCertStore, ServerConfig}; use crate::transport::Transport; -use tokio_rustls::rustls::{ - server::{AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, NoClientAuth}, - Certificate, Error as TlsError, PrivateKey, RootCertStore, ServerConfig, -}; /// Represents errors that can occur building the TlsConfig #[derive(Debug)] @@ -176,10 +174,8 @@ impl TlsConfigBuilder { pub(crate) fn build(mut self) -> Result { let mut cert_rdr = BufReader::new(self.cert); let cert = rustls_pemfile::certs(&mut cert_rdr) - .map_err(|_e| TlsConfigError::CertParseError)? - .into_iter() - .map(Certificate) - .collect(); + .collect::, _>>() + .map_err(|_e| TlsConfigError::CertParseError)?; let mut key_vec = Vec::new(); self.key @@ -193,12 +189,13 @@ impl TlsConfigBuilder { let mut key_opt = None; let mut key_cur = std::io::Cursor::new(key_vec); for item in rustls_pemfile::read_all(&mut key_cur) + .collect::, _>>() .map_err(|_e| TlsConfigError::InvalidIdentityPem)? { match item { - rustls_pemfile::Item::RSAKey(k) => key_opt = Some(PrivateKey(k)), - rustls_pemfile::Item::PKCS8Key(k) => key_opt = Some(PrivateKey(k)), - rustls_pemfile::Item::ECKey(k) => key_opt = Some(PrivateKey(k)), + rustls_pemfile::Item::Pkcs1Key(k) => key_opt = Some(k.into()), + rustls_pemfile::Item::Pkcs8Key(k) => key_opt = Some(k.into()), + rustls_pemfile::Item::Sec1Key(k) => key_opt = Some(k.into()), _ => return Err(TlsConfigError::UnknownPrivateKeyFormat), } } @@ -212,11 +209,13 @@ impl TlsConfigBuilder { ) -> Result { let trust_anchors = { let mut reader = BufReader::new(trust_anchor); - rustls_pemfile::certs(&mut reader).map_err(TlsConfigError::Io)? + rustls_pemfile::certs(&mut reader) + .collect::, _>>() + .map_err(TlsConfigError::Io)? }; let mut store = RootCertStore::empty(); - let (added, _skipped) = store.add_parsable_certificates(&trust_anchors); + let (added, _skipped) = store.add_parsable_certificates(trust_anchors); if added == 0 { return Err(TlsConfigError::CertParseError); } @@ -224,23 +223,32 @@ impl TlsConfigBuilder { Ok(store) } - let client_auth = match self.client_auth { - TlsClientAuth::Off => NoClientAuth::boxed(), - TlsClientAuth::Optional(trust_anchor) => { - AllowAnyAnonymousOrAuthenticatedClient::new(read_trust_anchor(trust_anchor)?) - .boxed() - } - TlsClientAuth::Required(trust_anchor) => { - AllowAnyAuthenticatedClient::new(read_trust_anchor(trust_anchor)?).boxed() + let config = { + let builder = ServerConfig::builder(); + let mut config = match self.client_auth { + TlsClientAuth::Off => builder.with_no_client_auth(), + TlsClientAuth::Optional(trust_anchor) => { + let verifier = + WebPkiClientVerifier::builder(read_trust_anchor(trust_anchor)?.into()) + .allow_unauthenticated() + .build() + .map_err(|_| TlsConfigError::CertParseError)?; + builder.with_client_cert_verifier(verifier) + } + TlsClientAuth::Required(trust_anchor) => { + let verifier = + WebPkiClientVerifier::builder(read_trust_anchor(trust_anchor)?.into()) + .build() + .map_err(|_| TlsConfigError::CertParseError)?; + builder.with_client_cert_verifier(verifier) + } } + .with_single_cert_with_ocsp(cert, key, self.ocsp_resp) + .map_err(TlsConfigError::InvalidKey)?; + config.alpn_protocols = vec!["h2".into(), "http/1.1".into()]; + config }; - let mut config = ServerConfig::builder() - .with_safe_defaults() - .with_client_cert_verifier(client_auth) - .with_single_cert_with_ocsp_and_sct(cert, key, self.ocsp_resp, Vec::new()) - .map_err(TlsConfigError::InvalidKey)?; - config.alpn_protocols = vec!["h2".into(), "http/1.1".into()]; Ok(config) } }