From 97541afaedcc465e12a8305b4b5623a9fc95bd17 Mon Sep 17 00:00:00 2001 From: Yujia Qiao Date: Sun, 1 Oct 2023 17:13:17 +0800 Subject: [PATCH 01/15] chore: fix build - Drop tls for embedded devices in release - Upgrade cross - Specify rust 1.71 --- .github/workflows/release.yml | 50 ++++++++++++++++++++++++++++++----- Cargo.toml | 4 +++ 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4fc1d3fb..fa71f278 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,59 +20,95 @@ jobs: - os: ubuntu-latest target: x86_64-unknown-linux-gnu exe: rathole - - os: ubuntu-latest - target: x86_64-unknown-linux-musl - exe: rathole + cross: false - os: ubuntu-latest target: aarch64-unknown-linux-musl exe: rathole + cross: true - os: ubuntu-latest target: arm-unknown-linux-musleabi exe: rathole + cross: true - os: ubuntu-latest target: arm-unknown-linux-musleabihf exe: rathole + cross: true - os: ubuntu-latest target: armv7-unknown-linux-musleabihf exe: rathole + cross: true - os: ubuntu-latest target: mips-unknown-linux-gnu exe: rathole + cross: true - os: ubuntu-latest target: mips-unknown-linux-musl exe: rathole + cross: true - os: ubuntu-latest target: mipsel-unknown-linux-gnu exe: rathole + cross: true - os: ubuntu-latest target: mipsel-unknown-linux-musl exe: rathole + cross: true - os: ubuntu-latest target: mips64-unknown-linux-gnuabi64 exe: rathole + cross: true - os: ubuntu-latest target: mips64el-unknown-linux-gnuabi64 exe: rathole + cross: true - os: macos-latest target: x86_64-apple-darwin exe: rathole + cross: false - os: windows-latest target: x86_64-pc-windows-msvc exe: rathole.exe + cross: false steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: stable + # Since rust 1.72, some platforms are tier 3 + toolchain: 1.71 + default: true + + - name: Install OpenSSL + if: matrix.os == 'ubuntu-latest' + run: sudo apt-get install pkg-config libssl-dev + - name: Install OpenSSL + if: matrix.os == 'macos-latest' + run: brew install openssl@3 + + # Native build + - name: Install target + if: matrix.cross == false + run: rustup target add ${{ matrix.target }} + - name: Run tests + if: matrix.cross == false + run: cargo test --release --target ${{ matrix.target }} --verbose + - name: Build release + if: matrix.cross == false + run: cargo build --release --target ${{ matrix.target }} + + # Cross build - name: Install cross - run: cargo install --version 0.1.16 cross + if: matrix.cross + run: cargo install --version 0.2.5 cross - name: Run tests - run: cross test --release --target ${{ matrix.target }} --verbose + if: matrix.cross + run: cross test --release --target ${{ matrix.target }} --verbose --features embedded --no-default-features - name: Build release - run: cross build --release --target ${{ matrix.target }} + if: matrix.cross + run: cross build --release --target ${{ matrix.target }} --features embedded --no-default-features + - name: Run UPX # Upx may not support some platforms. Ignore the errors continue-on-error: true diff --git a/Cargo.toml b/Cargo.toml index 501e8fab..1311f889 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,10 @@ websocket = ["tokio-tungstenite", "tokio-util", "futures-core", "futures-sink", # Configuration hot-reload support hot-reload = ["notify"] +# Default feature releasing embedded devices +# Cross-compiling with tls is hard. So we don't :( +embedded = ["server", "client", "hot-reload", "noise"] + # Feature to enable tokio-console. Disabled by default. # Don't enable it unless for debugging purposes. console = ["console-subscriber", "tokio/tracing"] From 2ccb386cea31bcacda2105500d9cec58495a2ebe Mon Sep 17 00:00:00 2001 From: Yujia Qiao Date: Sun, 1 Oct 2023 17:50:15 +0800 Subject: [PATCH 02/15] chore(test): update to be feature aware --- tests/integration_test.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 807f6fb2..3b8dec90 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -57,10 +57,17 @@ async fn tcp() -> Result<()> { test("tests/for_tcp/tcp_transport.toml", Type::Tcp).await?; // FIXME: Self-signed certificate on Mac requires mannual interference. Disable CI for now #[cfg(not(target_os = "macos"))] + #[cfg(feature="tls")] test("tests/for_tcp/tls_transport.toml", Type::Tcp).await?; + + #[cfg(feature="noise")] test("tests/for_tcp/noise_transport.toml", Type::Tcp).await?; + + #[cfg(feature="websocket")] test("tests/for_tcp/websocket_transport.toml", Type::Tcp).await?; + #[cfg(not(target_os = "macos"))] + #[cfg(feature="websocket")] test("tests/for_tcp/websocket_tls_transport.toml", Type::Tcp).await?; Ok(()) @@ -87,10 +94,17 @@ async fn udp() -> Result<()> { test("tests/for_udp/tcp_transport.toml", Type::Udp).await?; // See above #[cfg(not(target_os = "macos"))] + #[cfg(feature="tls")] test("tests/for_udp/tls_transport.toml", Type::Udp).await?; + + #[cfg(feature="noise")] test("tests/for_udp/noise_transport.toml", Type::Udp).await?; + + #[cfg(feature="websocket")] test("tests/for_udp/websocket_transport.toml", Type::Udp).await?; + #[cfg(not(target_os = "macos"))] + #[cfg(feature="websocket")] test("tests/for_udp/websocket_tls_transport.toml", Type::Udp).await?; Ok(()) From ebb764ae53d7ffe4fcb45f83f7563bec5c74199d Mon Sep 17 00:00:00 2001 From: Yujia Qiao Date: Sun, 1 Oct 2023 17:55:18 +0800 Subject: [PATCH 03/15] chore(bump): v0.5.0 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe7691f6..6856445f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1477,7 +1477,7 @@ dependencies = [ [[package]] name = "rathole" -version = "0.4.9" +version = "0.5.0" dependencies = [ "anyhow", "async-http-proxy", diff --git a/Cargo.toml b/Cargo.toml index 1311f889..1c8ca739 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rathole" -version = "0.4.9" +version = "0.5.0" edition = "2021" authors = ["Yujia Qiao "] description = "A reverse proxy for NAT traversal" From 84c04ab9df3a68dfcf7932bf3550c3e436d10204 Mon Sep 17 00:00:00 2001 From: Yujia Qiao Date: Fri, 3 Nov 2023 19:37:43 +0800 Subject: [PATCH 04/15] fix: clippy (#302) --- src/protocol.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/protocol.rs b/src/protocol.rs index 577c7323..955346ef 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -112,8 +112,7 @@ impl UdpTraffic { } pub async fn read(reader: &mut T, hdr_len: u8) -> Result { - let mut buf = Vec::new(); - buf.resize(hdr_len as usize, 0); + let mut buf = vec![0; hdr_len as usize]; reader .read_exact(&mut buf) .await From e08b2a9e92563c2366110c6b70b655897d5800cc Mon Sep 17 00:00:00 2001 From: blueskea Date: Sun, 5 Nov 2023 13:38:39 +0800 Subject: [PATCH 05/15] docs: build with openssl (#303) --- docs/build-guide.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/build-guide.md b/docs/build-guide.md index d9750688..f1683952 100644 --- a/docs/build-guide.md +++ b/docs/build-guide.md @@ -6,6 +6,7 @@ To use default build settings, run: ``` cargo build --release ``` +You may need to pre-install [openssl](https://docs.rs/openssl/latest/openssl/index.html) dependencies in Unix-like systems. ## Customize the build `rathole` comes with lots of *crate features* that determine whether a certain feature will be compiled or not. Supported features can be checked out in `[features]` of [Cargo.toml](../Cargo.toml). From 65b27f076cfb9789873223b803c6c394f91fd47d Mon Sep 17 00:00:00 2001 From: zhfish Date: Sun, 5 Nov 2023 13:39:16 +0800 Subject: [PATCH 06/15] chore: vendor openssl for musl (#301) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update Cargo.toml add openssl's features for musl * Update release.yml * Update Cargo.toml musl * 更新 Cargo.toml --------- Co-authored-by: Yujia Qiao --- .github/workflows/release.yml | 4 ++++ Cargo.toml | 3 +++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fa71f278..c42762f3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,6 +21,10 @@ jobs: target: x86_64-unknown-linux-gnu exe: rathole cross: false + - os: ubuntu-latest + target: x86_64-unknown-linux-musl + exe: rathole + cross: false - os: ubuntu-latest target: aarch64-unknown-linux-musl exe: rathole diff --git a/Cargo.toml b/Cargo.toml index 1c8ca739..bcd2295e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,6 +85,9 @@ tokio-util = { version="0.7.9", optional = true, features = ["io"] } futures-core = { version="0.3.28", optional = true } futures-sink = { version="0.3.28", optional = true } +[target.'cfg(target_env = "musl")'.dependencies] +openssl = { version = "0.10", features = ["vendored"] } + [build-dependencies] vergen = { version = "7.4.2", default-features = false, features = ["build", "git", "cargo"] } anyhow = "1.0" From 62114cde4cf011b41eb4cbd36c8af1186cdeea28 Mon Sep 17 00:00:00 2001 From: Thomas Fournier Date: Tue, 13 Feb 2024 10:08:06 -0500 Subject: [PATCH 07/15] fix: typo (#323) --- docs/internals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/internals.md b/docs/internals.md index b0ede5a3..0f630836 100644 --- a/docs/internals.md +++ b/docs/internals.md @@ -30,5 +30,5 @@ When `rathole` starts in the client mode, it creates connections to `server.comm When a control channel starts, the server challenge the client by a nonce, the client is required to authenticate as the service it wants to represent. Then the forwarding of that service is set up. -When the server accepts a connection on a service's `bind_port`, it sends a control command to the client via the corresponding contorl channel. Then the client connects to the server to create a data channel. In this way, a forwarding is set up. The server also creates a few data channels in advance to improve the latency. +When the server accepts a connection on a service's `bind_port`, it sends a control command to the client via the corresponding control channel. Then the client connects to the server to create a data channel. In this way, a forwarding is set up. The server also creates a few data channels in advance to improve the latency. From 915bf4d21dcc312c4f3ec3a348eed12773e6c061 Mon Sep 17 00:00:00 2001 From: sunmy2019 <59365878+sunmy2019@users.noreply.github.com> Date: Wed, 14 Feb 2024 11:29:49 +0800 Subject: [PATCH 08/15] chore: update dependencies in Cargo.lock (#329) --- Cargo.lock | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6856445f..c9f17736 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -458,9 +458,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.1" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" dependencies = [ "cfg-if", "cpufeatures", @@ -1245,6 +1245,15 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-src" +version = "300.2.3+3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843" +dependencies = [ + "cc", +] + [[package]] name = "openssl-sys" version = "0.9.93" @@ -1253,6 +1262,7 @@ checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" dependencies = [ "cc", "libc", + "openssl-src", "pkg-config", "vcpkg", ] @@ -1496,6 +1506,7 @@ dependencies = [ "hex", "lazy_static", "notify", + "openssl", "rand", "serde", "sha2", From 63221028c9aa3a985dd375039002b28923cd1e42 Mon Sep 17 00:00:00 2001 From: Ryan Dearing Date: Tue, 13 Feb 2024 20:30:52 -0700 Subject: [PATCH 09/15] fix: flush DataChannelCmd::StartForward* commands (#316) Without flushing this may sit in a kernel buffer and we won't know if the channel is still alive. This is particularly problematic for the TCP connection pool. --- src/helper.rs | 14 +++++++++++++- src/server.rs | 13 ++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/helper.rs b/src/helper.rs index b795932f..7e1e5d32 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -1,8 +1,9 @@ -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, Context, Result}; use async_http_proxy::{http_connect_tokio, http_connect_tokio_with_basic_auth}; use backoff::{backoff::Backoff, Notify}; use socket2::{SockRef, TcpKeepalive}; use std::{future::Future, net::SocketAddr, time::Duration}; +use tokio::io::{AsyncWrite, AsyncWriteExt}; use tokio::{ net::{lookup_host, TcpStream, ToSocketAddrs, UdpSocket}, sync::broadcast, @@ -144,3 +145,14 @@ where } } } + +pub async fn write_and_flush(conn: &mut T, data: &[u8]) -> Result<()> +where + T: AsyncWrite + Unpin, +{ + conn.write_all(data) + .await + .with_context(|| "Failed to write data")?; + conn.flush().await.with_context(|| "Failed to flush data")?; + Ok(()) +} diff --git a/src/server.rs b/src/server.rs index a36e3c21..83ae976a 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,7 +1,7 @@ use crate::config::{Config, ServerConfig, ServerServiceConfig, ServiceType, TransportType}; use crate::config_watcher::{ConfigChange, ServerServiceChange}; use crate::constants::{listen_backoff, UDP_BUFFER_SIZE}; -use crate::helper::retry_notify_with_deadline; +use crate::helper::{retry_notify_with_deadline, write_and_flush}; use crate::multi_map::MultiMap; use crate::protocol::Hello::{ControlChannelHello, DataChannelHello}; use crate::protocol::{ @@ -498,14 +498,9 @@ struct ControlChannel { impl ControlChannel { async fn write_and_flush(&mut self, data: &[u8]) -> Result<()> { - self.conn - .write_all(data) + write_and_flush(&mut self.conn, data) .await .with_context(|| "Failed to write control cmds")?; - self.conn - .flush() - .await - .with_context(|| "Failed to flush control cmds")?; Ok(()) } // Run a control channel @@ -640,7 +635,7 @@ async fn run_tcp_connection_pool( 'pool: while let Some(mut visitor) = visitor_rx.recv().await { loop { if let Some(mut ch) = data_ch_rx.recv().await { - if ch.write_all(&cmd).await.is_ok() { + if write_and_flush(&mut ch, &cmd).await.is_ok() { tokio::spawn(async move { let _ = copy_bidirectional(&mut ch, &mut visitor).await; }); @@ -690,7 +685,7 @@ async fn run_udp_connection_pool( .recv() .await .ok_or_else(|| anyhow!("No available data channels"))?; - conn.write_all(&cmd).await?; + write_and_flush(&mut conn, &cmd).await?; let mut buf = [0u8; UDP_BUFFER_SIZE]; loop { From e4766e7d9016f3e09523565ba3ab78217b4733b7 Mon Sep 17 00:00:00 2001 From: Vincent Young Date: Sat, 17 Feb 2024 21:25:45 -0500 Subject: [PATCH 10/15] ci: support apple aarch64 (#294) --- .github/workflows/release.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c42762f3..48a60cb3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -70,6 +70,11 @@ jobs: target: x86_64-apple-darwin exe: rathole cross: false + + - os: macos-latest + target: aarch64-apple-darwin + exe: rathole + cross: false - os: windows-latest target: x86_64-pc-windows-msvc @@ -96,7 +101,7 @@ jobs: if: matrix.cross == false run: rustup target add ${{ matrix.target }} - name: Run tests - if: matrix.cross == false + if: matrix.cross == false && matrix.target != 'aarch64-apple-darwin' run: cargo test --release --target ${{ matrix.target }} --verbose - name: Build release if: matrix.cross == false From ee7561c38d060195c82a5d01ac4c8307a6ec3c4e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 18 Feb 2024 10:50:47 +0800 Subject: [PATCH 11/15] chore(deps): bump snow from 0.9.3 to 0.9.6 (#333) Bumps [snow](https://github.com/mcginty/snow) from 0.9.3 to 0.9.6. - [Release notes](https://github.com/mcginty/snow/releases) - [Commits](https://github.com/mcginty/snow/compare/v0.9.3...v0.9.6) --- updated-dependencies: - dependency-name: snow dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 67 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c9f17736..515fa43b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,30 +19,30 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aead" -version = "0.4.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ + "crypto-common", "generic-array", ] [[package]] name = "aes" -version = "0.7.5" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", "cpufeatures", - "opaque-debug", ] [[package]] name = "aes-gcm" -version = "0.9.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc3be92e19a7ef47457b8e6f90707e12b6ac5d20c6f3866584fa3be0787d839f" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", "aes", @@ -276,21 +276,20 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chacha20" -version = "0.8.2" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", "cipher", "cpufeatures", - "zeroize", ] [[package]] name = "chacha20poly1305" -version = "0.9.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ "aead", "chacha20", @@ -301,11 +300,13 @@ dependencies = [ [[package]] name = "cipher" -version = "0.3.0" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "generic-array", + "crypto-common", + "inout", + "zeroize", ] [[package]] @@ -444,14 +445,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core", "typenum", ] [[package]] name = "ctr" -version = "0.7.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a232f92a03f37dd7d7dd2adc67166c77e9cd88de5b019b9a9eecfaeaf7bfd481" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ "cipher", ] @@ -744,9 +746,9 @@ dependencies = [ [[package]] name = "ghash" -version = "0.4.4" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" dependencies = [ "opaque-debug", "polyval", @@ -952,6 +954,15 @@ dependencies = [ "libc", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "instant" version = "0.1.12" @@ -1354,9 +1365,9 @@ checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" [[package]] name = "poly1305" -version = "0.7.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ "cpufeatures", "opaque-debug", @@ -1365,9 +1376,9 @@ dependencies = [ [[package]] name = "polyval" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" dependencies = [ "cfg-if", "cpufeatures", @@ -1757,9 +1768,9 @@ checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "snow" -version = "0.9.3" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9d1425eb528a21de2755c75af4c9b5d57f50a0d4c3b7f1828a4cd03f8ba155" +checksum = "850948bee068e713b8ab860fe1adc4d109676ab4c3b621fd8147f06b261f2f85" dependencies = [ "aes-gcm", "blake2", @@ -2220,11 +2231,11 @@ dependencies = [ [[package]] name = "universal-hash" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ - "generic-array", + "crypto-common", "subtle", ] From 7251759bdaf4b7d170575bdd6d2062bbd9f338bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 18 Feb 2024 02:55:27 +0000 Subject: [PATCH 12/15] chore(deps): bump h2 from 0.3.21 to 0.3.24 (#334) Bumps [h2](https://github.com/hyperium/h2) from 0.3.21 to 0.3.24. - [Release notes](https://github.com/hyperium/h2/releases) - [Changelog](https://github.com/hyperium/h2/blob/v0.3.24/CHANGELOG.md) - [Commits](https://github.com/hyperium/h2/compare/v0.3.21...v0.3.24) --- updated-dependencies: - dependency-name: h2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 515fa43b..ce79bad8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -319,7 +319,7 @@ dependencies = [ "bitflags 1.3.2", "clap_derive", "clap_lex", - "indexmap", + "indexmap 1.9.3", "once_cell", "strsim", "termcolor", @@ -534,6 +534,12 @@ dependencies = [ "syn 2.0.37", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.3" @@ -775,9 +781,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" dependencies = [ "bytes", "fnv", @@ -785,7 +791,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 2.2.3", "slab", "tokio", "tokio-util", @@ -798,6 +804,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + [[package]] name = "hdrhistogram" version = "7.5.2" @@ -931,7 +943,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", ] [[package]] @@ -2090,7 +2112,7 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", - "indexmap", + "indexmap 1.9.3", "pin-project", "pin-project-lite", "rand", From 4ac53a5a39e74d5eb12bee22d0fd4783acaae670 Mon Sep 17 00:00:00 2001 From: sunmy2019 <59365878+sunmy2019@users.noreply.github.com> Date: Sun, 18 Feb 2024 17:17:17 +0800 Subject: [PATCH 13/15] feat: optional rustls support (#330) * initial implementation of rustls support * Refactor create_self_signed_cert.sh script * resolve lint errors * Fix handling of Option in tls.rs * Update cargo-hack check command and feature dependencies * fix missing point * Add conditional check to skip test if client or server is not enabled * clean up things * fix for windows CI * try fixing Windows CI * Update src/main.rs * Update src/transport/websocket.rs * add missing messages * split the tls mod Co-authored-by: Ning Sun --- .github/workflows/rust.yml | 8 +- Cargo.lock | 164 ++++++++++++++++++++++++ Cargo.toml | 71 ++++++++-- examples/tls/create_self_signed_cert.sh | 5 +- examples/tls/identity.pfx | Bin 3587 -> 3453 bytes examples/tls/rootCA.crt | 32 ++--- src/client.rs | 19 +-- src/helper.rs | 8 ++ src/lib.rs | 9 +- src/server.rs | 16 +-- src/transport/mod.rs | 23 +++- src/transport/{tls.rs => native_tls.rs} | 13 +- src/transport/rustls.rs | 156 ++++++++++++++++++++++ src/transport/websocket.rs | 14 +- tests/integration_test.rs | 23 ++-- 15 files changed, 482 insertions(+), 79 deletions(-) rename src/transport/{tls.rs => native_tls.rs} (91%) create mode 100644 src/transport/rustls.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 45c693ab..916c0edf 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -32,7 +32,9 @@ jobs: - name: Setup cargo-hack run: cargo install cargo-hack - name: Check all features - run: cargo hack check --feature-powerset --no-dev-deps + run: > + cargo hack check --feature-powerset --no-dev-deps + --mutually-exclusive-features default,native-tls,websocket-native-tls,rustls,websocket-rustls build: name: Build for ${{ matrix.target }} @@ -62,8 +64,10 @@ jobs: - uses: Swatinem/rust-cache@v1 - name: Build run: cargo build - - name: Run tests + - name: Run tests with native-tls run: cargo test --verbose + - name: Run tests with rustls + run: cargo test --verbose --no-default-features --features server,client,rustls,noise,websocket-rustls,hot-reload - uses: actions/upload-artifact@v2 with: name: rathole-${{ matrix.target }} diff --git a/Cargo.lock b/Cargo.lock index ce79bad8..77a6517c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -243,6 +243,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -258,6 +267,15 @@ dependencies = [ "serde", ] +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + [[package]] name = "cc" version = "1.0.83" @@ -497,6 +515,15 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +[[package]] +name = "des" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" +dependencies = [ + "cipher", +] + [[package]] name = "digest" version = "0.10.7" @@ -850,6 +877,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "http" version = "0.2.9" @@ -982,6 +1018,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ + "block-padding", "generic-array", ] @@ -1312,6 +1349,23 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "p12" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4873306de53fe82e7e484df31e1e947d61514b6ea2ed6cd7b45d63006fd9224" +dependencies = [ + "cbc", + "cipher", + "des", + "getrandom", + "hmac", + "lazy_static", + "rc2", + "sha1", + "yasna", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -1540,13 +1594,17 @@ dependencies = [ "lazy_static", "notify", "openssl", + "p12", "rand", + "rustls-native-certs", + "rustls-pemfile", "serde", "sha2", "snowstorm", "socket2 0.4.9", "tokio", "tokio-native-tls", + "tokio-rustls", "tokio-tungstenite", "tokio-util", "toml", @@ -1556,6 +1614,15 @@ dependencies = [ "vergen", ] +[[package]] +name = "rc2" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62c64daa8e9438b84aaae55010a93f396f8e60e3911590fcba770d04643fc1dd" +dependencies = [ + "cipher", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -1609,6 +1676,20 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +[[package]] +name = "ring" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.48.0", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1637,6 +1718,60 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rustls" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e4980fa29e4c4b212ffb3db068a564cbf560e51d3944b7c88bd8bf5bec64f4" +dependencies = [ + "base64 0.21.4", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048a63e5b3ac996d78d402940b5fa47973d2d080c6c6fffa1d0f19c4445310b7" + +[[package]] +name = "rustls-webpki" +version = "0.102.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.14" @@ -1838,6 +1973,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "strsim" version = "0.10.0" @@ -2030,6 +2171,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.14" @@ -2261,6 +2413,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.4.1" @@ -2501,6 +2659,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" + [[package]] name = "zeroize" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index bcd2295e..44fcf97a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,18 +11,48 @@ build = "build.rs" include = ["src/**/*", "LICENSE", "README.md", "build.rs"] [features] -default = ["server", "client", "tls", "noise", "websocket", "hot-reload"] +default = [ + "server", + "client", + "native-tls", + "noise", + "websocket-native-tls", + "hot-reload", +] # Run as a server server = [] # Run as a client client = [] + # TLS support -tls = ["tokio-native-tls"] +native-tls = ["tokio-native-tls"] +rustls = [ + "tokio-rustls", + "rustls-pemfile", + "rustls-native-certs", + "p12", +] + # Noise support noise = ["snowstorm", "base64"] + # Websocket support -websocket = ["tokio-tungstenite", "tokio-util", "futures-core", "futures-sink", "tls"] +websocket-native-tls = [ + "tokio-tungstenite", + "tokio-util", + "futures-core", + "futures-sink", + "native-tls", +] +websocket-rustls = [ + "tokio-tungstenite", + "tokio-util", + "futures-core", + "futures-sink", + "rustls", +] + # Configuration hot-reload support hot-reload = ["notify"] @@ -67,27 +97,42 @@ hex = "0.4" rand = "0.8" backoff = { version = "0.4", features = ["tokio"] } tracing = "0.1" -tracing-subscriber = { version="0.3", features=["env-filter"] } +tracing-subscriber = { version = "0.3", features = ["env-filter"] } socket2 = { version = "0.4", features = ["all"] } fdlimit = "0.2" -tokio-native-tls = { version = "0.3", optional = true } async-trait = "0.1" -snowstorm = { version = "0.4", optional = true, features = ["stream"], default-features = false } +snowstorm = { version = "0.4", optional = true, features = [ + "stream", +], default-features = false } base64 = { version = "0.13", optional = true } notify = { version = "5.0.0-pre.13", optional = true } -console-subscriber = { version = "0.1", optional = true, features = ["parking_lot"] } +console-subscriber = { version = "0.1", optional = true, features = [ + "parking_lot", +] } atty = "0.2" -async-http-proxy = { version = "1.2", features = ["runtime-tokio", "basic-auth"] } +async-http-proxy = { version = "1.2", features = [ + "runtime-tokio", + "basic-auth", +] } async-socks5 = "0.5" url = { version = "2.2", features = ["serde"] } -tokio-tungstenite = { version="0.20.1", optional = true} -tokio-util = { version="0.7.9", optional = true, features = ["io"] } -futures-core = { version="0.3.28", optional = true } -futures-sink = { version="0.3.28", optional = true } +tokio-tungstenite = { version = "0.20.1", optional = true } +tokio-util = { version = "0.7.9", optional = true, features = ["io"] } +futures-core = { version = "0.3.28", optional = true } +futures-sink = { version = "0.3.28", optional = true } +tokio-native-tls = { version = "0.3", optional = true } +tokio-rustls = { version = "0.25", optional = true } +rustls-native-certs = { version = "0.7", optional = true } +rustls-pemfile = { version = "2.0", optional = true } +p12 = { version = "0.6.3", optional = true } [target.'cfg(target_env = "musl")'.dependencies] openssl = { version = "0.10", features = ["vendored"] } [build-dependencies] -vergen = { version = "7.4.2", default-features = false, features = ["build", "git", "cargo"] } +vergen = { version = "7.4.2", default-features = false, features = [ + "build", + "git", + "cargo", +] } anyhow = "1.0" diff --git a/examples/tls/create_self_signed_cert.sh b/examples/tls/create_self_signed_cert.sh index e110a1f4..6ab9db37 100644 --- a/examples/tls/create_self_signed_cert.sh +++ b/examples/tls/create_self_signed_cert.sh @@ -56,7 +56,8 @@ openssl x509 -req \ -sha256 -extfile cert.conf # create pkcs12 -openssl pkcs12 -export -out identity.pfx -inkey server.key -in server.crt -certfile rootCA.crt -passout pass:1234 +openssl pkcs12 -export -out identity.pfx -inkey server.key -in server.crt -certfile rootCA.crt \ + -passout pass:1234 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES # clean up -rm server.csr csr.conf cert.conf \ No newline at end of file +rm server.csr csr.conf cert.conf diff --git a/examples/tls/identity.pfx b/examples/tls/identity.pfx index dfc5160debcac20c939f71971a08c9a87a789c08..2041157ad25488f41cfdbbb0f3f62bc8407716f9 100644 GIT binary patch literal 3453 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 literal 3587 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 { - #[cfg(feature = "tls")] + #[cfg(any(feature = "native-tls", feature = "rustls"))] { let mut client = Client::::from(config).await?; client.run(shutdown_rx, update_rx).await } - #[cfg(not(feature = "tls"))] - crate::helper::feature_not_compile("tls") + #[cfg(not(any(feature = "native-tls", feature = "rustls")))] + crate::helper::feature_neither_compile("native-tls", "rustls") } TransportType::Noise => { #[cfg(feature = "noise")] @@ -65,13 +66,13 @@ pub async fn run_client( crate::helper::feature_not_compile("noise") } TransportType::Websocket => { - #[cfg(feature = "websocket")] + #[cfg(any(feature = "websocket-native-tls", feature = "websocket-rustls"))] { let mut client = Client::::from(config).await?; client.run(shutdown_rx, update_rx).await } - #[cfg(not(feature = "websocket"))] - crate::helper::feature_not_compile("websocket") + #[cfg(not(any(feature = "websocket-native-tls", feature = "websocket-rustls")))] + crate::helper::feature_neither_compile("websocket-native-tls", "websocket-rustls") } } } diff --git a/src/helper.rs b/src/helper.rs index 7e1e5d32..a292969f 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -43,6 +43,14 @@ pub fn feature_not_compile(feature: &str) -> ! { ) } +#[allow(dead_code)] +pub fn feature_neither_compile(feature1: &str, feature2: &str) -> ! { + panic!( + "Neither of the feature '{}' or '{}' is compiled in this binary. Please re-compile rathole", + feature1, feature2 + ) +} + pub async fn to_socket_addr(addr: A) -> Result { lookup_host(addr) .await? diff --git a/src/lib.rs b/src/lib.rs index 7fb2fa6d..65beb7f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,7 +83,7 @@ pub async fn run(args: Cli, shutdown_rx: broadcast::Receiver) -> Result<() if let Some((i, _)) = last_instance { info!("General configuration change detected. Restarting..."); shutdown_tx.send(true)?; - i.await?; + i.await??; } debug!("{:?}", config); @@ -119,8 +119,8 @@ async fn run_instance( args: Cli, shutdown_rx: broadcast::Receiver, service_update: mpsc::Receiver, -) { - let ret: Result<()> = match determine_run_mode(&config, &args) { +) -> Result<()> { + match determine_run_mode(&config, &args) { RunMode::Undetermine => panic!("Cannot determine running as a server or a client"), RunMode::Client => { #[cfg(not(feature = "client"))] @@ -134,8 +134,7 @@ async fn run_instance( #[cfg(feature = "server")] run_server(config, shutdown_rx, service_update).await } - }; - ret.unwrap(); + } } #[derive(PartialEq, Eq, Debug)] diff --git a/src/server.rs b/src/server.rs index 83ae976a..a4c49482 100644 --- a/src/server.rs +++ b/src/server.rs @@ -25,9 +25,9 @@ use tracing::{debug, error, info, info_span, instrument, warn, Instrument, Span} #[cfg(feature = "noise")] use crate::transport::NoiseTransport; -#[cfg(feature = "tls")] +#[cfg(any(feature = "native-tls", feature = "rustls"))] use crate::transport::TlsTransport; -#[cfg(feature = "websocket")] +#[cfg(any(feature = "websocket-native-tls", feature = "websocket-rustls"))] use crate::transport::WebsocketTransport; type ServiceDigest = protocol::Digest; // SHA256 of a service name @@ -57,13 +57,13 @@ pub async fn run_server( server.run(shutdown_rx, update_rx).await?; } TransportType::Tls => { - #[cfg(feature = "tls")] + #[cfg(any(feature = "native-tls", feature = "rustls"))] { let mut server = Server::::from(config).await?; server.run(shutdown_rx, update_rx).await?; } - #[cfg(not(feature = "tls"))] - crate::helper::feature_not_compile("tls") + #[cfg(not(any(feature = "native-tls", feature = "rustls")))] + crate::helper::feature_neither_compile("native-tls", "rustls") } TransportType::Noise => { #[cfg(feature = "noise")] @@ -75,13 +75,13 @@ pub async fn run_server( crate::helper::feature_not_compile("noise") } TransportType::Websocket => { - #[cfg(feature = "websocket")] + #[cfg(any(feature = "websocket-native-tls", feature = "websocket-rustls"))] { let mut server = Server::::from(config).await?; server.run(shutdown_rx, update_rx).await?; } - #[cfg(not(feature = "websocket"))] - crate::helper::feature_not_compile("websocket") + #[cfg(not(any(feature = "websocket-native-tls", feature = "websocket-rustls")))] + crate::helper::feature_neither_compile("websocket-native-tls", "websocket-rustls") } } diff --git a/src/transport/mod.rs b/src/transport/mod.rs index 38682a65..26d357fb 100644 --- a/src/transport/mod.rs +++ b/src/transport/mod.rs @@ -69,19 +69,30 @@ pub trait Transport: Debug + Send + Sync { mod tcp; pub use tcp::TcpTransport; -#[cfg(feature = "tls")] -mod tls; -#[cfg(feature = "tls")] -pub use tls::TlsTransport; + +#[cfg(all(feature = "native-tls", feature = "rustls"))] +compile_error!("Only one of `native-tls` and `rustls` can be enabled"); + +#[cfg(feature = "native-tls")] +mod native_tls; +#[cfg(feature = "native-tls")] +use native_tls as tls; +#[cfg(feature = "rustls")] +mod rustls; +#[cfg(feature = "rustls")] +use rustls as tls; + +#[cfg(any(feature = "native-tls", feature = "rustls"))] +pub(crate) use tls::TlsTransport; #[cfg(feature = "noise")] mod noise; #[cfg(feature = "noise")] pub use noise::NoiseTransport; -#[cfg(feature = "websocket")] +#[cfg(any(feature = "websocket-native-tls", feature = "websocket-rustls"))] mod websocket; -#[cfg(feature = "websocket")] +#[cfg(any(feature = "websocket-native-tls", feature = "websocket-rustls"))] pub use websocket::WebsocketTransport; #[derive(Debug, Clone, Copy)] diff --git a/src/transport/tls.rs b/src/transport/native_tls.rs similarity index 91% rename from src/transport/tls.rs rename to src/transport/native_tls.rs index 918af04b..40afd50b 100644 --- a/src/transport/tls.rs +++ b/src/transport/native_tls.rs @@ -1,14 +1,14 @@ -use std::net::SocketAddr; - -use super::{AddrMaybeCached, SocketOpts, TcpTransport, Transport}; use crate::config::{TlsConfig, TransportConfig}; use crate::helper::host_port_pair; +use crate::transport::{AddrMaybeCached, SocketOpts, TcpTransport, Transport}; use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; use std::fs; +use std::net::SocketAddr; use tokio::net::{TcpListener, TcpStream, ToSocketAddrs}; use tokio_native_tls::native_tls::{self, Certificate, Identity}; -use tokio_native_tls::{TlsAcceptor, TlsConnector, TlsStream}; +pub(crate) use tokio_native_tls::TlsStream; +use tokio_native_tls::{TlsAcceptor, TlsConnector}; #[derive(Debug)] pub struct TlsTransport { @@ -109,3 +109,8 @@ impl Transport for TlsTransport { .await?) } } + +#[cfg(feature = "websocket-native-tls")] +pub(crate) fn get_tcpstream(s: &TlsStream) -> &TcpStream { + s.get_ref().get_ref().get_ref() +} diff --git a/src/transport/rustls.rs b/src/transport/rustls.rs new file mode 100644 index 00000000..3ca4704e --- /dev/null +++ b/src/transport/rustls.rs @@ -0,0 +1,156 @@ +use crate::config::{TlsConfig, TransportConfig}; +use crate::helper::host_port_pair; +use crate::transport::{AddrMaybeCached, SocketOpts, TcpTransport, Transport}; +use std::fmt::Debug; +use std::fs; +use std::net::SocketAddr; +use std::sync::Arc; +use tokio::net::{TcpListener, TcpStream, ToSocketAddrs}; +use tokio_rustls::rustls::pki_types::{CertificateDer, PrivatePkcs8KeyDer, ServerName}; + +use anyhow::{anyhow, Context, Result}; +use async_trait::async_trait; +use p12::PFX; +use tokio_rustls::rustls::{ClientConfig, RootCertStore, ServerConfig}; +pub(crate) use tokio_rustls::TlsStream; +use tokio_rustls::{TlsAcceptor, TlsConnector}; + +pub struct TlsTransport { + tcp: TcpTransport, + config: TlsConfig, + connector: Option, + tls_acceptor: Option, +} + +// workaround for TlsConnector and TlsAcceptor not implementing Debug +impl Debug for TlsTransport { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TlsTransport") + .field("tcp", &self.tcp) + .field("config", &self.config) + .finish() + } +} + +fn load_server_config(config: &TlsConfig) -> Result> { + if let Some(pkcs12_path) = config.pkcs12.as_ref() { + let buf = fs::read(pkcs12_path)?; + let pfx = PFX::parse(buf.as_slice())?; + let pass = config.pkcs12_password.as_ref().unwrap(); + + let certs = pfx.cert_bags(pass)?; + let keys = pfx.key_bags(pass)?; + + let chain: Vec = certs.into_iter().map(CertificateDer::from).collect(); + let key = PrivatePkcs8KeyDer::from(keys.into_iter().next().unwrap()); + + Ok(Some( + ServerConfig::builder() + .with_no_client_auth() + .with_single_cert(chain, key.into())?, + )) + } else { + Ok(None) + } +} + +fn load_client_config(config: &TlsConfig) -> Result> { + let cert = if let Some(path) = config.trusted_root.as_ref() { + rustls_pemfile::certs(&mut std::io::BufReader::new(fs::File::open(path).unwrap())) + .map(|cert| cert.unwrap()) + .next() + .with_context(|| "Failed to read certificate")? + } else { + // read from native + match rustls_native_certs::load_native_certs() { + Ok(certs) => certs.into_iter().next().unwrap(), + Err(e) => { + eprintln!("Failed to load native certs: {}", e); + return Ok(None); + } + } + }; + + let mut root_certs = RootCertStore::empty(); + root_certs.add(cert).unwrap(); + + Ok(Some( + ClientConfig::builder() + .with_root_certificates(root_certs) + .with_no_client_auth(), + )) +} + +#[async_trait] +impl Transport for TlsTransport { + type Acceptor = TcpListener; + type RawStream = TcpStream; + type Stream = TlsStream; + + fn new(config: &TransportConfig) -> Result { + let tcp = TcpTransport::new(config)?; + let config = config + .tls + .as_ref() + .ok_or_else(|| anyhow!("Missing tls config"))?; + + let connector = load_client_config(config) + .unwrap() + .map(|c| Arc::new(c).into()); + let tls_acceptor = load_server_config(config) + .unwrap() + .map(|c| Arc::new(c).into()); + + Ok(TlsTransport { + tcp, + config: config.clone(), + connector, + tls_acceptor, + }) + } + + fn hint(conn: &Self::Stream, opt: SocketOpts) { + opt.apply(conn.get_ref().0); + } + + async fn bind(&self, addr: A) -> Result { + let l = TcpListener::bind(addr) + .await + .with_context(|| "Failed to create tcp listener")?; + Ok(l) + } + + async fn accept(&self, a: &Self::Acceptor) -> Result<(Self::RawStream, SocketAddr)> { + self.tcp + .accept(a) + .await + .with_context(|| "Failed to accept TCP connection") + } + + async fn handshake(&self, conn: Self::RawStream) -> Result { + let conn = self.tls_acceptor.as_ref().unwrap().accept(conn).await?; + Ok(tokio_rustls::TlsStream::Server(conn)) + } + + async fn connect(&self, addr: &AddrMaybeCached) -> Result { + let conn = self.tcp.connect(addr).await?; + + let connector = self.connector.as_ref().unwrap(); + + let host_name = self + .config + .hostname + .as_deref() + .unwrap_or(host_port_pair(&addr.addr)?.0); + + Ok(tokio_rustls::TlsStream::Client( + connector + .connect(ServerName::try_from(host_name)?.to_owned(), conn) + .await?, + )) + } +} + +pub(crate) fn get_tcpstream(s: &TlsStream) -> &TcpStream { + &s.get_ref().0 +} diff --git a/src/transport/websocket.rs b/src/transport/websocket.rs index ec6177d1..228eff70 100644 --- a/src/transport/websocket.rs +++ b/src/transport/websocket.rs @@ -13,10 +13,14 @@ use futures_core::stream::Stream; use futures_sink::Sink; use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite, ReadBuf}; use tokio::net::{TcpListener, TcpStream, ToSocketAddrs}; -use tokio_native_tls::TlsStream; -use tokio_tungstenite::tungstenite::protocol::WebSocketConfig; -use tokio_tungstenite::{accept_async_with_config, client_async_with_config}; -use tokio_tungstenite::{tungstenite::protocol::Message, WebSocketStream}; + +#[cfg(any(feature = "native-tls", feature = "rustls"))] +use super::tls::get_tcpstream; +#[cfg(any(feature = "native-tls", feature = "rustls"))] +use super::tls::TlsStream; + +use tokio_tungstenite::tungstenite::protocol::{Message, WebSocketConfig}; +use tokio_tungstenite::{accept_async_with_config, client_async_with_config, WebSocketStream}; use tokio_util::io::StreamReader; use url::Url; @@ -30,7 +34,7 @@ impl TransportStream { fn get_tcpstream(&self) -> &TcpStream { match self { TransportStream::Insecure(s) => s, - TransportStream::Secure(s) => s.get_ref().get_ref().get_ref(), + TransportStream::Secure(s) => get_tcpstream(s), } } } diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 3b8dec90..7b5d408d 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{Ok, Result}; use common::{run_rathole_client, PING, PONG}; use rand::Rng; use std::time::Duration; @@ -57,17 +57,17 @@ async fn tcp() -> Result<()> { test("tests/for_tcp/tcp_transport.toml", Type::Tcp).await?; // FIXME: Self-signed certificate on Mac requires mannual interference. Disable CI for now #[cfg(not(target_os = "macos"))] - #[cfg(feature="tls")] + #[cfg(any(feature = "native-tls", feature = "rustls"))] test("tests/for_tcp/tls_transport.toml", Type::Tcp).await?; - #[cfg(feature="noise")] + #[cfg(feature = "noise")] test("tests/for_tcp/noise_transport.toml", Type::Tcp).await?; - #[cfg(feature="websocket")] + #[cfg(any(feature = "websocket-native-tls", feature = "websocket-rustls"))] test("tests/for_tcp/websocket_transport.toml", Type::Tcp).await?; #[cfg(not(target_os = "macos"))] - #[cfg(feature="websocket")] + #[cfg(any(feature = "websocket-native-tls", feature = "websocket-rustls"))] test("tests/for_tcp/websocket_tls_transport.toml", Type::Tcp).await?; Ok(()) @@ -94,17 +94,17 @@ async fn udp() -> Result<()> { test("tests/for_udp/tcp_transport.toml", Type::Udp).await?; // See above #[cfg(not(target_os = "macos"))] - #[cfg(feature="tls")] + #[cfg(any(feature = "native-tls", feature = "rustls"))] test("tests/for_udp/tls_transport.toml", Type::Udp).await?; - #[cfg(feature="noise")] + #[cfg(feature = "noise")] test("tests/for_udp/noise_transport.toml", Type::Udp).await?; - #[cfg(feature="websocket")] + #[cfg(any(feature = "websocket-native-tls", feature = "websocket-rustls"))] test("tests/for_udp/websocket_transport.toml", Type::Udp).await?; #[cfg(not(target_os = "macos"))] - #[cfg(feature="websocket")] + #[cfg(any(feature = "websocket-native-tls", feature = "websocket-rustls"))] test("tests/for_udp/websocket_tls_transport.toml", Type::Udp).await?; Ok(()) @@ -112,6 +112,11 @@ async fn udp() -> Result<()> { #[instrument] async fn test(config_path: &'static str, t: Type) -> Result<()> { + if cfg!(not(all(feature = "client", feature = "server"))) { + // Skip the test if the client or the server is not enabled + return Ok(()); + } + let (client_shutdown_tx, client_shutdown_rx) = broadcast::channel(1); let (server_shutdown_tx, server_shutdown_rx) = broadcast::channel(1); From 3ab540f19ddc83f1541ac342f25aa59b7db5481e Mon Sep 17 00:00:00 2001 From: Yujia Qiao Date: Sun, 18 Feb 2024 22:55:59 +0800 Subject: [PATCH 14/15] chore: update bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 41f537f9..abd35b26 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -28,3 +28,4 @@ If you encountered a panic, please re-run with `RUST_BACKTRACE=1` to provide the - OS: - `rathole --version` output: - CPU architecture: +- rustc version: From 0ee0157b4b9584580dde07a351982d87880f2404 Mon Sep 17 00:00:00 2001 From: sunmy2019 <59365878+sunmy2019@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:46:29 +0800 Subject: [PATCH 15/15] docs: update docs for rustls (#337) * Fix typos and update docs for rustls * update description * Remove placeholders in doc --- README.md | 2 +- docs/build-guide.md | 36 ++++++++++++++++++++++++++++++------ docs/transport.md | 33 ++++++++++++++++++++++++++++++--- 3 files changed, 61 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 18e46aff..25cf97f1 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ Create `server.toml` with the following content and accommodate it to your needs bind_addr = "0.0.0.0:2333" # `2333` specifies the port that rathole listens for clients [server.services.my_nas_ssh] -token = "use_a_secret_that_only_you_know" # Token that is used to authenticate the client for the service. Change to a arbitrary value. +token = "use_a_secret_that_only_you_know" # Token that is used to authenticate the client for the service. Change to an arbitrary value. bind_addr = "0.0.0.0:5202" # `5202` specifies the port that exposes `my_nas_ssh` to the Internet ``` diff --git a/docs/build-guide.md b/docs/build-guide.md index f1683952..2adf6c22 100644 --- a/docs/build-guide.md +++ b/docs/build-guide.md @@ -1,31 +1,54 @@ # Build Guide + This is for those who want to build `rathole` themselves, possibly because the need of latest features or the minimal binary size. ## Build + To use default build settings, run: -``` + +```sh cargo build --release ``` + You may need to pre-install [openssl](https://docs.rs/openssl/latest/openssl/index.html) dependencies in Unix-like systems. -## Customize the build +## Customize the Build + `rathole` comes with lots of *crate features* that determine whether a certain feature will be compiled or not. Supported features can be checked out in `[features]` of [Cargo.toml](../Cargo.toml). For example, to build `rathole` with the `client` and `noise` feature: -``` + +```sh cargo build --release --no-default-features --features client,noise ``` +## Rustls Support + +`rathole` provides optional `rustls` support. It's an almost drop-in replacement of `native-tls` support. (See [Transport](transport.md) for more information.) + +To enable this, disable the default features and enable `rustls` feature. And for websocket feature, enable `websocket-rustls` feature as well. + +You can also use command line option for this. For example, to replace all default features with `rustls`: + +```sh +cargo build --release --no-default-features --features server,client,rustls,noise,websocket-rustls,hot-reload +``` + +Feature `rustls` and `websocket-rustls` cannot be enabled with `native-tls` and `websocket-native-tls` at the same time, as they are mutually exclusive. Enabling both will result in a compile error. + +(Note that default features contains `native-tls` and `websocket-native-tls`.) + ## Minimalize the binary 1. Build with the `minimal` profile -The `release` build profile optimize for the program running time, not the binary size. +The `release` build profile optimize for the program running time, not the binary size. However, the `minimal` profile enables lots of optimization for the binary size to produce a much smaller binary. For example, to build `rathole` with `client` feature with the `minimal` profile: -``` + +```sh cargo build --profile minimal --no-default-features --features client ``` @@ -34,7 +57,8 @@ cargo build --profile minimal --no-default-features --features client The binary that step 1 produces can be even smaller, by using `strip` and `upx` to remove the symbols and compress the binary. Like: -``` + +```sh strip rathole upx --best --lzma rathole ``` diff --git a/docs/transport.md b/docs/transport.md index a66bc7d6..c59f1dbe 100644 --- a/docs/transport.md +++ b/docs/transport.md @@ -3,21 +3,27 @@ By default, `rathole` forwards traffic as it is. Different options can be enabled to secure the traffic. ## TLS + Checkout the [example](../examples/tls) + ### Client + Normally, a self-signed certificate is used. In this case, the client needs to trust the CA. `trusted_root` is the path to the root CA's certificate PEM file. `hostname` is the hostname that the client used to validate aginst the certificate that the server presents. Note that it does not have to be the same with the `remote_addr` in `[client]`. -``` + +```toml [client.transport.tls] trusted_root = "example/tls/rootCA.crt" hostname = "localhost" ``` ### Server + PKCS#12 archives are needed to run the server. It can be created using openssl like: -``` + +```sh openssl pkcs12 -export -out identity.pfx -inkey server.key -in server.crt -certfile ca_chain_certs.crt ``` @@ -29,8 +35,22 @@ Aruguments are: Creating self-signed certificate with one's own CA is a non-trival task. However, a script is provided under tls example folder for reference. +### Rustls Support + +`rathole` provides optional `rustls` support. [Build Guide](build-guide.md) demostrated this. + +One difference is that, the crate we use for loading PKCS#12 archives can only handle limited types of PBE algorithms. We only support PKCS#12 archives that they (crate `p12`) support. So we need to specify the legacy format (openssl 1.x format) when creating the PKCS#12 archive. + +In short, the command used with openssl 3 to create the PKCS#12 archive with `rustls` support is: + +```sh +openssl pkcs12 -export -out identity.pfx -inkey server.key -in server.crt -certfile ca_chain_certs.crt -legacy +``` + ## Noise Protocol + ### Quickstart for the Noise Protocl + In one word, the [Noise Protocol](http://noiseprotocol.org/noise.html) is a lightweigt, easy to configure and drop-in replacement of TLS. No need to create a self-sign certificate to secure the connection. `rathole` comes with a reasonable default configuration for noise protocol. You can a glimpse of the minimal [example](../examples/noise_nk) for how it will look like. @@ -38,12 +58,14 @@ In one word, the [Noise Protocol](http://noiseprotocol.org/noise.html) is a ligh The default noise protocol that `rathole` uses, which is `Noise_NK_25519_ChaChaPoly_BLAKE2s`, providing the authentication of the server, just like TLS with properly configured certificates. So MITM is no more a problem. To use it, a X25519 keypair is needed. + #### Generate a Keypair 1. Run `rathole --genkey`, which will generate a keypair using the default X25519 algorithm. It emits: -``` + +```sh $ rathole --genkey Private Key: cQ/vwIqNPJZmuM/OikglzBo/+jlYGrOt9i0k5h5vn1Q= @@ -51,11 +73,13 @@ cQ/vwIqNPJZmuM/OikglzBo/+jlYGrOt9i0k5h5vn1Q= Public Key: GQYTKSbWLBUSZiGfdWPSgek9yoOuaiwGD/GIX8Z1kkE= ``` + (WARNING: Don't use the keypair from the Internet, including this one) 2. The server should keep the private key to identify itself. And the client should keep the public key, which is used to verify whether the peer is the authentic server. So relevant snippets of configuration are: + ```toml # Client Side Configuration [client.transport] @@ -73,9 +97,11 @@ local_private_key = "cQ/vwIqNPJZmuM/OikglzBo/+jlYGrOt9i0k5h5vn1Q=" Then `rathole` will run under the protection of the Noise Protocol. ## Specifying the Pattern of Noise Protocol + The default configuration of Noise Protocol that comes with `rathole` satifies most use cases, which is described above. But there're other patterns that can be useful. ### No Authentication + This configuration provides encryption of the traffic but provides no authentication, which means it's vulnerable to MITM attack, but is resistent to the sniffing and replay attack. If MITM attack is not one of the concerns, this is more convenient to use. ```toml @@ -107,6 +133,7 @@ remote_public_key = "server-pub-key-here" ### Other Patterns To find out which pattern to use, refer to: + - [7.5. Interactive handshake patterns (fundamental)](https://noiseprotocol.org/noise.html#interactive-handshake-patterns-fundamental) - [8. Protocol names and modifiers](https://noiseprotocol.org/noise.html#protocol-names-and-modifiers)