From cf2470ba1e6ea103a055654f04ceefe245cd6f4e Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 16 Jul 2025 18:29:33 +0800 Subject: [PATCH] test(iperf): install iptables rule inside of container (#9880) In Docker environments, applying iptables rules to filter container-container traffic on the Docker bridged network is not reliable, leading to direct connections being established in our relayed tests. To fix this, we insert the rules directly from the client container itself. --------- Co-authored-by: Jamil Bou Kheir --- .github/workflows/_integration_tests.yml | 6 +++++- .github/workflows/ci.yml | 5 ++++- rust/Dockerfile | 2 +- rust/client-shared/src/eventloop.rs | 17 +++++++++++++++++ scripts/tests/lib.sh | 11 ++++------- 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/.github/workflows/_integration_tests.yml b/.github/workflows/_integration_tests.yml index b0ce148a0..86de471f8 100644 --- a/.github/workflows/_integration_tests.yml +++ b/.github/workflows/_integration_tests.yml @@ -147,7 +147,11 @@ jobs: - name: Ensure Client emitted no warnings if: "!cancelled()" - run: docker compose logs client | grep "WARN" && exit 1 || exit 0 + # Remove the error filter once headless-client 1.5.2 is released. + run: | + docker compose logs client | \ + grep "Operation not permitted (os error 1)" --invert | \ + grep "WARN" && exit 1 || exit 0 - name: Show Client logs if: "!cancelled()" run: docker compose logs client diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2c4a01e7d..582e2f8e1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,6 +107,9 @@ jobs: if grep -q '^website/' changed_files.txt; then jobs="${jobs},codeql" fi + if grep -q '^scripts/tests/' changed_files.txt; then + jobs="${jobs},build-artifacts,build-perf-artifacts" + fi echo "jobs_to_run=$jobs" >> "$GITHUB_OUTPUT" @@ -311,7 +314,7 @@ jobs: run: | # We need to increase the log level to make sure that they don't hold off storm of packets # generated by UDP tests. Wire is especially chatty. - sed -i 's/^\(\s*\)RUST_LOG:.*$/\1RUST_LOG: wire=error,info/' docker-compose.yml + sed -i 's/^\(\s*\)RUST_LOG:.*$/\1RUST_LOG: wire=error,opentelemetry_sdk=error,info/' docker-compose.yml grep RUST_LOG docker-compose.yml # Start services in the same order each time for the tests diff --git a/rust/Dockerfile b/rust/Dockerfile index ee6d46276..2a09a2e60 100644 --- a/rust/Dockerfile +++ b/rust/Dockerfile @@ -46,7 +46,7 @@ CMD ${PACKAGE} # Build an image for GitHub Actions which includes debug asserts and more test utilities FROM runtime AS debug -RUN apk add --no-cache iperf3 bind-tools iproute2 jq procps +RUN apk add --no-cache iperf3 bind-tools iproute2 jq procps iptables ## Build first with `cargo build --target ${TARGET} -p ${PACKAGE} && mv /target/${TARGET}/debug/${PACKAGE} .` ARG PACKAGE diff --git a/rust/client-shared/src/eventloop.rs b/rust/client-shared/src/eventloop.rs index 05cea7832..2129d72ef 100644 --- a/rust/client-shared/src/eventloop.rs +++ b/rust/client-shared/src/eventloop.rs @@ -10,6 +10,7 @@ use firezone_tunnel::messages::client::{ use firezone_tunnel::{ClientTunnel, IpConfig}; use ip_network::{Ipv4Network, Ipv6Network}; use phoenix_channel::{ErrorReply, OutboundRequestId, PhoenixChannel, PublicKeyParam}; +use std::mem; use std::net::{Ipv4Addr, Ipv6Addr}; use std::time::Instant; use std::{ @@ -27,6 +28,8 @@ pub struct Eventloop { portal: PhoenixChannel<(), IngressMessages, (), PublicKeyParam>, cmd_rx: tokio::sync::mpsc::UnboundedReceiver, event_tx: tokio::sync::mpsc::Sender, + + logged_permission_denied: bool, } /// Commands that can be sent to the [`Eventloop`]. @@ -86,6 +89,7 @@ impl Eventloop { portal, cmd_rx, event_tx, + logged_permission_denied: false, } } } @@ -154,6 +158,19 @@ impl Eventloop { return Poll::Ready(Err(e)); } + if e.root_cause() + .downcast_ref::() + .is_some_and(|e| e.kind() == io::ErrorKind::PermissionDenied) + { + if !mem::replace(&mut self.logged_permission_denied, true) { + tracing::info!( + "Encountered `PermissionDenied` IO error. Check your local firewall rules to allow outbound STUN/TURN/WireGuard and general UDP traffic." + ) + } + + continue; + } + tracing::warn!("Tunnel error: {e:#}"); continue; } diff --git a/scripts/tests/lib.sh b/scripts/tests/lib.sh index fbadff191..2182303ef 100755 --- a/scripts/tests/lib.sh +++ b/scripts/tests/lib.sh @@ -19,14 +19,11 @@ function relay2() { } function install_iptables_drop_rules() { - sudo iptables -I FORWARD 1 -s 172.28.0.100 -d 172.28.0.105 -j DROP - sudo iptables -I FORWARD 1 -s 172.28.0.105 -d 172.28.0.100 -j DROP - trap remove_iptables_drop_rules EXIT # Cleanup after us -} + # Install `iptables` to have it available in the compatibility tests + docker compose exec -it client /bin/sh -c 'apk add iptables' -function remove_iptables_drop_rules() { - sudo iptables -D FORWARD -s 172.28.0.100 -d 172.28.0.105 -j DROP - sudo iptables -D FORWARD -s 172.28.0.105 -d 172.28.0.100 -j DROP + # Execute within the client container because doing so from the host is not reliable in CI. + docker compose exec -it client /bin/sh -c 'iptables -A OUTPUT -d 172.28.0.105 -j DROP' } function client_curl_resource() {