Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests for custom SOCKS bridges #5779

Merged
merged 2 commits into from
Feb 8, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 202 additions & 3 deletions test/test-manager/src/tests/tunnel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ use crate::network_monitor::{start_packet_monitor, MonitorOptions};

use mullvad_management_interface::MullvadProxyClient;
use mullvad_types::relay_constraints::{
self, BridgeSettings, Constraint, OpenVpnConstraints, RelayConstraints, RelaySettings,
SelectedObfuscation, TransportPort, Udp2TcpObfuscationSettings, WireguardConstraints,
self, BridgeConstraints, BridgeSettings, BridgeType, Constraint, OpenVpnConstraints,
RelayConstraints, RelaySettings, SelectedObfuscation, TransportPort,
Udp2TcpObfuscationSettings, WireguardConstraints,
};
use mullvad_types::wireguard;
use talpid_types::net::{TransportProtocol, TunnelType};
use std::net::SocketAddr;
use talpid_types::net::{
proxy::{CustomProxy, Socks5Local, Socks5Remote},
TransportProtocol, TunnelType,
};
use test_macro::test_function;
use test_rpc::meta::Os;
use test_rpc::mullvad_daemon::ServiceStatus;
Expand Down Expand Up @@ -571,3 +576,197 @@ pub async fn test_quantum_resistant_multihop_udp2tcp_tunnel(

Ok(())
}

/// Try to connect to an OpenVPN relay via a remote, passwordless SOCKS5 server.
/// * No outgoing traffic to the bridge/entry relay is observed from the SUT.
/// * The conncheck reports an unexpected exit relay.
#[test_function]
pub async fn test_remote_socks_bridge(
_: TestContext,
rpc: ServiceClient,
mut mullvad_client: MullvadProxyClient,
) -> Result<(), Error> {
mullvad_client
.set_bridge_state(relay_constraints::BridgeState::On)
.await
.expect("failed to enable bridge mode");

mullvad_client
.set_bridge_settings(BridgeSettings {
bridge_type: BridgeType::Custom,
normal: BridgeConstraints::default(),
custom: Some(CustomProxy::Socks5Remote(Socks5Remote::new((
crate::vm::network::NON_TUN_GATEWAY,
crate::vm::network::SOCKS5_PORT,
)))),
})
.await
.expect("failed to update bridge settings");

set_relay_settings(
&mut mullvad_client,
RelaySettings::Normal(RelayConstraints {
tunnel_protocol: Constraint::Only(TunnelType::OpenVpn),
..Default::default()
}),
)
.await
.expect("failed to update relay settings");

//
// Connect to VPN
//

connect_and_wait(&mut mullvad_client).await?;

let (entry, exit) = match mullvad_client.get_tunnel_state().await? {
mullvad_types::states::TunnelState::Connected { endpoint, .. } => {
(endpoint.proxy.unwrap().endpoint, endpoint.endpoint)
}
actual => {
panic!("unexpected tunnel state. Expected `TunnelState::Connected` but got {actual:?}")
}
};

log::info!(
"Selected entry bridge {entry_addr} & exit relay {exit_addr}",
entry_addr = entry.address,
exit_addr = exit.address
);

// Start recording outgoing packets. Their destination will be verified
// against the bridge's IP address later.
let monitor = start_packet_monitor(
move |packet| packet.destination.ip() == entry.address.ip(),
MonitorOptions::default(),
)
.await;

//
// Verify exit IP
//

log::info!("Verifying exit server");

assert!(
helpers::using_mullvad_exit(&rpc).await,
"expected Mullvad exit IP"
);

//
// Verify entry IP
//

log::info!("Verifying entry server");

let monitor_result = monitor.into_result().await.unwrap();
assert!(
!monitor_result.packets.is_empty(),
"detected no traffic to entry server",
);

Ok(())
}

/// Try to connect to an OpenVPN relay via a local, passwordless SOCKS5 server.
/// * No outgoing traffic to the bridge/entry relay is observed from the SUT.
/// * The conncheck reports an unexpected exit relay.
#[test_function]
pub async fn test_local_socks_bridge(
_: TestContext,
rpc: ServiceClient,
mut mullvad_client: MullvadProxyClient,
) -> Result<(), Error> {
let remote_addr = SocketAddr::from((
crate::vm::network::NON_TUN_GATEWAY,
crate::vm::network::SOCKS5_PORT,
));
let socks_server = rpc
.start_tcp_forward("127.0.0.1:0".parse().unwrap(), remote_addr)
.await
.expect("failed to start TCP forward");

mullvad_client
.set_bridge_state(relay_constraints::BridgeState::On)
.await
.expect("failed to enable bridge mode");

mullvad_client
.set_bridge_settings(BridgeSettings {
bridge_type: BridgeType::Custom,
normal: BridgeConstraints::default(),
custom: Some(CustomProxy::Socks5Local(
Socks5Local::new_with_transport_protocol(
remote_addr,
socks_server.bind_addr().port(),
TransportProtocol::Tcp,
),
)),
})
.await
.expect("failed to update bridge settings");

set_relay_settings(
&mut mullvad_client,
RelaySettings::Normal(RelayConstraints {
tunnel_protocol: Constraint::Only(TunnelType::OpenVpn),
..Default::default()
}),
)
.await
.expect("failed to update relay settings");

//
// Connect to VPN
//

connect_and_wait(&mut mullvad_client).await?;

let (entry, exit) = match mullvad_client.get_tunnel_state().await? {
mullvad_types::states::TunnelState::Connected { endpoint, .. } => {
(endpoint.proxy.unwrap().endpoint, endpoint.endpoint)
}
actual => {
panic!("unexpected tunnel state. Expected `TunnelState::Connected` but got {actual:?}")
}
};

log::info!(
"Selected entry bridge {entry_addr} & exit relay {exit_addr}",
entry_addr = entry.address,
exit_addr = exit.address
);

// Start recording outgoing packets. Their destination will be verified
// against the bridge's IP address later.
let monitor = start_packet_monitor(
move |packet| packet.destination.ip() == entry.address.ip(),
MonitorOptions::default(),
)
.await;

//
// Verify exit IP
//

log::info!("Verifying exit server");

assert!(
helpers::using_mullvad_exit(&rpc).await,
"expected Mullvad exit IP"
);

//
// Verify entry IP
//

log::info!("Verifying entry server");

let monitor_result = monitor.into_result().await.unwrap();
assert!(
!monitor_result.packets.is_empty(),
"detected no traffic to entry server",
);

Ok(())
}