From 0698e0d35f792e4e244c033468e621c7ae97eeb2 Mon Sep 17 00:00:00 2001 From: Jamil Date: Mon, 18 Aug 2025 16:59:40 -0400 Subject: [PATCH] ci: test IPv6 for CIDR resources (#10168) Docker for Mac finally supports IPv6 in general availability. It's time to add IPv6 to our suite of integration tests. The thinking behind this PR is try and not slow down CI much, if at all, by testing IPv6 side-by-side with the existing IPv4 tests. More comprehensive testing is being developed in #10131 that will test things like IPv4-in-6 relaying, client / gateway IP stack mismatches, and so forth. --- docker-compose.yml | 27 +++++++++++++++---- elixir/apps/domain/priv/repo/seeds.exs | 24 +++++++++++++++++ scripts/tests/direct-curl-api-down.sh | 2 ++ scripts/tests/direct-curl-api-restart.sh | 2 ++ scripts/tests/direct-curl-ecn.sh | 1 + scripts/tests/lib.sh | 5 ++-- scripts/tests/relay-graceful-shutdown.sh | 2 ++ scripts/tests/systemd/dns-systemd-resolved.sh | 1 + scripts/tests/systemd/env | 2 +- .../systemd/firezone-client-headless.service | 2 +- 10 files changed, 59 insertions(+), 9 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 3c710f3c7..89582e5c9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -334,6 +334,7 @@ services: - NET_ADMIN sysctls: - net.ipv6.conf.all.disable_ipv6=0 + - net.ipv6.conf.default.disable_ipv6=0 devices: - "/dev/net/tun:/dev/net/tun" depends_on: @@ -342,6 +343,7 @@ services: networks: app: ipv4_address: 172.28.0.100 + ipv6_address: 172:28:0::100 gateway: healthcheck: @@ -366,6 +368,7 @@ services: - net.ipv4.ip_forward=1 - net.ipv4.conf.all.src_valid_mark=1 - net.ipv6.conf.all.disable_ipv6=0 + - net.ipv6.conf.default.disable_ipv6=0 - net.ipv6.conf.all.forwarding=1 - net.ipv6.conf.default.forwarding=1 devices: @@ -376,16 +379,20 @@ services: networks: app: ipv4_address: 172.28.0.105 + ipv6_address: 172:28:0::105 dns_resources: resources: httpbin: image: kennethreitz/httpbin + # Needed to bind to IPv6 + command: ["gunicorn", "-b", "[::]:80", "httpbin:app"] healthcheck: test: ["CMD-SHELL", "ps -C gunicorn"] networks: resources: ipv4_address: 172.20.0.100 + ipv6_address: 172:20:0::100 download.httpbin: # Named after `httpbin` because that is how DNS resources are configured for the test setup. build: @@ -421,10 +428,12 @@ services: networks: resources: ipv4_address: 172.20.0.110 + ipv6_address: 172:20:0::110 relay-1: environment: PUBLIC_IP4_ADDR: ${RELAY_1_PUBLIC_IP4_ADDR:-172.28.0.101} + PUBLIC_IP6_ADDR: ${RELAY_1_PUBLIC_IP6_ADDR:-172:28:0::101} # PUBLIC_IP6_ADDR: fcff:3990:3990::101 # LOWEST_PORT: 55555 # HIGHEST_PORT: 55666 @@ -443,6 +452,9 @@ services: args: PACKAGE: firezone-relay image: ${RELAY_IMAGE:-ghcr.io/firezone/debug/relay}:${RELAY_TAG:-main} + sysctls: + - net.ipv6.conf.all.disable_ipv6=0 + - net.ipv6.conf.default.disable_ipv6=0 healthcheck: test: ["CMD-SHELL", "lsof -i UDP | grep firezone-relay"] start_period: 10s @@ -462,10 +474,12 @@ services: networks: app: ipv4_address: ${RELAY_1_PUBLIC_IP4_ADDR:-172.28.0.101} + ipv6_address: ${RELAY_1_PUBLIC_IP6_ADDR:-172:28:0::101} relay-2: environment: PUBLIC_IP4_ADDR: ${RELAY_2_PUBLIC_IP4_ADDR:-172.28.0.201} + PUBLIC_IP6_ADDR: ${RELAY_2_PUBLIC_IP6_ADDR:-172:28:0::201} # PUBLIC_IP6_ADDR: fcff:3990:3990::101 # Token for self-hosted Relay # FIREZONE_TOKEN: ".SFMyNTY.g2gDaANtAAAAJGM4OWJjYzhjLTkzOTItNGRhZS1hNDBkLTg4OGFlZjZkMjhlMG0AAAAkNTQ5YzQxMDctMTQ5Mi00ZjhmLWE0ZWMtYTlkMmE2NmQ4YWE5bQAAADhQVTVBSVRFMU84VkRWTk1ITU9BQzc3RElLTU9HVERJQTY3MlM2RzFBQjAyT1MzNEg1TUUwPT09PW4GAEngLBONAWIAAVGA.E-f2MFdGMX7JTL2jwoHBdWcUd2G3UNz2JRZLbQrlf0k" @@ -482,6 +496,9 @@ services: args: PACKAGE: firezone-relay image: ${RELAY_IMAGE:-ghcr.io/firezone/debug/relay}:${RELAY_TAG:-main} + sysctls: + - net.ipv6.conf.all.disable_ipv6=0 + - net.ipv6.conf.default.disable_ipv6=0 healthcheck: test: ["CMD-SHELL", "lsof -i UDP | grep firezone-relay"] start_period: 10s @@ -494,6 +511,7 @@ services: networks: app: ipv4_address: ${RELAY_2_PUBLIC_IP4_ADDR:-172.28.0.201} + ipv6_address: ${RELAY_2_PUBLIC_IP6_ADDR:-172:28:0::201} otel: image: otel/opentelemetry-collector:latest @@ -593,18 +611,17 @@ networks: config: - subnet: 172.21.0.0/24 resources: - # enable_ipv6: true + enable_ipv6: true ipam: config: - subnet: 172.20.0.0/24 - # - subnet: fc00:ff:1::/48 + - subnet: 172:20:0::/64 app: - # enable_ipv6: true + enable_ipv6: true ipam: config: - subnet: 172.28.0.0/24 - # Currently not working on testbed - # - subnet: fc00:ff:2::/48 + - subnet: 172:28:0::/64 99-ghost-in-da-edge: name: ghost-in-da-edge internal: false diff --git a/elixir/apps/domain/priv/repo/seeds.exs b/elixir/apps/domain/priv/repo/seeds.exs index faab1b73b..90b1d441c 100644 --- a/elixir/apps/domain/priv/repo/seeds.exs +++ b/elixir/apps/domain/priv/repo/seeds.exs @@ -1001,6 +1001,19 @@ defmodule Domain.Repo.Seeds do admin_subject ) + {:ok, ipv6_resource} = + Resources.create_resource( + %{ + type: :cidr, + name: "MyCorp Network (IPv6)", + address: "172:20:0::1/64", + address_description: "172:20:0::1/64", + connections: [%{gateway_group_id: gateway_group.id}], + filters: [] + }, + admin_subject + ) + {:ok, dns_httpbin_resource} = Resources.create_resource( %{ @@ -1044,6 +1057,7 @@ defmodule Domain.Repo.Seeds do IO.puts(" #{example_dns.address} - DNS - gateways: #{gateway_name}") IO.puts(" #{ip_resource.address} - IP - gateways: #{gateway_name}") IO.puts(" #{cidr_resource.address} - CIDR - gateways: #{gateway_name}") + IO.puts(" #{ipv6_resource.address} - CIDR - gateways: #{gateway_name}") IO.puts(" #{dns_httpbin_resource.address} - DNS - gateways: #{gateway_name}") IO.puts(" #{search_domain_resource.address} - DNS - gateways: #{gateway_name}") IO.puts("") @@ -1128,6 +1142,16 @@ defmodule Domain.Repo.Seeds do admin_subject ) + {:ok, _} = + Policies.create_policy( + %{ + name: "All Access To Network", + actor_group_id: synced_group.id, + resource_id: ipv6_resource.id + }, + admin_subject + ) + {:ok, _} = Policies.create_policy( %{ diff --git a/scripts/tests/direct-curl-api-down.sh b/scripts/tests/direct-curl-api-down.sh index bddca60d3..cd70fc816 100755 --- a/scripts/tests/direct-curl-api-down.sh +++ b/scripts/tests/direct-curl-api-down.sh @@ -3,7 +3,9 @@ source "./scripts/tests/lib.sh" client_curl_resource "172.20.0.100/get" +client_curl_resource "[172:20:0::100]/get" docker compose stop api # Stop portal client_curl_resource "172.20.0.100/get" +client_curl_resource "[172:20:0::100]/get" diff --git a/scripts/tests/direct-curl-api-restart.sh b/scripts/tests/direct-curl-api-restart.sh index 654810308..020075189 100755 --- a/scripts/tests/direct-curl-api-restart.sh +++ b/scripts/tests/direct-curl-api-restart.sh @@ -5,7 +5,9 @@ source "./scripts/tests/lib.sh" docker compose restart api # Restart portal client_curl_resource "172.20.0.100/get" +client_curl_resource "[172:20:0::100]/get" docker compose restart api # Restart again client_curl_resource "172.20.0.100/get" +client_curl_resource "[172:20:0::100]/get" diff --git a/scripts/tests/direct-curl-ecn.sh b/scripts/tests/direct-curl-ecn.sh index 026cf6f3a..39abb4dc0 100755 --- a/scripts/tests/direct-curl-ecn.sh +++ b/scripts/tests/direct-curl-ecn.sh @@ -5,3 +5,4 @@ source "./scripts/tests/lib.sh" client sysctl -w net.ipv4.tcp_ecn=1 client_curl_resource "172.20.0.100/get" +client_curl_resource "[172:20:0::100]/get" diff --git a/scripts/tests/lib.sh b/scripts/tests/lib.sh index e026a7790..5b71e2fdf 100755 --- a/scripts/tests/lib.sh +++ b/scripts/tests/lib.sh @@ -20,10 +20,11 @@ function relay2() { function install_iptables_drop_rules() { # Install `iptables` to have it available in the compatibility tests - docker compose exec -it client /bin/sh -c 'apk add iptables' + client apk add iptables # 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' + client iptables -A OUTPUT -d 172.28.0.105 -j DROP + client ip6tables -A OUTPUT -d 172:28:0::105 -j DROP } function client_curl_resource() { diff --git a/scripts/tests/relay-graceful-shutdown.sh b/scripts/tests/relay-graceful-shutdown.sh index 76d7f72d1..788921ced 100755 --- a/scripts/tests/relay-graceful-shutdown.sh +++ b/scripts/tests/relay-graceful-shutdown.sh @@ -5,6 +5,7 @@ source "./scripts/tests/lib.sh" # Arrange: Setup a relayed connection install_iptables_drop_rules client_curl_resource "172.20.0.100/get" +client_curl_resource "[172:20:0::100]/get" # Act: Send SIGTERM docker compose kill relay-1 --signal SIGTERM @@ -13,6 +14,7 @@ sleep 2 # Closing websocket isn't instant. # Assert: Dataplane still works client_curl_resource "172.20.0.100/get" +client_curl_resource "[172:20:0::100]/get" # Assert: Websocket connection is cut OPEN_SOCKETS=$(relay1 netstat -tn | grep "ESTABLISHED" | grep 8081 || true) # Portal listens on port 8081 diff --git a/scripts/tests/systemd/dns-systemd-resolved.sh b/scripts/tests/systemd/dns-systemd-resolved.sh index 2d38f2d01..c3fe7c47e 100755 --- a/scripts/tests/systemd/dns-systemd-resolved.sh +++ b/scripts/tests/systemd/dns-systemd-resolved.sh @@ -10,6 +10,7 @@ SERVICE_NAME=firezone-client-headless debug_exit() { echo "Bailing out. Waiting a couple seconds for things to settle..." sleep 5 + docker compose ps -a resolvectl dns tun-firezone || true systemctl status "$SERVICE_NAME" || true exit 1 diff --git a/scripts/tests/systemd/env b/scripts/tests/systemd/env index 8cf58c5e9..42e1d5d7d 100644 --- a/scripts/tests/systemd/env +++ b/scripts/tests/systemd/env @@ -1 +1 @@ -FIREZONE_API_URL=ws://localhost:8081 +FIREZONE_API_URL=ws://127.0.0.1:8081 diff --git a/scripts/tests/systemd/firezone-client-headless.service b/scripts/tests/systemd/firezone-client-headless.service index 5e8a0d36f..a9a12a04b 100644 --- a/scripts/tests/systemd/firezone-client-headless.service +++ b/scripts/tests/systemd/firezone-client-headless.service @@ -34,7 +34,7 @@ SystemCallArchitectures=native SystemCallFilter=@aio @basic-io @file-system @io-event @network-io @signal @system-service UMask=077 -Environment="FIREZONE_API_URL=ws://localhost:8081" +Environment="FIREZONE_API_URL=ws://127.0.0.1:8081" # TODO: Remove after #6163 gets into a release Environment="FIREZONE_DNS_CONTROL=systemd-resolved" Environment="RUST_LOG=info"