Skip to content

Commit

Permalink
test: add more tests and use sparse index on build
Browse files Browse the repository at this point in the history
  • Loading branch information
ihciah committed Feb 25, 2023
1 parent 442441e commit 6285b05
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 26 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build-docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:

env:
IMAGE_NAME: shadow-tls
CARGO_NET_GIT_FETCH_WITH_CLI: true

jobs:
build:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:

env:
CARGO_TERM_COLOR: always
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse

jobs:
build-cross:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ on:
env:
RUST_TOOLCHAIN: nightly
TOOLCHAIN_PROFILE: minimal
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse

jobs:
lints:
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ WORKDIR /usr/src/shadow-tls
RUN apk add --no-cache musl-dev libressl-dev

COPY . .
RUN RUSTFLAGS="" cargo build --bin shadow-tls --release
RUN CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse RUSTFLAGS="" cargo build --bin shadow-tls --release

FROM alpine:latest

Expand Down
2 changes: 1 addition & 1 deletion src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ impl<LA, TA> ShadowTlsClient<LA, TA> {
let stream = stream.into_inner();

// stage2:
if maybe_srh.is_none() || !authorized && self.v3.strict() {
if (maybe_srh.is_none() || !authorized) && self.v3.strict() {
tracing::warn!("V3 strict enabled: traffic hijacked or TLS1.3 is not supported");
let tls_stream = monoio_rustls_fork_shadow_tls::ClientTlsStream::new(stream, session);
if let Err(e) = fake_request(tls_stream).await {
Expand Down
29 changes: 23 additions & 6 deletions tests/sni.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
use std::time::Duration;

use monoio::net::TcpStream;
use monoio::{
io::{AsyncReadRentExt, AsyncWriteRentExt},
net::TcpStream,
};
use monoio_rustls_fork_shadow_tls::TlsConnector;
use rustls_fork_shadow_tls::{OwnedTrustAnchor, RootCertStore, ServerName};
use shadow_tls::{RunningArgs, TlsAddrs, V3Mode};

const FEISHU_HTTP_REQUEST: &[u8; 48] = b"GET / HTTP/1.1\r\nHost: feishu.cn\r\nAccept: */*\r\n\r\n";
const FEISHU_CN_HTTP_RESP: &[u8; 30] = b"HTTP/1.1 301 Moved Permanently";

#[monoio::test(enable_timer = true)]
async fn sni() {
// construct tls connector
Expand All @@ -24,7 +30,7 @@ async fn sni() {

// run server
let server = RunningArgs::Server {
listen_addr: "127.0.0.1:20012".to_string(),
listen_addr: "127.0.0.1:32000".to_string(),
target_addr: "t.cn:80".to_string(),
tls_addr: TlsAddrs::try_from("feishu.cn").unwrap(),
password: "test".to_string(),
Expand All @@ -35,14 +41,25 @@ async fn sni() {
monoio::time::sleep(Duration::from_secs(1)).await;

// connect and handshake
assert!(tls_connector
let mut feishu_conn = tls_connector
.connect(
ServerName::try_from("feishu.cn").unwrap(),
TcpStream::connect("127.0.0.1:20012").await.unwrap()
TcpStream::connect("127.0.0.1:32000").await.unwrap(),
)
.await
.is_ok());
let conn = TcpStream::connect("127.0.0.1:20012").await.unwrap();
.expect("unable to connect feishu.cn");
feishu_conn
.write_all(FEISHU_HTTP_REQUEST.to_vec())
.await
.0
.unwrap();
let (res, buf) = feishu_conn
.read_exact(vec![0; FEISHU_CN_HTTP_RESP.len()])
.await;
assert!(res.is_ok());
assert_eq!(&buf, FEISHU_CN_HTTP_RESP);

let conn = TcpStream::connect("127.0.0.1:32000").await.unwrap();
assert!(tls_connector
.connect(ServerName::try_from("t.cn").unwrap(), conn)
.await
Expand Down
55 changes: 46 additions & 9 deletions tests/tls12.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ use utils::*;
#[test]
fn tls12_v2() {
let client = RunningArgs::Client {
listen_addr: "127.0.0.1:20000".to_string(),
target_addr: "127.0.0.1:20001".to_string(),
listen_addr: "127.0.0.1:30000".to_string(),
target_addr: "127.0.0.1:30001".to_string(),
tls_names: TlsNames::try_from("t.cn").unwrap(),
tls_ext: TlsExtConfig::new(None),
password: "test".to_string(),
nodelay: true,
v3: V3Mode::Disabled,
};
let server = RunningArgs::Server {
listen_addr: "127.0.0.1:20001".to_string(),
listen_addr: "127.0.0.1:30001".to_string(),
target_addr: "t.cn:80".to_string(),
tls_addr: TlsAddrs::try_from("t.cn").unwrap(),
password: "test".to_string(),
Expand All @@ -34,16 +34,16 @@ fn tls12_v2() {
#[test]
fn tls12_v3_lossy() {
let client = RunningArgs::Client {
listen_addr: "127.0.0.1:20002".to_string(),
target_addr: "127.0.0.1:20003".to_string(),
listen_addr: "127.0.0.1:30002".to_string(),
target_addr: "127.0.0.1:30003".to_string(),
tls_names: TlsNames::try_from("t.cn").unwrap(),
tls_ext: TlsExtConfig::new(None),
password: "test".to_string(),
nodelay: true,
v3: V3Mode::Lossy,
};
let server = RunningArgs::Server {
listen_addr: "127.0.0.1:20003".to_string(),
listen_addr: "127.0.0.1:30003".to_string(),
target_addr: "t.cn:80".to_string(),
tls_addr: TlsAddrs::try_from("t.cn").unwrap(),
password: "test".to_string(),
Expand All @@ -61,16 +61,16 @@ fn tls12_v3_lossy() {
#[should_panic]
fn tls12_v3_strict() {
let client = RunningArgs::Client {
listen_addr: "127.0.0.1:20004".to_string(),
target_addr: "127.0.0.1:20005".to_string(),
listen_addr: "127.0.0.1:30004".to_string(),
target_addr: "127.0.0.1:30005".to_string(),
tls_names: TlsNames::try_from("t.cn").unwrap(),
tls_ext: TlsExtConfig::new(None),
password: "test".to_string(),
nodelay: true,
v3: V3Mode::Strict,
};
let server = RunningArgs::Server {
listen_addr: "127.0.0.1:20005".to_string(),
listen_addr: "127.0.0.1:30005".to_string(),
target_addr: "t.cn:80".to_string(),
tls_addr: TlsAddrs::try_from("t.cn").unwrap(),
password: "test".to_string(),
Expand All @@ -79,3 +79,40 @@ fn tls12_v3_strict() {
};
utils::test_ok(client, server, T_CN_HTTP_REQUEST, T_CN_HTTP_RESP);
}

// protocol: v2
// Note: v2 can not defend against hijack attack.
// The interceptor will see TLS Alert.
// But it will not cause data error since the connection will be closed.
#[test]
fn tls12_v2_hijack() {
let client = RunningArgs::Client {
listen_addr: "127.0.0.1:30006".to_string(),
target_addr: "qq.com:443".to_string(),
tls_names: TlsNames::try_from("qq.com").unwrap(),
tls_ext: TlsExtConfig::new(None),
password: "test".to_string(),
nodelay: true,
v3: V3Mode::Disabled,
};
test_hijack(client);
}

// protocol: v3 lossy
// (v3 strict can not work with tls1.2)
// Note: tls1.2 with v3 lossy can not defend against hijack attack.
// The interceptor will see TLS Alert.
// But it will not cause data error since the connection will be closed.
#[test]
fn tls12_v3_lossy_hijack() {
let client = RunningArgs::Client {
listen_addr: "127.0.0.1:30007".to_string(),
target_addr: "qq.com:443".to_string(),
tls_names: TlsNames::try_from("qq.com").unwrap(),
tls_ext: TlsExtConfig::new(None),
password: "test".to_string(),
nodelay: true,
v3: V3Mode::Lossy,
};
test_hijack(client);
}
69 changes: 60 additions & 9 deletions tests/tls13.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ use utils::*;
#[test]
fn tls13_v2() {
let client = RunningArgs::Client {
listen_addr: "127.0.0.1:20006".to_string(),
target_addr: "127.0.0.1:20007".to_string(),
listen_addr: "127.0.0.1:31000".to_string(),
target_addr: "127.0.0.1:31001".to_string(),
tls_names: TlsNames::try_from("feishu.cn").unwrap(),
tls_ext: TlsExtConfig::new(None),
password: "test".to_string(),
nodelay: true,
v3: V3Mode::Disabled,
};
let server = RunningArgs::Server {
listen_addr: "127.0.0.1:20007".to_string(),
listen_addr: "127.0.0.1:31001".to_string(),
target_addr: "t.cn:80".to_string(),
tls_addr: TlsAddrs::try_from("feishu.cn").unwrap(),
password: "test".to_string(),
Expand All @@ -34,16 +34,16 @@ fn tls13_v2() {
#[test]
fn tls13_v3_lossy() {
let client = RunningArgs::Client {
listen_addr: "127.0.0.1:20008".to_string(),
target_addr: "127.0.0.1:20009".to_string(),
listen_addr: "127.0.0.1:31002".to_string(),
target_addr: "127.0.0.1:31003".to_string(),
tls_names: TlsNames::try_from("feishu.cn").unwrap(),
tls_ext: TlsExtConfig::new(None),
password: "test".to_string(),
nodelay: true,
v3: V3Mode::Lossy,
};
let server = RunningArgs::Server {
listen_addr: "127.0.0.1:20009".to_string(),
listen_addr: "127.0.0.1:31003".to_string(),
target_addr: "t.cn:80".to_string(),
tls_addr: TlsAddrs::try_from("feishu.cn").unwrap(),
password: "test".to_string(),
Expand All @@ -59,16 +59,16 @@ fn tls13_v3_lossy() {
#[test]
fn tls13_v3_strict() {
let client = RunningArgs::Client {
listen_addr: "127.0.0.1:20010".to_string(),
target_addr: "127.0.0.1:20011".to_string(),
listen_addr: "127.0.0.1:31004".to_string(),
target_addr: "127.0.0.1:31005".to_string(),
tls_names: TlsNames::try_from("feishu.cn").unwrap(),
tls_ext: TlsExtConfig::new(None),
password: "test".to_string(),
nodelay: true,
v3: V3Mode::Strict,
};
let server = RunningArgs::Server {
listen_addr: "127.0.0.1:20011".to_string(),
listen_addr: "127.0.0.1:31005".to_string(),
target_addr: "t.cn:80".to_string(),
tls_addr: TlsAddrs::try_from("feishu.cn").unwrap(),
password: "test".to_string(),
Expand All @@ -77,3 +77,54 @@ fn tls13_v3_strict() {
};
utils::test_ok(client, server, T_CN_HTTP_REQUEST, T_CN_HTTP_RESP);
}

// protocol: v2
// Note: v2 can not defend against hijack attack.
// The interceptor will not see TLS Alert.
// But it will cause data error.
#[test]
#[should_panic]
fn tls13_v2_hijack() {
let client = RunningArgs::Client {
listen_addr: "127.0.0.1:31006".to_string(),
target_addr: "feishu.cn:443".to_string(),
tls_names: TlsNames::try_from("feishu.cn").unwrap(),
tls_ext: TlsExtConfig::new(None),
password: "test".to_string(),
nodelay: true,
v3: V3Mode::Disabled,
};
test_hijack(client);
}

// protocol: v3 lossy
// tls1.3 with v3 protocol defends against hijack attack.
#[test]
fn tls13_v3_lossy_hijack() {
let client = RunningArgs::Client {
listen_addr: "127.0.0.1:31007".to_string(),
target_addr: "feishu.cn:443".to_string(),
tls_names: TlsNames::try_from("feishu.cn").unwrap(),
tls_ext: TlsExtConfig::new(None),
password: "test".to_string(),
nodelay: true,
v3: V3Mode::Lossy,
};
test_hijack(client);
}

// protocol: v3 strict
// tls1.3 with v3 protocol defends against hijack attack.
#[test]
fn tls13_v3_strict_hijack() {
let client = RunningArgs::Client {
listen_addr: "127.0.0.1:31008".to_string(),
target_addr: "feishu.cn:443".to_string(),
tls_names: TlsNames::try_from("feishu.cn").unwrap(),
tls_ext: TlsExtConfig::new(None),
password: "test".to_string(),
nodelay: true,
v3: V3Mode::Strict,
};
test_hijack(client);
}
16 changes: 16 additions & 0 deletions tests/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,19 @@ pub fn test_ok(
conn.read_exact(&mut buf).unwrap();
assert_eq!(&buf, http_response);
}

pub fn test_hijack(client: RunningArgs) {
let client_listen = match &client {
RunningArgs::Client { listen_addr, .. } => listen_addr.clone(),
RunningArgs::Server { .. } => panic!("not valid client args"),
};
client.build().expect("build client failed").start(1);

// sleep 1s to make sure client and server have started
std::thread::sleep(Duration::from_secs(1));
let mut conn = TcpStream::connect(client_listen).unwrap();
conn.write_all(b"dummy").unwrap();
conn.set_read_timeout(Some(Duration::from_secs(1))).unwrap();
let mut dummy_buf = [0; 1];
assert!(!matches!(conn.read(&mut dummy_buf), Ok(1)));
}

0 comments on commit 6285b05

Please sign in to comment.