Skip to content

Commit 52fadcd

Browse files
ci/cd: fix IPv6 timeouts with force-ipv4 action
This commit introduces the `force-ipv4` GitHub action to address connectivity issues caused by the lack of IPv6 support in GitHub runners. Details: - actions/runner#3138 - actions/runner-images#668 This change solves connection problems when Node's `fetch` API fails due to `UND_ERR_CONNECT_TIMEOUT` errors. Details: - actions/runner-images#9540 - actions/runner#3213 This action disables IPv6 at the system level, ensuring all outging requests use IPv4. Resolving connectivity issues when running external URL checks and Docker build checks. This solution is a temporary workaround until GitHub runners support IPv6 or Node `fetch` API has a working solution such as Happy Eyeball. Detais: - nodejs/node#41625 - nodejs/undici#1531
1 parent 8a5592f commit 52fadcd

File tree

5 files changed

+129
-0
lines changed

5 files changed

+129
-0
lines changed

.github/actions/force-ipv4/README.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# force-ipv4
2+
3+
## Overview
4+
5+
This GitHub action enforces IPv4 for all outgoing network requests. It addresses connectivity issues encountered in GitHub runners, where IPv6 requests may lead to timeouts due to the lack of IPv6 support [1] [2].
6+
7+
## Background
8+
9+
Some applications attempt network connections over IPv6.
10+
Such as requests made by Node's `fetch` API causes `UND_ERR_CONNECT_TIMEOUT` errors [3] [4].
11+
This happens when the software cannot handle this such as by using Happy Eyeballs [5] [6].
12+
13+
## Usage
14+
15+
To use this action in your GitHub workflow, add the following step before any job that requires network access:
16+
17+
```yaml
18+
- name: Enforce IPv4 Connectivity
19+
uses: ./.github/actions/force-ipv4
20+
```
21+
22+
## Note
23+
24+
This action is a workaround addressing specific IPv6-related connectivity issues on GitHub runners and may not be necessary if GitHub's infrastructure evolves to fully support IPv6 in the future.
25+
26+
[1]: https://archive.ph/2024.03.28-185829/https://github.com/actions/runner/issues/3138 "Actions Runner fails on IPv6 only host · Issue #3138 · actions/runner · GitHub | github.com"
27+
[2]: https://archive.ph/2024.03.28-185838/https://github.com/actions/runner-images/issues/668 "IPv6 on GitHub-hosted runners · Issue #668 · actions/runner-images · GitHub | github.com"
28+
[3]: https://archive.ph/2024.03.28-185847/https://github.com/actions/runner/issues/3213 "GitHub runner cannot send `fetch` with `node`, failing with IPv6 DNS error `UND_ERR_CONNECT_TIMEOUT` · Issue #3213 · actions/runner · GitHub | github.com"
29+
[4]: https://archive.ph/2024.03.28-185853/https://github.com/actions/runner-images/issues/9540 "Cannot send outbound requests using node fetch, failing with IPv6 DNS error UND_ERR_CONNECT_TIMEOUT · Issue #9540 · actions/runner-images · GitHub | github.com"
30+
[5]: https://archive.ph/2024.03.28-185900/https://github.com/nodejs/node/issues/41625 "Happy Eyeballs support (address IPv6 issues in Node 17) · Issue #41625 · nodejs/node · GitHub | github.com"
31+
[6]: https://archive.ph/2024.03.28-185910/https://github.com/nodejs/undici/issues/1531 "fetch times out in under 5 seconds · Issue #1531 · nodejs/undici · GitHub | github.com"

.github/actions/force-ipv4/action.yml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
inputs:
2+
project-root:
3+
required: false
4+
default: '.'
5+
runs:
6+
using: composite
7+
steps:
8+
-
9+
name: Run prefer IPv4 script
10+
shell: bash
11+
run: ./.github/actions/force-ipv4/force-ipv4.sh
12+
working-directory: ${{ inputs.project-root }}
+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/usr/bin/env bash
2+
3+
main() {
4+
if is_linux; then
5+
echo 'Configuring Linux...'
6+
7+
configure_warp_with_doh_and_ipv6_exclusion_on_linux # [WORKS] Resolves the issue when run independently on GitHub runners lacking IPv6 support.
8+
prefer_ipv4_on_linux # [DOES NOT WORK] It does not resolve the issue when run independently on GitHub runners without IPv6 support.
9+
10+
# Considered alternatives:
11+
# - `sysctl` commands, and direct changes to `/proc/sys/net/` and `/etc/sysctl.conf` led to silent
12+
# Node 18 exits (code: 13) when using `fetch`.
13+
elif is_macos; then
14+
echo 'Configuring macOS...'
15+
16+
configure_warp_with_doh_and_ipv6_exclusion_on_macos # [WORKS] Resolves the issue when run independently on GitHub runners lacking IPv6 support.
17+
disable_ipv6_on_macos # [WORKS INCONSISTENTLY] Resolves the issue inconsistently when run independently on GitHub runners without IPv6 support.
18+
fi
19+
echo "IPv4: $(curl --ipv4 --silent --max-time 15 --retry 3 --user-agent Mozilla https://api.ip.sb/geoip)"
20+
echo "IPv6: $(curl --ipv6 --silent --max-time 15 --retry 3 --user-agent Mozilla https://api.ip.sb/geoip)"
21+
}
22+
23+
is_linux() {
24+
[[ "$(uname -s)" == "Linux" ]]
25+
}
26+
27+
is_macos() {
28+
[[ "$(uname -s)" == "Darwin" ]]
29+
}
30+
31+
configure_warp_with_doh_and_ipv6_exclusion_on_linux() {
32+
install_warp_on_debian
33+
configure_warp_doh_and_exclude_ipv6
34+
}
35+
36+
configure_warp_with_doh_and_ipv6_exclusion_on_macos() {
37+
brew install cloudflare-warp
38+
configure_warp_doh_and_exclude_ipv6
39+
}
40+
41+
configure_warp_doh_and_exclude_ipv6() {
42+
echo 'Beginning configuration of the Cloudflare WARP client with DNS-over-HTTPS and IPv6 exclusion...'
43+
echo 'Initiating client registration with Cloudflare...'
44+
warp-cli --accept-tos registration new
45+
echo 'Configuring WARP to operate in DNS-over-HTTPS mode (warp+doh)...'
46+
warp-cli --accept-tos mode warp+doh
47+
echo 'Excluding IPv6 traffic from WARP by configuring it as a split tunnel...'
48+
warp-cli --accept-tos add-excluded-route '::/0' # Exclude IPv6, forcing IPv4 resolution
49+
# `tunnel ip add` does not work with IP ranges, see https://community.cloudflare.com/t/cant-cidr-for-split-tunnling/630834
50+
echo 'Establishing WARP connection...'
51+
warp-cli --accept-tos connect
52+
}
53+
54+
install_warp_on_debian() {
55+
curl -fsSL https://pkg.cloudflareclient.com/pubkey.gpg | sudo gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg
56+
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflare-client.list
57+
sudo apt-get update
58+
sudo apt-get install -y cloudflare-warp
59+
}
60+
61+
disable_ipv6_on_macos() {
62+
networksetup -listallnetworkservices \
63+
| tail -n +2 \
64+
| while IFS= read -r interface; do
65+
echo "Disabling IPv6 on: $interface..."
66+
networksetup -setv6off "$interface"
67+
done
68+
}
69+
70+
prefer_ipv4_on_linux() {
71+
local -r gai_config_file_path='/etc/gai.conf'
72+
if [ ! -f "$gai_config_file_path" ]; then
73+
echo "Creating $gai_config_file_path since it doesn't exist..."
74+
touch "$gai_config_file_path"
75+
fi
76+
echo "precedence ::ffff:0:0/96 100" | sudo tee -a "$gai_config_file_path" > /dev/null
77+
echo "Configuration complete."
78+
}
79+
80+
main

.github/workflows/checks.build.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ jobs:
9595
-
9696
name: Run Docker image on port 8080
9797
run: docker run -d -p 8080:80 --rm --name privacy.sexy undergroundwires/privacy.sexy:latest
98+
-
99+
name: Enforce IPv4 Connectivity # Used due to GitHub runners' lack of IPv6 support, preventing request timeouts.
100+
uses: ./.github/actions/force-ipv4
98101
-
99102
name: Check server is up and returns HTTP 200
100103
run: >-

.github/workflows/checks.external-urls.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ jobs:
1717
-
1818
name: Install dependencies
1919
uses: ./.github/actions/npm-install-dependencies
20+
-
21+
name: Enforce IPv4 Connectivity # Used due to GitHub runners' lack of IPv6 support, preventing request timeouts.
22+
uses: ./.github/actions/force-ipv4
2023
-
2124
name: Test
2225
run: npm run check:external-urls

0 commit comments

Comments
 (0)