diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8c0dca6..3f2c594 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,6 +34,14 @@ jobs: platform: linux/arm64 tags-suffix: "-slim" + - target: "musl" + platform: linux/amd64 + tags-suffix: "-slim-pq" + + - target: "musl" + platform: linux/arm64 + tags-suffix: "-slim-pq" + - target: "gnu" build-feature: "-s2n" platform: linux/amd64 @@ -44,6 +52,26 @@ jobs: platform: linux/arm64 tags-suffix: "-s2n" + - target: "gnu" + build-feature: "-pq" + platform: linux/amd64 + tags-suffix: "-pq" + + - target: "gnu" + build-feature: "-pq" + platform: linux/arm64 + tags-suffix: "-pq" + + - target: "gnu" + build-feature: "-s2n-pq" + platform: linux/amd64 + tags-suffix: "-s2n-pq" + + - target: "gnu" + build-feature: "-s2n" + platform: linux/arm64 + tags-suffix: "-s2n-pq" + - target: "gnu" build-feature: "-webpki-roots" platform: linux/amd64 diff --git a/.github/workflows/release_docker.yml b/.github/workflows/release_docker.yml index c018272..7a3105d 100644 --- a/.github/workflows/release_docker.yml +++ b/.github/workflows/release_docker.yml @@ -30,6 +30,17 @@ jobs: jqtype/rpxy:latest ghcr.io/junkurihara/rust-rpxy:latest + - target: "default-pq" + dockerfile: ./docker/Dockerfile + platforms: linux/amd64,linux/arm64 + build-args: | + "CARGO_FEATURES=--no-default-features --features=http3-quinn,cache,rustls-backend,acme,post-quantum" + tags-suffix: "-pq" + # Aliases must be used only for release builds + aliases: | + jqtype/rpxy:pq + ghcr.io/junkurihara/rust-rpxy:pq + - target: "default-slim" dockerfile: ./docker/Dockerfile-slim build-contexts: | @@ -42,6 +53,20 @@ jobs: jqtype/rpxy:slim ghcr.io/junkurihara/rust-rpxy:slim + - target: "default-slim-pq" + dockerfile: ./docker/Dockerfile-slim + build-args: | + "CARGO_FEATURES=--no-default-features --features=http3-quinn,cache,rustls-backend,acme,post-quantum" + build-contexts: | + messense/rust-musl-cross:amd64-musl=docker-image://messense/rust-musl-cross:x86_64-musl + messense/rust-musl-cross:arm64-musl=docker-image://messense/rust-musl-cross:aarch64-musl + platforms: linux/amd64,linux/arm64 + tags-suffix: "-slim-pq" + # Aliases must be used only for release builds + aliases: | + jqtype/rpxy:slim-pq + ghcr.io/junkurihara/rust-rpxy:slim-pq + - target: "s2n" dockerfile: ./docker/Dockerfile build-args: | @@ -54,6 +79,18 @@ jobs: jqtype/rpxy:s2n ghcr.io/junkurihara/rust-rpxy:s2n + - target: "s2n-pq" + dockerfile: ./docker/Dockerfile + build-args: | + "CARGO_FEATURES=--no-default-features --features=http3-s2n,cache,rustls-backend,acme,post-quantum" + "ADDITIONAL_DEPS=pkg-config libssl-dev cmake libclang1 gcc g++" + platforms: linux/amd64,linux/arm64 + tags-suffix: "-s2n-pq" + # Aliases must be used only for release builds + aliases: | + jqtype/rpxy:s2n-pq + ghcr.io/junkurihara/rust-rpxy:s2n-pq + - target: "webpki-roots" dockerfile: ./docker/Dockerfile platforms: linux/amd64,linux/arm64 diff --git a/CHANGELOG.md b/CHANGELOG.md index 74a82d0..2bcf601 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,17 @@ # CHANGELOG -## 0.9.3 or 0.10.0 (Unreleased) +## 0.10.0 (Unreleased) + +## 0.9.3 ### Improvement +- Feat: Support post-quantum `X25519Kyber768Draft00` for incoming TLS initiation. This is non-default feature [feature: `post-quantum`]. Upstream connection to backend applications does not yet support PQC. - Feat: emit WARN messages if there exist unused and unsupported options specified in configuration file. - Docs: `rpxy.io` is now available for the official website of `rpxy`. - Refactor: lots of minor improvements - Deps - ## 0.9.2 ### Improvement diff --git a/README.md b/README.md index 504e07d..28e4f62 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,12 @@ [^pure_rust]: Doubtfully can be claimed to be written in pure Rust since current `rpxy` is based on `aws-lc-rs` for cryptographic operations. -By default, `rpxy` provides the *TLS connection sanitization* by correctly binding a certificate used to establish a secure channel with the backend application. Specifically, it always keeps the consistency between the given SNI (server name indication) in `ClientHello` of the underlying TLS and the domain name given by the overlaid HTTP HOST header (or URL in Request line) [^1]. Additionally, as a somewhat unstable feature, our `rpxy` can handle the brand-new HTTP/3 connection thanks to [`quinn`](https://github.com/quinn-rs/quinn), [`s2n-quic`](https://github.com/aws/s2n-quic) and [`hyperium/h3`](https://github.com/hyperium/h3).[^h3lib] Furthermore, `rpxy` supports the automatic issuance and renewal of certificates via [TLS-ALPN-01 (RFC8737)](https://www.rfc-editor.org/rfc/rfc8737) of [ACME protocol (RFC8555)](https://www.rfc-editor.org/rfc/rfc8555) thanks to [`rustls-acme`](https://github.com/FlorianUekermann/rustls-acme). +By default, `rpxy` provides the *TLS connection sanitization* by correctly binding a certificate used to establish a secure channel with the backend application. Specifically, it always keeps the consistency between the given SNI (server name indication) in `ClientHello` of the underlying TLS and the domain name given by the overlaid HTTP HOST header (or URL in Request line) [^1]. Additionally, as a somewhat unstable feature, our `rpxy` can handle the brand-new HTTP/3 connection thanks to [`quinn`](https://github.com/quinn-rs/quinn), [`s2n-quic`](https://github.com/aws/s2n-quic) and [`hyperium/h3`](https://github.com/hyperium/h3).[^h3lib] Furthermore, `rpxy` supports the automatic issuance and renewal of certificates via [TLS-ALPN-01 (RFC8737)](https://www.rfc-editor.org/rfc/rfc8737) of [ACME protocol (RFC8555)](https://www.rfc-editor.org/rfc/rfc8555) thanks to [`rustls-acme`](https://github.com/FlorianUekermann/rustls-acme), and the hybridized post-quantum key exchange [`X25519Kyber768Draft00`](https://datatracker.ietf.org/doc/draft-tls-westerbaan-xyber768d00/)[^kyber] for TLS initiation thanks to `rustls-post-quantum` crate. [^h3lib]: HTTP/3 libraries are mutually exclusive. You need to explicitly specify `s2n-quic` with `--no-default-features` flag. Also note that if you build `rpxy` with `s2n-quic`, then it requires `openssl` just for building the package. + [^kyber]: This is not yet a default feature. You need to specify `--features post-quantum` when building `rpxy`. Also note that `X25519Kyber768Draft00` is a draft version yet this is widely used on the Internet. We will update the feature when the newest version (`X25519MLKEM768` in [`ECDHE-MLKEM`](https://www.ietf.org/archive/id/draft-kwiatkowski-tls-ecdhe-mlkem-02.html)) is available. + This project is still *work-in-progress*. But it is already working in some production environments and serves a number of domain names. Furthermore it *significantly outperforms* NGINX and Caddy, e.g., *1.5x faster than NGINX*, in the setting of a very simple HTTP reverse-proxy scenario (See [`bench`](./bench/) directory). [^1]: We should note that NGINX doesn't guarantee such a consistency by default. To this end, you have to add `if` statement in the configuration file in NGINX. diff --git a/docker/README.md b/docker/README.md index 25f5b86..f5a4c2b 100644 --- a/docker/README.md +++ b/docker/README.md @@ -32,12 +32,14 @@ Differences among tags are summarized as follows. - `latest`: Built from the `main` branch with default features, running on Ubuntu. - `latest-slim`, `slim`: Built by `musl` from the `main` branch with default features, running on Alpine. - `latest-s2n`, `s2n`: Built from the `main` branch with the `http3-s2n` feature, running on Ubuntu. +- `*-pq`: Built with the `post-quantum` feature. This feature supports the post-quantum key exchange using `rustls-post-quantum` crate. ### Nightly Builds - `nightly`: Built from the `develop` branch with default features, running on Ubuntu. - `nightly-slim`: Built by `musl` from the `develop` branch with default features, running on Alpine. - `nightly-s2n`: Built from the `develop` branch with the `http3-s2n` feature, running on Ubuntu. +- `*-pq`: Built with the `post-quantum` feature. This feature supports the hybridized post-quantum key exchange using `rustls-post-quantum` crate. ## Caveats diff --git a/rpxy-acme/Cargo.toml b/rpxy-acme/Cargo.toml index ef46ffa..6b7b96e 100644 --- a/rpxy-acme/Cargo.toml +++ b/rpxy-acme/Cargo.toml @@ -10,10 +10,13 @@ readme.workspace = true edition.workspace = true publish.workspace = true +[features] +post-quantum = ["rustls-post-quantum"] + [dependencies] url = { version = "2.5.2" } rustc-hash = "2.0.0" -thiserror = "1.0.65" +thiserror = "1.0.66" tracing = "0.1.40" async-trait = "0.1.83" base64 = "0.22.1" @@ -21,7 +24,7 @@ aws-lc-rs = { version = "1.10.0", default-features = false, features = [ "aws-lc-sys", ] } blocking = "1.6.1" -rustls = { version = "0.23.15", default-features = false, features = [ +rustls = { version = "0.23.16", default-features = false, features = [ "std", "aws_lc_rs", ] } @@ -29,6 +32,7 @@ rustls-platform-verifier = { version = "0.3.4" } rustls-acme = { path = "../submodules/rustls-acme/", default-features = false, features = [ "aws-lc-rs", ] } +rustls-post-quantum = { version = "0.1.0", optional = true } tokio = { version = "1.41.0", default-features = false } tokio-util = { version = "0.7.12", default-features = false } tokio-stream = { version = "0.1.16", default-features = false } diff --git a/rpxy-acme/src/manager.rs b/rpxy-acme/src/manager.rs index fe10c85..1fe6f9e 100644 --- a/rpxy-acme/src/manager.rs +++ b/rpxy-acme/src/manager.rs @@ -37,8 +37,11 @@ impl AcmeManager { domains: &[String], runtime_handle: Handle, ) -> Result { + #[cfg(not(feature = "post-quantum"))] // Install aws_lc_rs as default crypto provider for rustls let _ = rustls::crypto::CryptoProvider::install_default(rustls::crypto::aws_lc_rs::default_provider()); + #[cfg(feature = "post-quantum")] + let _ = rustls::crypto::CryptoProvider::install_default(rustls_post_quantum::provider()); let acme_registry_dir = acme_registry_dir .map(|v| v.to_ascii_lowercase()) diff --git a/rpxy-bin/Cargo.toml b/rpxy-bin/Cargo.toml index 96c552c..50173fe 100644 --- a/rpxy-bin/Cargo.toml +++ b/rpxy-bin/Cargo.toml @@ -13,6 +13,8 @@ publish.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] +# default = ["http3-quinn", "cache", "rustls-backend", "acme", "post-quantum"] +# default = ["http3-s2n", "cache", "rustls-backend", "acme", "post-quantum"] default = ["http3-quinn", "cache", "rustls-backend", "acme"] # default = ["http3-s2n", "cache", "rustls-backend", "acme"] http3-quinn = ["rpxy-lib/http3-quinn"] @@ -22,6 +24,7 @@ rustls-backend = ["rpxy-lib/rustls-backend"] webpki-roots = ["rpxy-lib/webpki-roots"] cache = ["rpxy-lib/cache"] acme = ["rpxy-lib/acme", "rpxy-acme"] +post-quantum = ["rpxy-lib/post-quantum"] [dependencies] rpxy-lib = { path = "../rpxy-lib/", default-features = false, features = [ @@ -31,7 +34,7 @@ rpxy-lib = { path = "../rpxy-lib/", default-features = false, features = [ mimalloc = { version = "*", default-features = false } anyhow = "1.0.91" rustc-hash = "2.0.0" -serde = { version = "1.0.213", default-features = false, features = ["derive"] } +serde = { version = "1.0.214", default-features = false, features = ["derive"] } tokio = { version = "1.41.0", default-features = false, features = [ "net", "rt-multi-thread", diff --git a/rpxy-certs/Cargo.toml b/rpxy-certs/Cargo.toml index b96e251..ba39e6c 100644 --- a/rpxy-certs/Cargo.toml +++ b/rpxy-certs/Cargo.toml @@ -12,16 +12,17 @@ publish.workspace = true [features] default = ["http3"] +post-quantum = ["rustls-post-quantum"] http3 = [] [dependencies] rustc-hash = { version = "2.0.0" } tracing = { version = "0.1.40" } derive_builder = { version = "0.20.2" } -thiserror = { version = "1.0.65" } +thiserror = { version = "1.0.66" } hot_reload = { version = "0.1.6" } async-trait = { version = "0.1.83" } -rustls = { version = "0.23.15", default-features = false, features = [ +rustls = { version = "0.23.16", default-features = false, features = [ "std", "aws_lc_rs", ] } @@ -30,6 +31,7 @@ rustls-webpki = { version = "0.102.8", default-features = false, features = [ "std", "aws_lc_rs", ] } +rustls-post-quantum = { version = "0.1.0", optional = true } x509-parser = { version = "0.16.0" } [dev-dependencies] diff --git a/rpxy-certs/src/lib.rs b/rpxy-certs/src/lib.rs index 6a262f0..43994a1 100644 --- a/rpxy-certs/src/lib.rs +++ b/rpxy-certs/src/lib.rs @@ -12,7 +12,7 @@ mod log { use crate::{error::*, log::*, reloader_service::DynCryptoSource}; use hot_reload::{ReloaderReceiver, ReloaderService}; use rustc_hash::FxHashMap as HashMap; -use rustls::crypto::{aws_lc_rs, CryptoProvider}; +use rustls::crypto::CryptoProvider; use std::sync::Arc; /* ------------------------------------------------ */ @@ -44,8 +44,11 @@ where T: CryptoSource + Send + Sync + Clone + 'static, { info!("Building certificate reloader service"); + #[cfg(not(feature = "post-quantum"))] // Install aws_lc_rs as default crypto provider for rustls - let _ = CryptoProvider::install_default(aws_lc_rs::default_provider()); + let _ = CryptoProvider::install_default(rustls::crypto::aws_lc_rs::default_provider()); + #[cfg(feature = "post-quantum")] + let _ = CryptoProvider::install_default(rustls_post_quantum::provider()); let source = crypto_source_map .iter() diff --git a/rpxy-certs/src/server_crypto.rs b/rpxy-certs/src/server_crypto.rs index 60ffcba..71426c8 100644 --- a/rpxy-certs/src/server_crypto.rs +++ b/rpxy-certs/src/server_crypto.rs @@ -179,7 +179,10 @@ mod tests { #[tokio::test] async fn test_server_crypto_base_try_into() { + #[cfg(not(feature = "post-quantum"))] let _ = CryptoProvider::install_default(rustls::crypto::aws_lc_rs::default_provider()); + #[cfg(feature = "post-quantum")] + let _ = CryptoProvider::install_default(rustls_post_quantum::provider()); let mut server_crypto_base = ServerCryptoBase::default(); diff --git a/rpxy-lib/Cargo.toml b/rpxy-lib/Cargo.toml index e50dc2a..dc5d09a 100644 --- a/rpxy-lib/Cargo.toml +++ b/rpxy-lib/Cargo.toml @@ -28,6 +28,11 @@ native-tls-backend = ["hyper-tls"] rustls-backend = ["hyper-rustls"] webpki-roots = ["rustls-backend", "hyper-rustls/webpki-tokio"] acme = ["dep:rpxy-acme"] +post-quantum = [ + "rustls-post-quantum", + "rpxy-acme/post-quantum", + "rpxy-certs/post-quantum", +] [dependencies] rand = "0.8.5" @@ -49,13 +54,13 @@ async-trait = "0.1.83" # Error handling anyhow = "1.0.91" -thiserror = "1.0.65" +thiserror = "1.0.66" # http for both server and client http = "1.1.0" http-body-util = "0.1.2" hyper = { version = "1.5.0", default-features = false } -hyper-util = { version = "0.1.9", features = ["full"] } +hyper-util = { version = "0.1.10", features = ["full"] } futures-util = { version = "0.3.31", default-features = false } futures-channel = { version = "0.3.31", default-features = false } @@ -74,7 +79,8 @@ hyper-rustls = { version = "0.27.3", default-features = false, features = [ # tls and cert management for server rpxy-certs = { path = "../rpxy-certs/", default-features = false } hot_reload = "0.1.6" -rustls = { version = "0.23.15", default-features = false } +rustls = { version = "0.23.16", default-features = false } +rustls-post-quantum = { version = "0.1.0", optional = true } tokio-rustls = { version = "0.26.0", features = ["early-data"] } # acme diff --git a/rpxy-lib/src/lib.rs b/rpxy-lib/src/lib.rs index c1ee44f..b12d5e0 100644 --- a/rpxy-lib/src/lib.rs +++ b/rpxy-lib/src/lib.rs @@ -22,7 +22,7 @@ use crate::{ use futures::future::join_all; use hot_reload::ReloaderReceiver; use rpxy_certs::ServerCryptoBase; -use rustls::crypto::{aws_lc_rs, CryptoProvider}; +use rustls::crypto::CryptoProvider; use std::sync::Arc; use tokio_util::sync::CancellationToken; @@ -101,8 +101,11 @@ pub async fn entrypoint( info!("Cache is disabled") } + #[cfg(not(feature = "post-quantum"))] // Install aws_lc_rs as default crypto provider for rustls - let _ = CryptoProvider::install_default(aws_lc_rs::default_provider()); + let _ = CryptoProvider::install_default(rustls::crypto::aws_lc_rs::default_provider()); + #[cfg(feature = "post-quantum")] + let _ = CryptoProvider::install_default(rustls_post_quantum::provider()); // 1. build backends, and make it contained in Arc let app_manager = Arc::new(backend::BackendAppManager::try_from(app_config_list)?);