Skip to content

Commit db79886

Browse files
feat(actix-tls): support for rustls 0.23 (#554)
* Add feature for using rustls 0.23 * Fix mistake * Fix use of wrong tokio rustls package * Fix accept openssl test * Use rustls 0.23 for the example * Install nasm in CI step for windows * Change outdated step name * Fix CI mistake * test: install default crypto provider in tests * docs: update changelog --------- Co-authored-by: Rob Ede <[email protected]>
1 parent 1db640f commit db79886

File tree

11 files changed

+408
-19
lines changed

11 files changed

+408
-19
lines changed

.github/workflows/ci.yml

+4
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ jobs:
4343
if: matrix.target.os == 'ubuntu-latest'
4444
run: ./scripts/free-disk-space.sh
4545

46+
- name: Install nasm
47+
if: matrix.target.os == 'windows-latest'
48+
uses: ilammy/[email protected]
49+
4650
- name: Install OpenSSL
4751
if: matrix.target.os == 'windows-latest'
4852
shell: bash

actix-tls/CHANGES.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
## Unreleased
44

5+
- Add `rustls-0_23`, `rustls-0_23-webpki-roots`, and `rustls-0_23-native-roots` crate features.
56
- Minimum supported Rust version (MSRV) is now 1.70.
67

78
## 3.3.0
89

9-
- Add `rustls-0_22` create feature which excludes any root certificate methods or re-exports.
10+
- Add `rustls-0_22` crate feature which excludes any root certificate methods or re-exports.
1011

1112
## 3.2.0
1213

actix-tls/Cargo.toml

+12-4
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ rustls-0_22 = ["dep:tokio-rustls-025", "dep:rustls-pki-types-1"]
6161
rustls-0_22-webpki-roots = ["rustls-0_22", "dep:webpki-roots-026"]
6262
rustls-0_22-native-roots = ["rustls-0_22", "dep:rustls-native-certs-07"]
6363

64+
# use rustls v0.23 impls
65+
rustls-0_23 = ["dep:tokio-rustls-026", "dep:rustls-pki-types-1"]
66+
rustls-0_23-webpki-roots = ["rustls-0_23", "dep:webpki-roots-026"]
67+
rustls-0_23-native-roots = ["rustls-0_23", "dep:rustls-native-certs-07"]
68+
6469
# use native-tls impls
6570
native-tls = ["dep:tokio-native-tls"]
6671

@@ -98,9 +103,12 @@ tokio-rustls-024 = { package = "tokio-rustls", version = "0.24", optional = true
98103
webpki-roots-025 = { package = "webpki-roots", version = "0.25", optional = true }
99104

100105
# rustls v0.22
101-
rustls-pki-types-1 = { package = "rustls-pki-types", version = "1", optional = true }
106+
rustls-pki-types-1 = { package = "rustls-pki-types", version = "1", optional = true } # Also used for rustls v0.23
102107
tokio-rustls-025 = { package = "tokio-rustls", version = "0.25", optional = true }
103-
webpki-roots-026 = { package = "webpki-roots", version = "0.26", optional = true }
108+
webpki-roots-026 = { package = "webpki-roots", version = "0.26", optional = true } # Also used for rustls v0.23
109+
110+
# rustls v0.23
111+
tokio-rustls-026 = { package = "tokio-rustls", version = "0.26", optional = true }
104112

105113
# native root certificates for rustls impls
106114
rustls-native-certs-06 = { package = "rustls-native-certs", version = "0.6", optional = true }
@@ -119,9 +127,9 @@ futures-util = { version = "0.3.17", default-features = false, features = ["sink
119127
itertools = "0.12"
120128
rcgen = "0.12"
121129
rustls-pemfile = "2"
122-
tokio-rustls-025 = { package = "tokio-rustls", version = "0.25" }
130+
tokio-rustls-026 = { package = "tokio-rustls", version = "0.26", features = ["ring"] }
123131
trust-dns-resolver = "0.23"
124132

125133
[[example]]
126134
name = "accept-rustls"
127-
required-features = ["accept", "rustls-0_22"]
135+
required-features = ["accept", "rustls-0_23"]

actix-tls/examples/accept-rustls.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@ use std::{
3030
use actix_rt::net::TcpStream;
3131
use actix_server::Server;
3232
use actix_service::ServiceFactoryExt as _;
33-
use actix_tls::accept::rustls_0_22::{Acceptor as RustlsAcceptor, TlsStream};
33+
use actix_tls::accept::rustls_0_23::{Acceptor as RustlsAcceptor, TlsStream};
3434
use futures_util::future::ok;
3535
use itertools::Itertools as _;
3636
use rustls::server::ServerConfig;
3737
use rustls_pemfile::{certs, rsa_private_keys};
3838
use rustls_pki_types_1::PrivateKeyDer;
39-
use tokio_rustls_025::rustls;
39+
use tokio_rustls_026::rustls;
4040
use tracing::info;
4141

4242
#[actix_rt::main]

actix-tls/src/accept/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ pub mod rustls_0_21;
2525
#[cfg(feature = "rustls-0_22")]
2626
pub mod rustls_0_22;
2727

28+
#[cfg(feature = "rustls-0_23")]
29+
pub mod rustls_0_23;
30+
2831
#[cfg(feature = "native-tls")]
2932
pub mod native_tls;
3033

@@ -35,6 +38,7 @@ pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256);
3538
feature = "rustls-0_20",
3639
feature = "rustls-0_21",
3740
feature = "rustls-0_22",
41+
feature = "rustls-0_23",
3842
feature = "native-tls",
3943
))]
4044
pub(crate) const DEFAULT_TLS_HANDSHAKE_TIMEOUT: std::time::Duration =

actix-tls/src/accept/rustls_0_23.rs

+198
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
//! `rustls` v0.23 based TLS connection acceptor service.
2+
//!
3+
//! See [`Acceptor`] for main service factory docs.
4+
5+
use std::{
6+
convert::Infallible,
7+
future::Future,
8+
io::{self, IoSlice},
9+
pin::Pin,
10+
sync::Arc,
11+
task::{Context, Poll},
12+
time::Duration,
13+
};
14+
15+
use actix_rt::{
16+
net::{ActixStream, Ready},
17+
time::{sleep, Sleep},
18+
};
19+
use actix_service::{Service, ServiceFactory};
20+
use actix_utils::{
21+
counter::{Counter, CounterGuard},
22+
future::{ready, Ready as FutReady},
23+
};
24+
use pin_project_lite::pin_project;
25+
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
26+
use tokio_rustls::{Accept, TlsAcceptor};
27+
use tokio_rustls_026 as tokio_rustls;
28+
29+
use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
30+
31+
pub mod reexports {
32+
//! Re-exports from `rustls` that are useful for acceptors.
33+
34+
pub use tokio_rustls_026::rustls::ServerConfig;
35+
}
36+
37+
/// Wraps a `rustls` based async TLS stream in order to implement [`ActixStream`].
38+
pub struct TlsStream<IO>(tokio_rustls::server::TlsStream<IO>);
39+
40+
impl_more::impl_from!(<IO> in tokio_rustls::server::TlsStream<IO> => TlsStream<IO>);
41+
impl_more::impl_deref_and_mut!(<IO> in TlsStream<IO> => tokio_rustls::server::TlsStream<IO>);
42+
43+
impl<IO: ActixStream> AsyncRead for TlsStream<IO> {
44+
fn poll_read(
45+
self: Pin<&mut Self>,
46+
cx: &mut Context<'_>,
47+
buf: &mut ReadBuf<'_>,
48+
) -> Poll<io::Result<()>> {
49+
Pin::new(&mut **self.get_mut()).poll_read(cx, buf)
50+
}
51+
}
52+
53+
impl<IO: ActixStream> AsyncWrite for TlsStream<IO> {
54+
fn poll_write(
55+
self: Pin<&mut Self>,
56+
cx: &mut Context<'_>,
57+
buf: &[u8],
58+
) -> Poll<io::Result<usize>> {
59+
Pin::new(&mut **self.get_mut()).poll_write(cx, buf)
60+
}
61+
62+
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
63+
Pin::new(&mut **self.get_mut()).poll_flush(cx)
64+
}
65+
66+
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
67+
Pin::new(&mut **self.get_mut()).poll_shutdown(cx)
68+
}
69+
70+
fn poll_write_vectored(
71+
self: Pin<&mut Self>,
72+
cx: &mut Context<'_>,
73+
bufs: &[IoSlice<'_>],
74+
) -> Poll<io::Result<usize>> {
75+
Pin::new(&mut **self.get_mut()).poll_write_vectored(cx, bufs)
76+
}
77+
78+
fn is_write_vectored(&self) -> bool {
79+
(**self).is_write_vectored()
80+
}
81+
}
82+
83+
impl<IO: ActixStream> ActixStream for TlsStream<IO> {
84+
fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
85+
IO::poll_read_ready((**self).get_ref().0, cx)
86+
}
87+
88+
fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
89+
IO::poll_write_ready((**self).get_ref().0, cx)
90+
}
91+
}
92+
93+
/// Accept TLS connections via the `rustls` crate.
94+
pub struct Acceptor {
95+
config: Arc<reexports::ServerConfig>,
96+
handshake_timeout: Duration,
97+
}
98+
99+
impl Acceptor {
100+
/// Constructs `rustls` based acceptor service factory.
101+
pub fn new(config: reexports::ServerConfig) -> Self {
102+
Acceptor {
103+
config: Arc::new(config),
104+
handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
105+
}
106+
}
107+
108+
/// Limit the amount of time that the acceptor will wait for a TLS handshake to complete.
109+
///
110+
/// Default timeout is 3 seconds.
111+
pub fn set_handshake_timeout(&mut self, handshake_timeout: Duration) -> &mut Self {
112+
self.handshake_timeout = handshake_timeout;
113+
self
114+
}
115+
}
116+
117+
impl Clone for Acceptor {
118+
fn clone(&self) -> Self {
119+
Self {
120+
config: self.config.clone(),
121+
handshake_timeout: self.handshake_timeout,
122+
}
123+
}
124+
}
125+
126+
impl<IO: ActixStream> ServiceFactory<IO> for Acceptor {
127+
type Response = TlsStream<IO>;
128+
type Error = TlsError<io::Error, Infallible>;
129+
type Config = ();
130+
type Service = AcceptorService;
131+
type InitError = ();
132+
type Future = FutReady<Result<Self::Service, Self::InitError>>;
133+
134+
fn new_service(&self, _: ()) -> Self::Future {
135+
let res = MAX_CONN_COUNTER.with(|conns| {
136+
Ok(AcceptorService {
137+
acceptor: self.config.clone().into(),
138+
conns: conns.clone(),
139+
handshake_timeout: self.handshake_timeout,
140+
})
141+
});
142+
143+
ready(res)
144+
}
145+
}
146+
147+
/// Rustls based acceptor service.
148+
pub struct AcceptorService {
149+
acceptor: TlsAcceptor,
150+
conns: Counter,
151+
handshake_timeout: Duration,
152+
}
153+
154+
impl<IO: ActixStream> Service<IO> for AcceptorService {
155+
type Response = TlsStream<IO>;
156+
type Error = TlsError<io::Error, Infallible>;
157+
type Future = AcceptFut<IO>;
158+
159+
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
160+
if self.conns.available(cx) {
161+
Poll::Ready(Ok(()))
162+
} else {
163+
Poll::Pending
164+
}
165+
}
166+
167+
fn call(&self, req: IO) -> Self::Future {
168+
AcceptFut {
169+
fut: self.acceptor.accept(req),
170+
timeout: sleep(self.handshake_timeout),
171+
_guard: self.conns.get(),
172+
}
173+
}
174+
}
175+
176+
pin_project! {
177+
/// Accept future for Rustls service.
178+
#[doc(hidden)]
179+
pub struct AcceptFut<IO: ActixStream> {
180+
fut: Accept<IO>,
181+
#[pin]
182+
timeout: Sleep,
183+
_guard: CounterGuard,
184+
}
185+
}
186+
187+
impl<IO: ActixStream> Future for AcceptFut<IO> {
188+
type Output = Result<TlsStream<IO>, TlsError<io::Error, Infallible>>;
189+
190+
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
191+
let mut this = self.project();
192+
match Pin::new(&mut this.fut).poll(cx) {
193+
Poll::Ready(Ok(stream)) => Poll::Ready(Ok(TlsStream(stream))),
194+
Poll::Ready(Err(err)) => Poll::Ready(Err(TlsError::Tls(err))),
195+
Poll::Pending => this.timeout.poll(cx).map(|_| Err(TlsError::Timeout)),
196+
}
197+
}
198+
}

actix-tls/src/connect/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ pub mod rustls_0_21;
4949
#[cfg(feature = "rustls-0_22")]
5050
pub mod rustls_0_22;
5151

52+
#[cfg(feature = "rustls-0_23")]
53+
pub mod rustls_0_23;
54+
5255
#[cfg(feature = "native-tls")]
5356
pub mod native_tls;
5457

0 commit comments

Comments
 (0)