1
0
mirror of https://github.com/fafhrd91/actix-net synced 2024-11-24 00:01:11 +01:00

Rustls v0.21 support (#480)

This commit is contained in:
Rob Ede 2023-08-26 14:59:51 +01:00 committed by GitHub
parent 6ce8307060
commit 0bc310a656
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 504 additions and 75 deletions

View File

@ -15,9 +15,11 @@ ci-check-linux = "hack --workspace --feature-powerset check --tests --examples"
# tests avoiding io-uring feature # tests avoiding io-uring feature
ci-test = "hack --feature-powerset --exclude-features=io-uring test --lib --tests --no-fail-fast -- --nocapture" ci-test = "hack --feature-powerset --exclude-features=io-uring test --lib --tests --no-fail-fast -- --nocapture"
ci-test-rustls-020 = "hack --feature-powerset --exclude-features=io-uring,rustls-0_21 test --lib --tests --no-fail-fast -- --nocapture"
ci-test-rustls-021 = "hack --feature-powerset --exclude-features=io-uring,rustls-0_20 test --lib --tests --no-fail-fast -- --nocapture"
# tests avoiding io-uring feature on Windows # tests avoiding io-uring feature on Windows
ci-test-win = "hack --feature-powerset --depth 2 --exclude-features=io-uring test --lib --tests --no-fail-fast -- --nocapture" ci-test-win = "hack --feature-powerset --depth=2 --exclude-features=io-uring test --lib --tests --no-fail-fast -- --nocapture"
# test with io-uring feature # test with io-uring feature
ci-test-linux = "hack --feature-powerset test --lib --tests --no-fail-fast -- --nocapture" ci-test-linux = "hack --feature-powerset --exclude-features=rustls-0_20 test --lib --tests --no-fail-fast -- --nocapture"

View File

@ -16,6 +16,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
# prettier-ignore
target: target:
- { name: Linux, os: ubuntu-latest, triple: x86_64-unknown-linux-gnu } - { name: Linux, os: ubuntu-latest, triple: x86_64-unknown-linux-gnu }
- { name: macOS, os: macos-latest, triple: x86_64-apple-darwin } - { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }
@ -37,6 +38,10 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Free Disk Space
if: matrix.target.os == 'ubuntu-latest'
run: ./scripts/free-disk-space.sh
- name: Install OpenSSL - name: Install OpenSSL
if: matrix.target.os == 'windows-latest' if: matrix.target.os == 'windows-latest'
run: choco install openssl -y --forcex64 --no-progress run: choco install openssl -y --forcex64 --no-progress
@ -83,8 +88,15 @@ jobs:
run: cargo ci-test run: cargo ci-test
- name: tests - name: tests
if: matrix.target.os == 'ubuntu-latest' if: matrix.target.os == 'ubuntu-latest'
run: | run: >-
sudo bash -c "ulimit -Sl 512 && ulimit -Hl 512 && PATH=$PATH:/usr/share/rust/.cargo/bin && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-linux" sudo bash -c "
ulimit -Sl 512
&& ulimit -Hl 512
&& PATH=$PATH:/usr/share/rust/.cargo/bin
&& RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-rustls-020
&& RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-rustls-021
&& RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-linux
"
- name: Clear the cargo caches - name: Clear the cargo caches
run: | run: |

View File

@ -16,6 +16,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
# prettier-ignore
target: target:
- { name: Linux, os: ubuntu-latest, triple: x86_64-unknown-linux-gnu } - { name: Linux, os: ubuntu-latest, triple: x86_64-unknown-linux-gnu }
- { name: macOS, os: macos-latest, triple: x86_64-apple-darwin } - { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }
@ -99,8 +100,15 @@ jobs:
run: cargo ci-test-win run: cargo ci-test-win
- name: tests - name: tests
if: matrix.target.os == 'ubuntu-latest' if: matrix.target.os == 'ubuntu-latest'
run: | run: >-
sudo bash -c "ulimit -Sl 512 && ulimit -Hl 512 && PATH=$PATH:/usr/share/rust/.cargo/bin && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-linux" sudo bash -c "
ulimit -Sl 512
&& ulimit -Hl 512
&& PATH=$PATH:/usr/share/rust/.cargo/bin
&& RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-rustls-020
&& RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-rustls-021
&& RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-linux
"
- name: Clear the cargo caches - name: Clear the cargo caches
run: | run: |

View File

@ -15,6 +15,7 @@ members = [
resolver = "2" resolver = "2"
[workspace.package] [workspace.package]
license = "MIT OR Apache-2.0"
edition = "2021" edition = "2021"
rust-version = "1.65" rust-version = "1.65"

View File

@ -625,6 +625,7 @@ impl Future for ServerWorker {
self.poll(cx) self.poll(cx)
} }
}, },
WorkerState::Restarting(ref mut restart) => { WorkerState::Restarting(ref mut restart) => {
let factory_id = restart.factory_id; let factory_id = restart.factory_id;
let token = restart.token; let token = restart.token;
@ -649,6 +650,7 @@ impl Future for ServerWorker {
self.poll(cx) self.poll(cx)
} }
WorkerState::Shutdown(ref mut shutdown) => { WorkerState::Shutdown(ref mut shutdown) => {
// drop all pending connections in rx channel. // drop all pending connections in rx channel.
while let Poll::Ready(Some(conn)) = this.conn_rx.poll_recv(cx) { while let Poll::Ready(Some(conn)) = this.conn_rx.poll_recv(cx) {
@ -682,6 +684,7 @@ impl Future for ServerWorker {
shutdown.timer.as_mut().poll(cx) shutdown.timer.as_mut().poll(cx)
} }
} }
// actively poll stream and handle worker command // actively poll stream and handle worker command
WorkerState::Available => loop { WorkerState::Available => loop {
match this.check_readiness(cx) { match this.check_readiness(cx) {

View File

@ -1,40 +1,43 @@
# Changes # Changes
## Unreleased - 2023-xx-xx ## Unreleased
- Support Rustls v0.21.
- Added `{accept, connect}::rustls_0_21` modules.
- Added `{accept, connect}::rustls_0_20` alias for `{accept, connect}::rustls` modules.
- Minimum supported Rust version (MSRV) is now 1.65. - Minimum supported Rust version (MSRV) is now 1.65.
## 3.0.4 - 2022-03-15 ## 3.0.4
- Logs emitted now use the `tracing` crate with `log` compatibility. [#451] - Logs emitted now use the `tracing` crate with `log` compatibility. [#451]
[#451]: https://github.com/actix/actix-net/pull/451 [#451]: https://github.com/actix/actix-net/pull/451
## 3.0.3 - 2022-02-15 ## 3.0.3
- No significant changes since `3.0.2`. - No significant changes since `3.0.2`.
## 3.0.2 - 2022-01-28 ## 3.0.2
- Expose `connect::Connection::new`. [#439] - Expose `connect::Connection::new`. [#439]
[#439]: https://github.com/actix/actix-net/pull/439 [#439]: https://github.com/actix/actix-net/pull/439
## 3.0.1 - 2022-01-11 ## 3.0.1
- No significant changes since `3.0.0`. - No significant changes since `3.0.0`.
## 3.0.0 - 2021-12-26 ## 3.0.0
- No significant changes since `3.0.0-rc.2`. - No significant changes since `3.0.0-rc.2`.
## 3.0.0-rc.2 - 2021-12-10 ## 3.0.0-rc.2
- Re-export `openssl::SslConnectorBuilder` in `connect::openssl::reexports`. [#429] - Re-export `openssl::SslConnectorBuilder` in `connect::openssl::reexports`. [#429]
[#429]: https://github.com/actix/actix-net/pull/429 [#429]: https://github.com/actix/actix-net/pull/429
## 3.0.0-rc.1 - 2021-11-29 ## 3.0.0-rc.1
### Added ### Added
@ -72,7 +75,7 @@
[#422]: https://github.com/actix/actix-net/pull/422 [#422]: https://github.com/actix/actix-net/pull/422
[#423]: https://github.com/actix/actix-net/pull/423 [#423]: https://github.com/actix/actix-net/pull/423
## 3.0.0-beta.9 - 2021-11-22 ## 3.0.0-beta.9
- Add configurable timeout for accepting TLS connection. [#393] - Add configurable timeout for accepting TLS connection. [#393]
- Added `TlsError::Timeout` variant. [#393] - Added `TlsError::Timeout` variant. [#393]
@ -82,20 +85,20 @@
[#393]: https://github.com/actix/actix-net/pull/393 [#393]: https://github.com/actix/actix-net/pull/393
[#420]: https://github.com/actix/actix-net/pull/420 [#420]: https://github.com/actix/actix-net/pull/420
## 3.0.0-beta.8 - 2021-11-15 ## 3.0.0-beta.8
- Add `Connect::request` for getting a reference to the connection request. [#415] - Add `Connect::request` for getting a reference to the connection request. [#415]
[#415]: https://github.com/actix/actix-net/pull/415 [#415]: https://github.com/actix/actix-net/pull/415
## 3.0.0-beta.7 - 2021-10-20 ## 3.0.0-beta.7
- Add `webpki_roots_cert_store()` to get rustls compatible webpki roots cert store. [#401] - Add `webpki_roots_cert_store()` to get rustls compatible webpki roots cert store. [#401]
- Alias `connect::ssl` to `connect::tls`. [#401] - Alias `connect::ssl` to `connect::tls`. [#401]
[#401]: https://github.com/actix/actix-net/pull/401 [#401]: https://github.com/actix/actix-net/pull/401
## 3.0.0-beta.6 - 2021-10-19 ## 3.0.0-beta.6
- Update `tokio-rustls` to `0.23` which uses `rustls` `0.20`. [#396] - Update `tokio-rustls` to `0.23` which uses `rustls` `0.20`. [#396]
- Removed a re-export of `Session` from `rustls` as it no longer exist. [#396] - Removed a re-export of `Session` from `rustls` as it no longer exist. [#396]
@ -103,7 +106,7 @@
[#396]: https://github.com/actix/actix-net/pull/396 [#396]: https://github.com/actix/actix-net/pull/396
## 3.0.0-beta.5 - 2021-03-29 ## 3.0.0-beta.5
- Changed `connect::ssl::rustls::RustlsConnectorService` to return error when `DNSNameRef` generation failed instead of panic. [#296] - Changed `connect::ssl::rustls::RustlsConnectorService` to return error when `DNSNameRef` generation failed instead of panic. [#296]
- Remove `connect::ssl::openssl::OpensslConnectServiceFactory`. [#297] - Remove `connect::ssl::openssl::OpensslConnectServiceFactory`. [#297]
@ -117,7 +120,7 @@
[#297]: https://github.com/actix/actix-net/pull/297 [#297]: https://github.com/actix/actix-net/pull/297
[#299]: https://github.com/actix/actix-net/pull/299 [#299]: https://github.com/actix/actix-net/pull/299
## 3.0.0-beta.4 - 2021-02-24 ## 3.0.0-beta.4
- Rename `accept::openssl::{SslStream => TlsStream}`. - Rename `accept::openssl::{SslStream => TlsStream}`.
- Add `connect::Connect::set_local_addr` to attach local `IpAddr`. [#282] - Add `connect::Connect::set_local_addr` to attach local `IpAddr`. [#282]
@ -125,7 +128,7 @@
[#282]: https://github.com/actix/actix-net/pull/282 [#282]: https://github.com/actix/actix-net/pull/282
## 3.0.0-beta.3 - 2021-02-06 ## 3.0.0-beta.3
- Remove `trust-dns-proto` and `trust-dns-resolver`. [#248] - Remove `trust-dns-proto` and `trust-dns-resolver`. [#248]
- Use `std::net::ToSocketAddrs` as simple and basic default resolver. [#248] - Use `std::net::ToSocketAddrs` as simple and basic default resolver. [#248]
@ -139,13 +142,13 @@
[#248]: https://github.com/actix/actix-net/pull/248 [#248]: https://github.com/actix/actix-net/pull/248
[#273]: https://github.com/actix/actix-net/pull/273 [#273]: https://github.com/actix/actix-net/pull/273
## 3.0.0-beta.2 - 2022-xx-xx ## 3.0.0-beta.2
- Depend on stable trust-dns packages. [#204] - Depend on stable trust-dns packages. [#204]
[#204]: https://github.com/actix/actix-net/pull/204 [#204]: https://github.com/actix/actix-net/pull/204
## 3.0.0-beta.1 - 2020-12-29 ## 3.0.0-beta.1
- Move acceptors under `accept` module. [#238] - Move acceptors under `accept` module. [#238]
- Merge `actix-connect` crate under `connect` module. [#238] - Merge `actix-connect` crate under `connect` module. [#238]
@ -153,7 +156,7 @@
[#238]: https://github.com/actix/actix-net/pull/238 [#238]: https://github.com/actix/actix-net/pull/238
## 2.0.0 - 2020-09-03 ## 2.0.0
- `nativetls::NativeTlsAcceptor` is renamed to `nativetls::Acceptor`. - `nativetls::NativeTlsAcceptor` is renamed to `nativetls::Acceptor`.
- Where possible, "SSL" terminology is replaced with "TLS". - Where possible, "SSL" terminology is replaced with "TLS".
@ -161,28 +164,28 @@
- `TlsError::Ssl` enum variant is renamed to `TlsError::Tls`. - `TlsError::Ssl` enum variant is renamed to `TlsError::Tls`.
- `max_concurrent_ssl_connect` is renamed to `max_concurrent_tls_connect`. - `max_concurrent_ssl_connect` is renamed to `max_concurrent_tls_connect`.
## 2.0.0-alpha.2 - 2020-08-17 ## 2.0.0-alpha.2
- Update `rustls` dependency to 0.18 - Update `rustls` dependency to 0.18
- Update `tokio-rustls` dependency to 0.14 - Update `tokio-rustls` dependency to 0.14
- Update `webpki-roots` dependency to 0.20 - Update `webpki-roots` dependency to 0.20
## [2.0.0-alpha.1] - 2020-03-03 ## [2.0.0-alpha.1]
- Update `rustls` dependency to 0.17 - Update `rustls` dependency to 0.17
- Update `tokio-rustls` dependency to 0.13 - Update `tokio-rustls` dependency to 0.13
- Update `webpki-roots` dependency to 0.19 - Update `webpki-roots` dependency to 0.19
## [1.0.0] - 2019-12-11 ## [1.0.0]
- 1.0.0 release - 1.0.0 release
## [1.0.0-alpha.3] - 2019-12-07 ## [1.0.0-alpha.3]
- Migrate to tokio 0.2 - Migrate to tokio 0.2
- Enable rustls acceptor service - Enable rustls acceptor service
- Enable native-tls acceptor service - Enable native-tls acceptor service
## [1.0.0-alpha.1] - 2019-12-02 ## [1.0.0-alpha.1]
- Split openssl acceptor from actix-server package - Split openssl acceptor from actix-server package

View File

@ -9,7 +9,7 @@ description = "TLS acceptor and connector services for Actix ecosystem"
keywords = ["network", "tls", "ssl", "async", "transport"] keywords = ["network", "tls", "ssl", "async", "transport"]
repository = "https://github.com/actix/actix-net.git" repository = "https://github.com/actix/actix-net.git"
categories = ["network-programming", "asynchronous", "cryptography"] categories = ["network-programming", "asynchronous", "cryptography"]
license = "MIT OR Apache-2.0" license.workspace = true
edition.workspace = true edition.workspace = true
rust-version.workspace = true rust-version.workspace = true
@ -29,8 +29,14 @@ connect = []
# use openssl impls # use openssl impls
openssl = ["tls-openssl", "tokio-openssl"] openssl = ["tls-openssl", "tokio-openssl"]
# use rustls impls # alias for backwards compat
rustls = ["tokio-rustls", "webpki-roots"] rustls = ["rustls-0_20"]
# use rustls v0.20 impls
rustls-0_20 = ["tokio-rustls-023", "webpki-roots-022"]
# use rustls v0.21 impls
rustls-0_21 = ["tokio-rustls-024", "webpki-roots-025"]
# use native-tls impls # use native-tls impls
native-tls = ["tokio-native-tls"] native-tls = ["tokio-native-tls"]
@ -57,9 +63,13 @@ http = { version = "0.2.3", optional = true }
tls-openssl = { package = "openssl", version = "0.10.48", optional = true } tls-openssl = { package = "openssl", version = "0.10.48", optional = true }
tokio-openssl = { version = "0.6", optional = true } tokio-openssl = { version = "0.6", optional = true }
# rustls # rustls v0.20
tokio-rustls = { version = "0.23", optional = true } tokio-rustls-023 = { package = "tokio-rustls", version = "0.23", optional = true }
webpki-roots = { version = "0.22", optional = true } webpki-roots-022 = { package = "webpki-roots", version = "0.22", optional = true }
# rustls v0.21
tokio-rustls-024 = { package = "tokio-rustls", version = "0.24", optional = true }
webpki-roots-025 = { package = "webpki-roots", version = "0.25", optional = true }
# native-tls # native-tls
tokio-native-tls = { version = "0.3", optional = true } tokio-native-tls = { version = "0.3", optional = true }
@ -72,11 +82,11 @@ bytes = "1"
env_logger = "0.10" env_logger = "0.10"
futures-util = { version = "0.3.17", default-features = false, features = ["sink"] } futures-util = { version = "0.3.17", default-features = false, features = ["sink"] }
log = "0.4" log = "0.4"
rcgen = "0.10" rcgen = "0.11"
rustls-pemfile = "1" rustls-pemfile = "1"
tokio-rustls = { version = "0.23", features = ["dangerous_configuration"] } tokio-rustls-024 = { package = "tokio-rustls", version = "0.24", features = ["dangerous_configuration"] }
trust-dns-resolver = "0.22" trust-dns-resolver = "0.23"
[[example]] [[example]]
name = "accept-rustls" name = "accept-rustls"
required-features = ["accept", "rustls"] required-features = ["accept", "rustls-0_21"]

View File

@ -17,12 +17,13 @@
#[rustfmt::skip] #[rustfmt::skip]
// this `use` is only exists because of how we have organised the crate // this `use` is only exists because of how we have organised the crate
// it is not necessary for your actual code; you should import from `rustls` directly // it is not necessary for your actual code; you should import from `rustls` normally
use tokio_rustls::rustls; use tokio_rustls_024::rustls;
use std::{ use std::{
fs::File, fs::File,
io::{self, BufReader}, io::{self, BufReader},
path::PathBuf,
sync::{ sync::{
atomic::{AtomicUsize, Ordering}, atomic::{AtomicUsize, Ordering},
Arc, Arc,
@ -32,7 +33,7 @@ use std::{
use actix_rt::net::TcpStream; use actix_rt::net::TcpStream;
use actix_server::Server; use actix_server::Server;
use actix_service::ServiceFactoryExt as _; use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::rustls::{Acceptor as RustlsAcceptor, TlsStream}; use actix_tls::accept::rustls_0_21::{Acceptor as RustlsAcceptor, TlsStream};
use futures_util::future::ok; use futures_util::future::ok;
use rustls::{server::ServerConfig, Certificate, PrivateKey}; use rustls::{server::ServerConfig, Certificate, PrivateKey};
use rustls_pemfile::{certs, rsa_private_keys}; use rustls_pemfile::{certs, rsa_private_keys};
@ -42,9 +43,16 @@ use tracing::info;
async fn main() -> io::Result<()> { async fn main() -> io::Result<()> {
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info")); env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
let root_path = env!("CARGO_MANIFEST_DIR")
.parse::<PathBuf>()
.unwrap()
.join("examples");
let cert_path = root_path.clone().join("cert.pem");
let key_path = root_path.clone().join("key.pem");
// Load TLS key and cert files // Load TLS key and cert files
let cert_file = &mut BufReader::new(File::open("./examples/cert.pem").unwrap()); let cert_file = &mut BufReader::new(File::open(cert_path).unwrap());
let key_file = &mut BufReader::new(File::open("./examples/key.pem").unwrap()); let key_file = &mut BufReader::new(File::open(key_path).unwrap());
let cert_chain = certs(cert_file) let cert_chain = certs(cert_file)
.unwrap() .unwrap()
@ -64,7 +72,7 @@ async fn main() -> io::Result<()> {
let count = Arc::new(AtomicUsize::new(0)); let count = Arc::new(AtomicUsize::new(0));
let addr = ("127.0.0.1", 8443); let addr = ("127.0.0.1", 8443);
info!("starting server on port: {}", &addr.0); info!("starting server at: {addr:?}");
Server::build() Server::build()
.bind("tls-example", addr, move || { .bind("tls-example", addr, move || {

View File

@ -12,15 +12,27 @@ use actix_utils::counter::Counter;
#[cfg(feature = "openssl")] #[cfg(feature = "openssl")]
pub mod openssl; pub mod openssl;
#[cfg(feature = "rustls")] #[cfg(feature = "rustls-0_20")]
pub mod rustls; pub mod rustls_0_20;
#[doc(hidden)]
#[cfg(feature = "rustls-0_20")]
pub use rustls_0_20 as rustls;
#[cfg(feature = "rustls-0_21")]
pub mod rustls_0_21;
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
pub mod native_tls; pub mod native_tls;
pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256); pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256);
#[cfg(any(feature = "openssl", feature = "rustls", feature = "native-tls"))] #[cfg(any(
feature = "openssl",
feature = "rustls-0_20",
feature = "rustls-0_21",
feature = "native-tls",
))]
pub(crate) const DEFAULT_TLS_HANDSHAKE_TIMEOUT: std::time::Duration = pub(crate) const DEFAULT_TLS_HANDSHAKE_TIMEOUT: std::time::Duration =
std::time::Duration::from_secs(3); std::time::Duration::from_secs(3);

View File

@ -1,4 +1,4 @@
//! `rustls` based TLS connection acceptor service. //! `rustls` v0.20 based TLS connection acceptor service.
//! //!
//! See [`Acceptor`] for main service factory docs. //! See [`Acceptor`] for main service factory docs.
@ -24,13 +24,14 @@ use actix_utils::{
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio_rustls::{rustls::ServerConfig, Accept, TlsAcceptor}; use tokio_rustls::{rustls::ServerConfig, Accept, TlsAcceptor};
use tokio_rustls_023 as tokio_rustls;
use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER}; use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
pub mod reexports { pub mod reexports {
//! Re-exports from `rustls` that are useful for acceptors. //! Re-exports from `rustls` that are useful for acceptors.
pub use tokio_rustls::rustls::ServerConfig; pub use tokio_rustls_023::rustls::ServerConfig;
} }
/// Wraps a `rustls` based async TLS stream in order to implement [`ActixStream`]. /// Wraps a `rustls` based async TLS stream in order to implement [`ActixStream`].

View File

@ -0,0 +1,198 @@
//! `rustls` v0.21 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::{rustls::ServerConfig, Accept, TlsAcceptor};
use tokio_rustls_024 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_024::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<ServerConfig>,
handshake_timeout: Duration,
}
impl Acceptor {
/// Constructs `rustls` based acceptor service factory.
pub fn new(config: 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)),
}
}
}

View File

@ -27,8 +27,15 @@ mod uri;
#[cfg(feature = "openssl")] #[cfg(feature = "openssl")]
pub mod openssl; pub mod openssl;
#[cfg(feature = "rustls")] #[cfg(feature = "rustls-0_20")]
pub mod rustls; pub mod rustls_0_20;
#[doc(hidden)]
#[cfg(feature = "rustls-0_20")]
pub use rustls_0_20 as rustls;
#[cfg(feature = "rustls-0_21")]
pub mod rustls_0_21;
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
pub mod native_tls; pub mod native_tls;

View File

@ -20,22 +20,21 @@ use tokio_rustls::{
rustls::{client::ServerName, ClientConfig, OwnedTrustAnchor, RootCertStore}, rustls::{client::ServerName, ClientConfig, OwnedTrustAnchor, RootCertStore},
Connect as RustlsConnect, TlsConnector as RustlsTlsConnector, Connect as RustlsConnect, TlsConnector as RustlsTlsConnector,
}; };
use tracing::trace; use tokio_rustls_023 as tokio_rustls;
use webpki_roots::TLS_SERVER_ROOTS;
use crate::connect::{Connection, Host}; use crate::connect::{Connection, Host};
pub mod reexports { pub mod reexports {
//! Re-exports from `rustls` and `webpki_roots` that are useful for connectors. //! Re-exports from `rustls` and `webpki_roots` that are useful for connectors.
pub use tokio_rustls::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig}; pub use tokio_rustls_023::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig};
pub use webpki_roots::TLS_SERVER_ROOTS; pub use webpki_roots_022::TLS_SERVER_ROOTS;
} }
/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store. /// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store.
pub fn webpki_roots_cert_store() -> RootCertStore { pub fn webpki_roots_cert_store() -> RootCertStore {
let mut root_certs = RootCertStore::empty(); let mut root_certs = RootCertStore::empty();
for cert in TLS_SERVER_ROOTS.0 { for cert in webpki_roots_022::TLS_SERVER_ROOTS.0 {
let cert = OwnedTrustAnchor::from_subject_spki_name_constraints( let cert = OwnedTrustAnchor::from_subject_spki_name_constraints(
cert.subject, cert.subject,
cert.spki, cert.spki,
@ -102,12 +101,13 @@ where
actix_service::always_ready!(); actix_service::always_ready!();
fn call(&self, connection: Connection<R, IO>) -> Self::Future { fn call(&self, connection: Connection<R, IO>) -> Self::Future {
trace!("TLS handshake start for: {:?}", connection.hostname()); tracing::trace!("TLS handshake start for: {:?}", connection.hostname());
let (stream, connection) = connection.replace_io(()); let (stream, connection) = connection.replace_io(());
match ServerName::try_from(connection.hostname()) { match ServerName::try_from(connection.hostname()) {
Ok(host) => ConnectFut::Future { Ok(host) => ConnectFut::Future {
connect: RustlsTlsConnector::from(self.connector.clone()).connect(host, stream), connect: RustlsTlsConnector::from(Arc::clone(&self.connector))
.connect(host, stream),
connection: Some(connection), connection: Some(connection),
}, },
Err(_) => ConnectFut::InvalidDns, Err(_) => ConnectFut::InvalidDns,
@ -117,6 +117,7 @@ where
/// Connect future for Rustls service. /// Connect future for Rustls service.
#[doc(hidden)] #[doc(hidden)]
#[allow(clippy::large_enum_variant)]
pub enum ConnectFut<R, IO> { pub enum ConnectFut<R, IO> {
/// See issue <https://github.com/briansmith/webpki/issues/54> /// See issue <https://github.com/briansmith/webpki/issues/54>
InvalidDns, InvalidDns,
@ -131,17 +132,23 @@ where
R: Host, R: Host,
IO: ActixStream, IO: ActixStream,
{ {
type Output = Result<Connection<R, AsyncTlsStream<IO>>, io::Error>; type Output = io::Result<Connection<R, AsyncTlsStream<IO>>>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.get_mut() { match self.get_mut() {
Self::InvalidDns => Poll::Ready(Err( Self::InvalidDns => Poll::Ready(Err(io::Error::new(
io::Error::new(io::ErrorKind::Other, "rustls currently only handles hostname-based connections. See https://github.com/briansmith/webpki/issues/54") io::ErrorKind::Other,
)), "Rustls v0.20 can only handle hostname-based connections. Enable the `rustls-0_21` \
Self::Future { connect, connection } => { feature and use the Rustls v0.21 utilities to gain this feature.",
))),
Self::Future {
connect,
connection,
} => {
let stream = ready!(Pin::new(connect).poll(cx))?; let stream = ready!(Pin::new(connect).poll(cx))?;
let connection = connection.take().unwrap(); let connection = connection.take().unwrap();
trace!("TLS handshake success: {:?}", connection.hostname()); tracing::trace!("TLS handshake success: {:?}", connection.hostname());
Poll::Ready(Ok(connection.replace_io(stream).1)) Poll::Ready(Ok(connection.replace_io(stream).1))
} }
} }

View File

@ -0,0 +1,154 @@
//! Rustls based connector service.
//!
//! See [`TlsConnector`] for main connector service factory docs.
use std::{
convert::TryFrom,
future::Future,
io,
pin::Pin,
sync::Arc,
task::{Context, Poll},
};
use actix_rt::net::ActixStream;
use actix_service::{Service, ServiceFactory};
use actix_utils::future::{ok, Ready};
use futures_core::ready;
use tokio_rustls::{
client::TlsStream as AsyncTlsStream,
rustls::{client::ServerName, ClientConfig, OwnedTrustAnchor, RootCertStore},
Connect as RustlsConnect, TlsConnector as RustlsTlsConnector,
};
use tokio_rustls_024 as tokio_rustls;
use crate::connect::{Connection, Host};
pub mod reexports {
//! Re-exports from `rustls` and `webpki_roots` that are useful for connectors.
pub use tokio_rustls_024::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig};
pub use webpki_roots_025::TLS_SERVER_ROOTS;
}
/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store.
pub fn webpki_roots_cert_store() -> RootCertStore {
let mut root_certs = RootCertStore::empty();
for cert in webpki_roots_025::TLS_SERVER_ROOTS {
let cert = OwnedTrustAnchor::from_subject_spki_name_constraints(
cert.subject,
cert.spki,
cert.name_constraints,
);
let certs = vec![cert].into_iter();
root_certs.add_trust_anchors(certs);
}
root_certs
}
/// Connector service factory using `rustls`.
#[derive(Clone)]
pub struct TlsConnector {
connector: Arc<ClientConfig>,
}
impl TlsConnector {
/// Constructs new connector service factory from a `rustls` client configuration.
pub fn new(connector: Arc<ClientConfig>) -> Self {
TlsConnector { connector }
}
/// Constructs new connector service from a `rustls` client configuration.
pub fn service(connector: Arc<ClientConfig>) -> TlsConnectorService {
TlsConnectorService { connector }
}
}
impl<R, IO> ServiceFactory<Connection<R, IO>> for TlsConnector
where
R: Host,
IO: ActixStream + 'static,
{
type Response = Connection<R, AsyncTlsStream<IO>>;
type Error = io::Error;
type Config = ();
type Service = TlsConnectorService;
type InitError = ();
type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future {
ok(TlsConnectorService {
connector: self.connector.clone(),
})
}
}
/// Connector service using `rustls`.
#[derive(Clone)]
pub struct TlsConnectorService {
connector: Arc<ClientConfig>,
}
impl<R, IO> Service<Connection<R, IO>> for TlsConnectorService
where
R: Host,
IO: ActixStream,
{
type Response = Connection<R, AsyncTlsStream<IO>>;
type Error = io::Error;
type Future = ConnectFut<R, IO>;
actix_service::always_ready!();
fn call(&self, connection: Connection<R, IO>) -> Self::Future {
tracing::trace!("TLS handshake start for: {:?}", connection.hostname());
let (stream, connection) = connection.replace_io(());
match ServerName::try_from(connection.hostname()) {
Ok(host) => ConnectFut::Future {
connect: RustlsTlsConnector::from(Arc::clone(&self.connector))
.connect(host, stream),
connection: Some(connection),
},
Err(_) => ConnectFut::InvalidServerName,
}
}
}
/// Connect future for Rustls service.
#[doc(hidden)]
#[allow(clippy::large_enum_variant)]
pub enum ConnectFut<R, IO> {
InvalidServerName,
Future {
connect: RustlsConnect<IO>,
connection: Option<Connection<R, ()>>,
},
}
impl<R, IO> Future for ConnectFut<R, IO>
where
R: Host,
IO: ActixStream,
{
type Output = io::Result<Connection<R, AsyncTlsStream<IO>>>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.get_mut() {
Self::InvalidServerName => Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidInput,
"connection parameters specified invalid server name",
))),
Self::Future {
connect,
connection,
} => {
let stream = ready!(Pin::new(connect).poll(cx))?;
let connection = connection.take().unwrap();
tracing::trace!("TLS handshake success: {:?}", connection.hostname());
Poll::Ready(Ok(connection.replace_io(stream).1))
}
}
}
}

View File

@ -3,7 +3,7 @@
#![cfg(all( #![cfg(all(
feature = "accept", feature = "accept",
feature = "connect", feature = "connect",
feature = "rustls", feature = "rustls-0_21",
feature = "openssl" feature = "openssl"
))] ))]
@ -15,6 +15,7 @@ use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::openssl::{Acceptor, TlsStream}; use actix_tls::accept::openssl::{Acceptor, TlsStream};
use actix_utils::future::ok; use actix_utils::future::ok;
use tokio_rustls::rustls::{Certificate, ClientConfig, RootCertStore, ServerName}; use tokio_rustls::rustls::{Certificate, ClientConfig, RootCertStore, ServerName};
use tokio_rustls_024 as tokio_rustls;
fn new_cert_and_key() -> (String, String) { fn new_cert_and_key() -> (String, String) {
let cert = let cert =
@ -49,7 +50,7 @@ fn openssl_acceptor(cert: String, key: String) -> tls_openssl::ssl::SslAcceptor
mod danger { mod danger {
use std::time::SystemTime; use std::time::SystemTime;
use tokio_rustls::rustls::{ use tokio_rustls_024::rustls::{
self, self,
client::{ServerCertVerified, ServerCertVerifier}, client::{ServerCertVerified, ServerCertVerifier},
}; };

View File

@ -3,7 +3,7 @@
#![cfg(all( #![cfg(all(
feature = "accept", feature = "accept",
feature = "connect", feature = "connect",
feature = "rustls", feature = "rustls-0_21",
feature = "openssl" feature = "openssl"
))] ))]
@ -15,13 +15,14 @@ use actix_rt::net::TcpStream;
use actix_server::TestServer; use actix_server::TestServer;
use actix_service::ServiceFactoryExt as _; use actix_service::ServiceFactoryExt as _;
use actix_tls::{ use actix_tls::{
accept::rustls::{Acceptor, TlsStream}, accept::rustls_0_21::{Acceptor, TlsStream},
connect::openssl::reexports::SslConnector, connect::openssl::reexports::SslConnector,
}; };
use actix_utils::future::ok; use actix_utils::future::ok;
use rustls_pemfile::{certs, pkcs8_private_keys}; use rustls_pemfile::{certs, pkcs8_private_keys};
use tls_openssl::ssl::SslVerifyMode; use tls_openssl::ssl::SslVerifyMode;
use tokio_rustls::rustls::{self, Certificate, PrivateKey, ServerConfig}; use tokio_rustls::rustls::{self, Certificate, PrivateKey, ServerConfig};
use tokio_rustls_024 as tokio_rustls;
fn new_cert_and_key() -> (String, String) { fn new_cert_and_key() -> (String, String) {
let cert = let cert =

View File

@ -30,7 +30,7 @@ async fn test_string() {
assert_eq!(con.peer_addr().unwrap(), srv.addr()); assert_eq!(con.peer_addr().unwrap(), srv.addr());
} }
#[cfg(feature = "rustls")] #[cfg(feature = "rustls-0_21")]
#[actix_rt::test] #[actix_rt::test]
async fn test_rustls_string() { async fn test_rustls_string() {
let srv = TestServer::start(|| { let srv = TestServer::start(|| {
@ -114,7 +114,7 @@ async fn test_openssl_uri() {
assert_eq!(con.peer_addr().unwrap(), srv.addr()); assert_eq!(con.peer_addr().unwrap(), srv.addr());
} }
#[cfg(all(feature = "rustls", feature = "uri"))] #[cfg(all(feature = "rustls-0_21", feature = "uri"))]
#[actix_rt::test] #[actix_rt::test]
async fn test_rustls_uri() { async fn test_rustls_uri() {
use std::convert::TryFrom; use std::convert::TryFrom;

View File

@ -29,7 +29,7 @@ impl Counter {
/// Returns true if counter is below capacity. Otherwise, register to wake task when it is. /// Returns true if counter is below capacity. Otherwise, register to wake task when it is.
#[inline] #[inline]
pub fn available(&self, cx: &mut task::Context<'_>) -> bool { pub fn available(&self, cx: &task::Context<'_>) -> bool {
self.0.available(cx) self.0.available(cx)
} }
@ -59,7 +59,7 @@ impl CounterInner {
} }
} }
fn available(&self, cx: &mut task::Context<'_>) -> bool { fn available(&self, cx: &task::Context<'_>) -> bool {
if self.count.get() < self.capacity { if self.count.get() < self.capacity {
true true
} else { } else {

View File

@ -62,6 +62,7 @@ where
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// SAFETY: we are not moving out of the pinned field // SAFETY: we are not moving out of the pinned field
// see https://github.com/rust-lang/rust/pull/102737 // see https://github.com/rust-lang/rust/pull/102737
#[allow(clippy::needless_borrow)]
(unsafe { &mut self.get_unchecked_mut().f })(cx) (unsafe { &mut self.get_unchecked_mut().f })(cx)
} }
} }