Skip to content

Commit

Permalink
Merge branch 'seanmonstar:master' into local-socketaddr
Browse files Browse the repository at this point in the history
  • Loading branch information
daviessm committed Dec 29, 2023
2 parents 6f8de48 + 2c3581e commit c73dda7
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 66 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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<impl Reply, impl Reply>`.
- 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**:
Expand Down
17 changes: 8 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 <sean@seanmonstar.com>"]
license = "MIT"
Expand All @@ -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.19", features = ["stream", "server", "http1", "http2", "tcp", "client"] }
log = "0.4"
Expand All @@ -31,17 +31,16 @@ 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.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 }
rustls-pemfile = "1.0"
tokio-rustls = { version = "0.25", optional = true }
rustls-pemfile = { version = "2.0", optional = true }

[dev-dependencies]
pretty_env_logger = "0.5"
Expand All @@ -57,7 +56,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"]
Expand Down
4 changes: 4 additions & 0 deletions examples/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
12 changes: 12 additions & 0 deletions examples/tls/cert.ecc.pem
Original file line number Diff line number Diff line change
@@ -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-----
5 changes: 5 additions & 0 deletions examples/tls/key.ecc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIPwp3LAnLEyWe2lLz66Y3QCCJ/BEMJheTM0shZnnSw6toAoGCCqGSM49
AwEHoUQDQgAE2UeBetF/oh43g+pMkmX15YzXJA29tkGEO+k7OBhWFpHQ7LVOsnoc
chEfjGVrJlJ0xPxst5p6UpjM6EgX6CkZhw==
-----END EC PRIVATE KEY-----
16 changes: 13 additions & 3 deletions src/filters/ws.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand Down
139 changes: 86 additions & 53 deletions src/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,23 @@ 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)]
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
Expand All @@ -40,8 +40,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),
}
Expand Down Expand Up @@ -170,73 +174,81 @@ impl TlsConfigBuilder {
pub(crate) fn build(mut self) -> Result<ServerConfig, TlsConfigError> {
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();

let key = {
// convert it to Vec<u8> 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)?;

if key_vec.is_empty() {
return Err(TlsConfigError::EmptyKey);
}
.collect::<Result<Vec<_>, _>>()
.map_err(|_e| TlsConfigError::CertParseError)?;

let mut pkcs8 = rustls_pemfile::pkcs8_private_keys(&mut key_vec.as_slice())
.map_err(|_e| TlsConfigError::Pkcs8ParseError)?;
let mut key_vec = Vec::new();
self.key
.read_to_end(&mut key_vec)
.map_err(TlsConfigError::Io)?;

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)
.collect::<Result<Vec<_>, _>>()
.map_err(|_e| TlsConfigError::InvalidIdentityPem)?
{
match item {
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),
}
}
let key = match key_opt {
Some(v) => v,
_ => return Err(TlsConfigError::MissingPrivateKey),
};

fn read_trust_anchor(
trust_anchor: Box<dyn Read + Send + Sync>,
) -> Result<RootCertStore, TlsConfigError> {
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::<Result<Vec<_>, _>>()
.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);
}

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)
}
}
Expand Down Expand Up @@ -416,4 +428,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();
}
}

0 comments on commit c73dda7

Please sign in to comment.