diff --git a/test/test-manager/src/tests/tunnel.rs b/test/test-manager/src/tests/tunnel.rs index a66b7f7b881a..3bbb83244300 100644 --- a/test/test-manager/src/tests/tunnel.rs +++ b/test/test-manager/src/tests/tunnel.rs @@ -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; @@ -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(()) +}