diff --git a/CHANGES.md b/CHANGES.md index 1654b0856..cea963ca1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ * Inner field made private on `web::Payload`. [#2384] * `Data::into_inner` and `Data::get_ref` no longer require T: Sized. [#2403] * Minimum supported Rust version (MSRV) is now 1.52. +* Updated rustls to v0.20. [#2414] ### Removed * `ServiceResponse::checked_expr` was a legacy and just removed. [#2401] @@ -17,6 +18,7 @@ [#2362]: https://github.com/actix/actix-web/pull/2362 [#2384]: https://github.com/actix/actix-web/pull/2384 [#2401]: https://github.com/actix/actix-web/pull/2401 +[#2414]: https://github.com/actix/actix-web/pull/2414 ## 4.0.0-beta.9 - 2021-09-09 diff --git a/Cargo.toml b/Cargo.toml index 630ef5642..e53f4411b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,7 +73,7 @@ actix-rt = "2.2" actix-server = "2.0.0-beta.3" actix-service = "2.0.0" actix-utils = "3.0.0" -actix-tls = { version = "3.0.0-beta.5", default-features = false, optional = true } +actix-tls = { version = "3.0.0-beta.6", default-features = false, optional = true } actix-web-codegen = "0.5.0-beta.4" actix-http = "3.0.0-beta.10" @@ -111,11 +111,15 @@ brotli2 = "0.3.2" criterion = { version = "0.3", features = ["html_reports"] } env_logger = "0.8" flate2 = "1.0.13" -zstd = "0.7" +futures-util = { version = "0.3.7", default-features = false, features = ["std"] } rand = "0.8" rcgen = "0.8" +rustls-pemfile = "0.2" tls-openssl = { package = "openssl", version = "0.10.9" } -tls-rustls = { package = "rustls", version = "0.19.0" } +tls-rustls = { package = "rustls", version = "0.20.0" } +webpki = "0.22" +webpki-roots = "0.22" +zstd = "0.7" [profile.dev] # Disabling debug info speeds up builds a bunch and we don't rely on it for debugging that much. diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index ee4971a1e..2bdd6969d 100644 --- a/actix-http-test/Cargo.toml +++ b/actix-http-test/Cargo.toml @@ -31,7 +31,7 @@ openssl = ["tls-openssl", "awc/openssl"] [dependencies] actix-service = "2.0.0" actix-codec = "0.4.0" -actix-tls = "3.0.0-beta.5" +actix-tls = "3.0.0-beta.6" actix-utils = "3.0.0" actix-rt = "2.2" actix-server = "2.0.0-beta.3" diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 71aa8668d..3911fc00a 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,8 +1,12 @@ # Changes ## Unreleased - 2021-xx-xx +### Changed +* Updated rustls to v0.20. [#2414] * Minimum supported Rust version (MSRV) is now 1.52. +[#2414]: https://github.com/actix/actix-web/pull/2414 + ## 3.0.0-beta.10 - 2021-09-09 ### Changed diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 889c91331..d0724ba5f 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -46,7 +46,7 @@ actix-service = "2.0.0" actix-codec = "0.4.0" actix-utils = "3.0.0" actix-rt = "2.2" -actix-tls = { version = "3.0.0-beta.5", features = ["accept", "connect"] } +actix-tls = { version = "3.0.0-beta.6", features = ["accept", "connect"] } ahash = "0.7" base64 = "0.13" @@ -85,17 +85,18 @@ trust-dns-resolver = { version = "0.20.0", optional = true } [dev-dependencies] actix-server = "2.0.0-beta.3" actix-http-test = { version = "3.0.0-beta.5", features = ["openssl"] } -actix-tls = { version = "3.0.0-beta.5", features = ["openssl"] } +actix-tls = { version = "3.0.0-beta.6", features = ["openssl"] } async-stream = "0.3" criterion = { version = "0.3", features = ["html_reports"] } env_logger = "0.8" rcgen = "0.8" regex = "1.3" +rustls-pemfile = "0.2" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -tls-openssl = { version = "0.10", package = "openssl" } -tls-rustls = { version = "0.19", package = "rustls" } -webpki = { version = "0.21" } +tls-openssl = { package = "openssl", version = "0.10.9" } +tls-rustls = { package = "rustls", version = "0.20.0" } +webpki = { version = "0.22" } [[example]] name = "ws" diff --git a/actix-http/examples/ws.rs b/actix-http/examples/ws.rs index d3cedf870..b6be4d2f1 100644 --- a/actix-http/examples/ws.rs +++ b/actix-http/examples/ws.rs @@ -85,22 +85,31 @@ impl Stream for Heartbeat { fn tls_config() -> rustls::ServerConfig { use std::io::BufReader; - use rustls::{ - internal::pemfile::{certs, pkcs8_private_keys}, - NoClientAuth, ServerConfig, - }; + use rustls::{Certificate, PrivateKey}; + use rustls_pemfile::{certs, pkcs8_private_keys}; let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_owned()]).unwrap(); let cert_file = cert.serialize_pem().unwrap(); let key_file = cert.serialize_private_key_pem(); - let mut config = ServerConfig::new(NoClientAuth::new()); let cert_file = &mut BufReader::new(cert_file.as_bytes()); let key_file = &mut BufReader::new(key_file.as_bytes()); - let cert_chain = certs(cert_file).unwrap(); + let cert_chain = certs(cert_file) + .unwrap() + .into_iter() + .map(Certificate) + .collect(); let mut keys = pkcs8_private_keys(key_file).unwrap(); - config.set_single_cert(cert_chain, keys.remove(0)).unwrap(); + + let mut config = rustls::ServerConfig::builder() + .with_safe_defaults() + .with_no_client_auth() + .with_single_cert(cert_chain, PrivateKey(keys.remove(0))) + .unwrap(); + + config.alpn_protocols.push(b"http/1.1".to_vec()); + config.alpn_protocols.push(b"h2".to_vec()); config } diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index bd46919e8..bde5e4853 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -313,18 +313,15 @@ where SslConnector::Rustls(tls) => { const H2: &[u8] = b"h2"; - use actix_tls::connect::ssl::rustls::{ - RustlsConnector, Session, TlsStream, - }; + use actix_tls::connect::ssl::rustls::{RustlsConnector, TlsStream}; impl IntoConnectionIo for TcpConnection> { fn into_connection_io(self) -> (Box, Protocol) { let sock = self.into_parts().0; - let h2 = sock - .get_ref() - .1 - .get_alpn_protocol() - .map_or(false, |protos| protos.windows(2).any(|w| w == H2)); + let h2 = + sock.get_ref().1.alpn_protocol().map_or(false, |protos| { + protos.windows(2).any(|w| w == H2) + }); if h2 { (Box::new(sock), Protocol::Http2) } else { diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index 09e24045b..32dae8ac3 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -177,7 +177,7 @@ mod rustls { > { let mut protos = vec![b"h2".to_vec()]; protos.extend_from_slice(&config.alpn_protocols); - config.set_protocols(&protos); + config.alpn_protocols = protos; Acceptor::new(config) .map_err(TlsError::Tls) diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index afe47bf2d..62c968870 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -263,7 +263,7 @@ mod openssl { mod rustls { use std::io; - use actix_tls::accept::rustls::{Acceptor, ServerConfig, Session, TlsStream}; + use actix_tls::accept::rustls::{Acceptor, ServerConfig, TlsStream}; use actix_tls::accept::TlsError; use super::*; @@ -308,14 +308,13 @@ mod rustls { > { let mut protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; protos.extend_from_slice(&config.alpn_protocols); - config.set_protocols(&protos); + config.alpn_protocols = protos; Acceptor::new(config) .map_err(TlsError::Tls) .map_init_err(|_| panic!()) .and_then(|io: TlsStream| async { - let proto = if let Some(protos) = io.get_ref().1.get_alpn_protocol() - { + let proto = if let Some(protos) = io.get_ref().1.alpn_protocol() { if protos.windows(2).any(|window| window == b"h2") { Protocol::Http2 } else { diff --git a/actix-http/tests/test_rustls.rs b/actix-http/tests/test_rustls.rs index cb7c77ad6..69c7db74d 100644 --- a/actix-http/tests/test_rustls.rs +++ b/actix-http/tests/test_rustls.rs @@ -3,7 +3,7 @@ extern crate tls_rustls as rustls; use std::{ - convert::Infallible, + convert::{Infallible, TryFrom}, io::{self, BufReader, Write}, net::{SocketAddr, TcpStream as StdTcpStream}, sync::Arc, @@ -20,16 +20,17 @@ use actix_http::{ }; use actix_http_test::test_server; use actix_service::{fn_factory_with_config, fn_service}; +use actix_tls::connect::ssl::rustls::TLS_SERVER_ROOTS; use actix_utils::future::{err, ok}; use bytes::{Bytes, BytesMut}; use derive_more::{Display, Error}; use futures_core::Stream; use futures_util::stream::{once, StreamExt as _}; use rustls::{ - internal::pemfile::{certs, pkcs8_private_keys}, - NoClientAuth, ServerConfig as RustlsServerConfig, Session, + Certificate, OwnedTrustAnchor, PrivateKey, RootCertStore, + ServerConfig as RustlsServerConfig, ServerName, }; -use webpki::DNSNameRef; +use rustls_pemfile::{certs, pkcs8_private_keys}; async fn load_body(mut stream: S) -> Result where @@ -47,13 +48,24 @@ fn tls_config() -> RustlsServerConfig { let cert_file = cert.serialize_pem().unwrap(); let key_file = cert.serialize_private_key_pem(); - let mut config = RustlsServerConfig::new(NoClientAuth::new()); let cert_file = &mut BufReader::new(cert_file.as_bytes()); let key_file = &mut BufReader::new(key_file.as_bytes()); - let cert_chain = certs(cert_file).unwrap(); + let cert_chain = certs(cert_file) + .unwrap() + .into_iter() + .map(Certificate) + .collect(); let mut keys = pkcs8_private_keys(key_file).unwrap(); - config.set_single_cert(cert_chain, keys.remove(0)).unwrap(); + + let mut config = RustlsServerConfig::builder() + .with_safe_defaults() + .with_no_client_auth() + .with_single_cert(cert_chain, PrivateKey(keys.remove(0))) + .unwrap(); + + config.alpn_protocols.push(HTTP1_1_ALPN_PROTOCOL.to_vec()); + config.alpn_protocols.push(H2_ALPN_PROTOCOL.to_vec()); config } @@ -62,19 +74,39 @@ pub fn get_negotiated_alpn_protocol( addr: SocketAddr, client_alpn_protocol: &[u8], ) -> Option> { - let mut config = rustls::ClientConfig::new(); + let mut root_certs = RootCertStore::empty(); + for cert in TLS_SERVER_ROOTS.0 { + let cert = OwnedTrustAnchor::from_subject_spki_name_constraints( + cert.subject, + cert.spki, + cert.name_constraints, + ); + let certs = vec![cert].into_iter(); + root_certs.add_server_trust_anchors(certs); + } + + let mut config = rustls::ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(root_certs) + .with_no_client_auth(); + config.alpn_protocols.push(client_alpn_protocol.to_vec()); - let mut sess = rustls::ClientSession::new( - &Arc::new(config), - DNSNameRef::try_from_ascii_str("localhost").unwrap(), - ); + + let mut sess = rustls::ClientConnection::new( + Arc::new(config), + ServerName::try_from("localhost").unwrap(), + ) + .unwrap(); + let mut sock = StdTcpStream::connect(addr).unwrap(); let mut stream = rustls::Stream::new(&mut sess, &mut sock); + // The handshake will fails because the client will not be able to verify the server // certificate, but it doesn't matter here as we are just interested in the negotiated ALPN // protocol let _ = stream.flush(); - sess.get_alpn_protocol().map(|proto| proto.to_vec()) + + sess.alpn_protocol().map(|proto| proto.to_vec()) } #[actix_rt::test] diff --git a/actix-test/Cargo.toml b/actix-test/Cargo.toml index 41d32257c..62a27e5a5 100644 --- a/actix-test/Cargo.toml +++ b/actix-test/Cargo.toml @@ -35,4 +35,4 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" serde_urlencoded = "0.7" tls-openssl = { package = "openssl", version = "0.10.9", optional = true } -tls-rustls = { package = "rustls", version = "0.19.0", optional = true } +tls-rustls = { package = "rustls", version = "0.20.0", optional = true } diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 252b62efa..49d88e5e8 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx +* Updated rustls to v0.20. [#2414] + +[#2414]: https://github.com/actix/actix-web/pull/2414 ## 3.0.0-beta.8 - 2021-09-09 diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 262c3dce5..967d49602 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -73,8 +73,8 @@ rand = "0.8" serde = "1.0" serde_json = "1.0" serde_urlencoded = "0.7" -tls-openssl = { version = "0.10.9", package = "openssl", optional = true } -tls-rustls = { version = "0.19.0", package = "rustls", optional = true, features = ["dangerous_configuration"] } +tls-openssl = { package = "openssl", version = "0.10.9", optional = true } +tls-rustls = { package = "rustls", version = "0.20.0", optional = true, features = ["dangerous_configuration"] } [dev-dependencies] actix-web = { version = "4.0.0-beta.9", features = ["openssl"] } @@ -82,7 +82,7 @@ actix-http = { version = "3.0.0-beta.10", features = ["openssl"] } actix-http-test = { version = "3.0.0-beta.5", features = ["openssl"] } actix-utils = "3.0.0" actix-server = "2.0.0-beta.3" -actix-tls = { version = "3.0.0-beta.5", features = ["openssl", "rustls"] } +actix-tls = { version = "3.0.0-beta.6", features = ["openssl", "rustls"] } actix-test = { version = "0.1.0-beta.3", features = ["openssl", "rustls"] } brotli2 = "0.3.2" @@ -90,7 +90,9 @@ env_logger = "0.8" flate2 = "1.0.13" futures-util = { version = "0.3.7", default-features = false } rcgen = "0.8" -webpki = "0.21" +rustls-pemfile = "0.2" +webpki = "0.22" +webpki-roots = "0.22" [[example]] name = "client" diff --git a/awc/tests/test_rustls_client.rs b/awc/tests/test_rustls_client.rs index bc811c046..95f2d0616 100644 --- a/awc/tests/test_rustls_client.rs +++ b/awc/tests/test_rustls_client.rs @@ -8,6 +8,7 @@ use std::{ atomic::{AtomicUsize, Ordering}, Arc, }, + time::SystemTime, }; use actix_http::HttpService; @@ -15,37 +16,52 @@ use actix_http_test::test_server; use actix_service::{fn_service, map_config, ServiceFactoryExt}; use actix_utils::future::ok; use actix_web::{dev::AppConfig, http::Version, web, App, HttpResponse}; -use rustls::internal::pemfile::{certs, pkcs8_private_keys}; -use rustls::{ClientConfig, NoClientAuth, ServerConfig}; +use rustls::{ + client::{ServerCertVerified, ServerCertVerifier}, + Certificate, ClientConfig, OwnedTrustAnchor, PrivateKey, RootCertStore, ServerConfig, + ServerName, +}; +use rustls_pemfile::{certs, pkcs8_private_keys}; +use webpki_roots::TLS_SERVER_ROOTS; fn tls_config() -> ServerConfig { let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_owned()]).unwrap(); let cert_file = cert.serialize_pem().unwrap(); let key_file = cert.serialize_private_key_pem(); - let mut config = ServerConfig::new(NoClientAuth::new()); let cert_file = &mut BufReader::new(cert_file.as_bytes()); let key_file = &mut BufReader::new(key_file.as_bytes()); - let cert_chain = certs(cert_file).unwrap(); + let cert_chain = certs(cert_file) + .unwrap() + .into_iter() + .map(Certificate) + .collect(); let mut keys = pkcs8_private_keys(key_file).unwrap(); - config.set_single_cert(cert_chain, keys.remove(0)).unwrap(); - config + ServerConfig::builder() + .with_safe_defaults() + .with_no_client_auth() + .with_single_cert(cert_chain, PrivateKey(keys.remove(0))) + .unwrap() } mod danger { + use super::*; + pub struct NoCertificateVerification; - impl rustls::ServerCertVerifier for NoCertificateVerification { + impl ServerCertVerifier for NoCertificateVerification { fn verify_server_cert( &self, - _roots: &rustls::RootCertStore, - _presented_certs: &[rustls::Certificate], - _dns_name: webpki::DNSNameRef<'_>, - _ocsp: &[u8], - ) -> Result { - Ok(rustls::ServerCertVerified::assertion()) + _end_entity: &Certificate, + _intermediates: &[Certificate], + _server_name: &ServerName, + _scts: &mut dyn Iterator, + _ocsp_response: &[u8], + _now: SystemTime, + ) -> Result { + Ok(ServerCertVerified::assertion()) } } } @@ -73,10 +89,26 @@ async fn test_connection_reuse_h2() { }) .await; - // disable TLS verification - let mut config = ClientConfig::new(); + let mut root_certs = RootCertStore::empty(); + for cert in TLS_SERVER_ROOTS.0 { + let cert = OwnedTrustAnchor::from_subject_spki_name_constraints( + cert.subject, + cert.spki, + cert.name_constraints, + ); + let certs = vec![cert].into_iter(); + root_certs.add_server_trust_anchors(certs); + } + + let mut config = ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(root_certs) + .with_no_client_auth(); + let protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; - config.set_protocols(&protos); + config.alpn_protocols = protos; + + // disable TLS verification config .dangerous() .set_certificate_verifier(Arc::new(danger::NoCertificateVerification)); diff --git a/tests/test_server.rs b/tests/test_server.rs index beb8ff0f5..ff6f5ae5e 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -883,27 +883,31 @@ async fn test_brotli_encoding_large_openssl() { mod plus_rustls { use std::io::BufReader; - use rustls::{ - internal::pemfile::{certs, pkcs8_private_keys}, - NoClientAuth, ServerConfig as RustlsServerConfig, - }; + use rustls::{Certificate, PrivateKey, ServerConfig as RustlsServerConfig}; + use rustls_pemfile::{certs, pkcs8_private_keys}; use super::*; - fn rustls_config() -> RustlsServerConfig { + fn tls_config() -> RustlsServerConfig { let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_owned()]).unwrap(); let cert_file = cert.serialize_pem().unwrap(); let key_file = cert.serialize_private_key_pem(); - let mut config = RustlsServerConfig::new(NoClientAuth::new()); let cert_file = &mut BufReader::new(cert_file.as_bytes()); let key_file = &mut BufReader::new(key_file.as_bytes()); - let cert_chain = certs(cert_file).unwrap(); + let cert_chain = certs(cert_file) + .unwrap() + .into_iter() + .map(Certificate) + .collect(); let mut keys = pkcs8_private_keys(key_file).unwrap(); - config.set_single_cert(cert_chain, keys.remove(0)).unwrap(); - config + RustlsServerConfig::builder() + .with_safe_defaults() + .with_no_client_auth() + .with_single_cert(cert_chain, PrivateKey(keys.remove(0))) + .unwrap() } #[actix_rt::test] @@ -914,7 +918,7 @@ mod plus_rustls { .map(char::from) .collect::(); - let srv = actix_test::start_with(actix_test::config().rustls(rustls_config()), || { + let srv = actix_test::start_with(actix_test::config().rustls(tls_config()), || { App::new().service(web::resource("/").route(web::to(|bytes: Bytes| { HttpResponse::Ok() .encoding(actix_web::http::ContentEncoding::Identity)