Fix VPN routing: give portforward-helper NET_ADMIN to delete gluetun rule 100

gluetun adds 'ip rule priority 100 from <pod-IP> lookup 200' which routes
all pod traffic via eth0. The iptables OUTPUT DROP policy then blocks it.
Rule 101 (not fwmark 0xca6c -> tun0) never fires because rule 100 matches
first. Result: qBittorrent has no internet access through the VPN.

Fix: portforward-helper waits for gluetun to be ready (polls :9999), then
deletes rule 100. Application traffic then falls through to rule 101 and
routes correctly via tun0.
This commit is contained in:
Will Anderson
2026-04-15 01:40:02 -05:00
parent 87367d4387
commit e12fd9781a
2 changed files with 57 additions and 14 deletions
@@ -107,6 +107,9 @@ spec:
- name: portforward-helper
image: alpine:latest
imagePullPolicy: Always
securityContext:
capabilities:
add: ["NET_ADMIN"]
env:
- name: COORDINATOR_URL
value: "https://fornax.neuralplatform.ai"
@@ -116,6 +119,14 @@ spec:
- sh
- -c
- |
echo "Waiting for gluetun health endpoint..."
while ! wget -q -T 3 -O- http://127.0.0.1:9999 > /dev/null 2>&1; do sleep 3; done
echo "gluetun ready"
# gluetun adds 'ip rule priority 100 from <pod-IP> lookup 200' which routes
# all pod traffic via eth0, bypassing tun0. The iptables OUTPUT DROP policy
# then blocks it. Removing rule 100 lets traffic fall through to priority 101
# (not fwmark 0xca6c -> table 51820 -> tun0).
ip rule del priority 100 2>/dev/null && echo "Fixed VPN routing (removed rule 100)" || echo "Rule 100 not present"
until apk add -q curl 2>/dev/null; do sleep 5; done
echo "Watching /tmp/gluetun/forwarded_port for assigned port..."
TTL=300
@@ -129,18 +140,13 @@ spec:
curl -s -b /tmp/qbt.txt -X POST http://localhost:8080/api/v2/app/setPreferences \
-d "json={\"listen_port\":$PORT,\"random_port\":false}" >/dev/null 2>&1
echo "$(date): qBittorrent listen port set to $PORT"
# Notify coordinator of port lease
if [ -n "$COORDINATOR_URL" ] && [ -n "$WORKER_ID" ]; then
curl -s -X POST "$COORDINATOR_URL/api/v2/workers/$WORKER_ID/port-lease" \
-H "Content-Type: application/json" \
-d "{\"port\": $PORT, \"ttlSeconds\": $TTL}" >/dev/null 2>&1 || true
echo "$(date): Coordinator notified of port $PORT (TTL: ${TTL}s)"
fi
else
echo "$(date): Port file empty, waiting..."
fi
else
echo "$(date): Waiting for gluetun to write port file..."
fi
sleep 45
done