Skip to content

Commit

Permalink
feat(actix-tls): support for rustls 0.23 (#554)
Browse files Browse the repository at this point in the history
* 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 <robjtede@icloud.com>
  • Loading branch information
SleeplessOne1917 and robjtede committed May 12, 2024
1 parent 1db640f commit db79886
Show file tree
Hide file tree
Showing 11 changed files with 408 additions and 19 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ jobs:
if: matrix.target.os == 'ubuntu-latest'
run: ./scripts/free-disk-space.sh

- name: Install nasm
if: matrix.target.os == 'windows-latest'
uses: ilammy/setup-nasm@v1.5.1

- name: Install OpenSSL
if: matrix.target.os == 'windows-latest'
shell: bash
Expand Down
3 changes: 2 additions & 1 deletion actix-tls/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

## Unreleased

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

## 3.3.0

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

## 3.2.0

Expand Down
16 changes: 12 additions & 4 deletions actix-tls/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ rustls-0_22 = ["dep:tokio-rustls-025", "dep:rustls-pki-types-1"]
rustls-0_22-webpki-roots = ["rustls-0_22", "dep:webpki-roots-026"]
rustls-0_22-native-roots = ["rustls-0_22", "dep:rustls-native-certs-07"]

# use rustls v0.23 impls
rustls-0_23 = ["dep:tokio-rustls-026", "dep:rustls-pki-types-1"]
rustls-0_23-webpki-roots = ["rustls-0_23", "dep:webpki-roots-026"]
rustls-0_23-native-roots = ["rustls-0_23", "dep:rustls-native-certs-07"]

# use native-tls impls
native-tls = ["dep:tokio-native-tls"]

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

# rustls v0.22
rustls-pki-types-1 = { package = "rustls-pki-types", version = "1", optional = true }
rustls-pki-types-1 = { package = "rustls-pki-types", version = "1", optional = true } # Also used for rustls v0.23
tokio-rustls-025 = { package = "tokio-rustls", version = "0.25", optional = true }
webpki-roots-026 = { package = "webpki-roots", version = "0.26", optional = true }
webpki-roots-026 = { package = "webpki-roots", version = "0.26", optional = true } # Also used for rustls v0.23

# rustls v0.23
tokio-rustls-026 = { package = "tokio-rustls", version = "0.26", optional = true }

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

[[example]]
name = "accept-rustls"
required-features = ["accept", "rustls-0_22"]
required-features = ["accept", "rustls-0_23"]
4 changes: 2 additions & 2 deletions actix-tls/examples/accept-rustls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ use std::{
use actix_rt::net::TcpStream;
use actix_server::Server;
use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::rustls_0_22::{Acceptor as RustlsAcceptor, TlsStream};
use actix_tls::accept::rustls_0_23::{Acceptor as RustlsAcceptor, TlsStream};
use futures_util::future::ok;
use itertools::Itertools as _;
use rustls::server::ServerConfig;
use rustls_pemfile::{certs, rsa_private_keys};
use rustls_pki_types_1::PrivateKeyDer;
use tokio_rustls_025::rustls;
use tokio_rustls_026::rustls;
use tracing::info;

#[actix_rt::main]
Expand Down
4 changes: 4 additions & 0 deletions actix-tls/src/accept/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ pub mod rustls_0_21;
#[cfg(feature = "rustls-0_22")]
pub mod rustls_0_22;

#[cfg(feature = "rustls-0_23")]
pub mod rustls_0_23;

#[cfg(feature = "native-tls")]
pub mod native_tls;

Expand All @@ -35,6 +38,7 @@ pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256);
feature = "rustls-0_20",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "rustls-0_23",
feature = "native-tls",
))]
pub(crate) const DEFAULT_TLS_HANDSHAKE_TIMEOUT: std::time::Duration =
Expand Down
198 changes: 198 additions & 0 deletions actix-tls/src/accept/rustls_0_23.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
//! `rustls` v0.23 based TLS connection acceptor service.
//!
//! See [`Acceptor`] for main service factory docs.

use std::{
convert::Infallible,
future::Future,
io::{self, IoSlice},
pin::Pin,
sync::Arc,
task::{Context, Poll},
time::Duration,
};

use actix_rt::{
net::{ActixStream, Ready},
time::{sleep, Sleep},
};
use actix_service::{Service, ServiceFactory};
use actix_utils::{
counter::{Counter, CounterGuard},
future::{ready, Ready as FutReady},
};
use pin_project_lite::pin_project;
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio_rustls::{Accept, TlsAcceptor};
use tokio_rustls_026 as tokio_rustls;

use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};

pub mod reexports {
//! Re-exports from `rustls` that are useful for acceptors.

pub use tokio_rustls_026::rustls::ServerConfig;
}

/// Wraps a `rustls` based async TLS stream in order to implement [`ActixStream`].
pub struct TlsStream<IO>(tokio_rustls::server::TlsStream<IO>);

impl_more::impl_from!(<IO> in tokio_rustls::server::TlsStream<IO> => TlsStream<IO>);
impl_more::impl_deref_and_mut!(<IO> in TlsStream<IO> => tokio_rustls::server::TlsStream<IO>);

impl<IO: ActixStream> AsyncRead for TlsStream<IO> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
Pin::new(&mut **self.get_mut()).poll_read(cx, buf)
}
}

impl<IO: ActixStream> AsyncWrite for TlsStream<IO> {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut **self.get_mut()).poll_write(cx, buf)
}

fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut **self.get_mut()).poll_flush(cx)
}

fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut **self.get_mut()).poll_shutdown(cx)
}

fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>],
) -> Poll<io::Result<usize>> {
Pin::new(&mut **self.get_mut()).poll_write_vectored(cx, bufs)
}

fn is_write_vectored(&self) -> bool {
(**self).is_write_vectored()
}
}

impl<IO: ActixStream> ActixStream for TlsStream<IO> {
fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
IO::poll_read_ready((**self).get_ref().0, cx)
}

fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
IO::poll_write_ready((**self).get_ref().0, cx)
}
}

/// Accept TLS connections via the `rustls` crate.
pub struct Acceptor {
config: Arc<reexports::ServerConfig>,
handshake_timeout: Duration,
}

impl Acceptor {
/// Constructs `rustls` based acceptor service factory.
pub fn new(config: reexports::ServerConfig) -> Self {
Acceptor {
config: Arc::new(config),
handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
}
}

/// Limit the amount of time that the acceptor will wait for a TLS handshake to complete.
///
/// Default timeout is 3 seconds.
pub fn set_handshake_timeout(&mut self, handshake_timeout: Duration) -> &mut Self {
self.handshake_timeout = handshake_timeout;
self
}
}

impl Clone for Acceptor {
fn clone(&self) -> Self {
Self {
config: self.config.clone(),
handshake_timeout: self.handshake_timeout,
}
}
}

impl<IO: ActixStream> ServiceFactory<IO> for Acceptor {
type Response = TlsStream<IO>;
type Error = TlsError<io::Error, Infallible>;
type Config = ();
type Service = AcceptorService;
type InitError = ();
type Future = FutReady<Result<Self::Service, Self::InitError>>;

fn new_service(&self, _: ()) -> Self::Future {
let res = MAX_CONN_COUNTER.with(|conns| {
Ok(AcceptorService {
acceptor: self.config.clone().into(),
conns: conns.clone(),
handshake_timeout: self.handshake_timeout,
})
});

ready(res)
}
}

/// Rustls based acceptor service.
pub struct AcceptorService {
acceptor: TlsAcceptor,
conns: Counter,
handshake_timeout: Duration,
}

impl<IO: ActixStream> Service<IO> for AcceptorService {
type Response = TlsStream<IO>;
type Error = TlsError<io::Error, Infallible>;
type Future = AcceptFut<IO>;

fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
if self.conns.available(cx) {
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
}

fn call(&self, req: IO) -> Self::Future {
AcceptFut {
fut: self.acceptor.accept(req),
timeout: sleep(self.handshake_timeout),
_guard: self.conns.get(),
}
}
}

pin_project! {
/// Accept future for Rustls service.
#[doc(hidden)]
pub struct AcceptFut<IO: ActixStream> {
fut: Accept<IO>,
#[pin]
timeout: Sleep,
_guard: CounterGuard,
}
}

impl<IO: ActixStream> Future for AcceptFut<IO> {
type Output = Result<TlsStream<IO>, TlsError<io::Error, Infallible>>;

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.project();
match Pin::new(&mut this.fut).poll(cx) {
Poll::Ready(Ok(stream)) => Poll::Ready(Ok(TlsStream(stream))),
Poll::Ready(Err(err)) => Poll::Ready(Err(TlsError::Tls(err))),
Poll::Pending => this.timeout.poll(cx).map(|_| Err(TlsError::Timeout)),
}
}
}
3 changes: 3 additions & 0 deletions actix-tls/src/connect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ pub mod rustls_0_21;
#[cfg(feature = "rustls-0_22")]
pub mod rustls_0_22;

#[cfg(feature = "rustls-0_23")]
pub mod rustls_0_23;

#[cfg(feature = "native-tls")]
pub mod native_tls;

Expand Down

0 comments on commit db79886

Please sign in to comment.