mirror of
https://github.com/shadowsocks/shadowsocks-libev.git
synced 2026-10-20 13:03:50 +00:00
Boot a minimal Alpine Linux guest in QEMU with iptables, ss-redir, and ss-nat to verify the full transparent proxy chain: iptables REDIRECT → ss-redir → ss-server (host) → internet. The host-built ss-redir binary and its shared library dependencies are copied into the Alpine rootfs, avoiding static linking issues. Uses QEMU user-mode networking with KVM acceleration when available. Linux-only CI step with 8-minute timeout. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
169 lines
4.4 KiB
Bash
Executable File
169 lines
4.4 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Guest init script (PID 1) for ss-redir transparent proxy test.
|
|
# Runs inside a QEMU Alpine Linux VM.
|
|
#
|
|
# Flow:
|
|
# 1. Mount filesystems and load kernel modules
|
|
# 2. Configure networking (QEMU user-mode: guest 10.0.2.15, host 10.0.2.2)
|
|
# 3. Start ss-redir pointing at ss-server on the host
|
|
# 4. Run ss-nat to set up iptables REDIRECT rules
|
|
# 5. Test with curl through the transparent proxy
|
|
# 6. Print REDIR_TEST_PASS or REDIR_TEST_FAIL to serial console
|
|
#
|
|
|
|
set -e
|
|
|
|
# --- helpers ----------------------------------------------------------------
|
|
|
|
pass() {
|
|
echo ""
|
|
echo "========================================="
|
|
echo "REDIR_TEST_PASS"
|
|
echo "========================================="
|
|
sync
|
|
echo o > /proc/sysrq-trigger
|
|
sleep 10
|
|
}
|
|
|
|
fail() {
|
|
local msg="${1:-unknown error}"
|
|
echo ""
|
|
echo "========================================="
|
|
echo "REDIR_TEST_FAIL: $msg"
|
|
echo "========================================="
|
|
sync
|
|
echo o > /proc/sysrq-trigger
|
|
sleep 10
|
|
}
|
|
|
|
# --- mount filesystems ------------------------------------------------------
|
|
|
|
mount -t proc proc /proc
|
|
mount -t sysfs sysfs /sys
|
|
mount -t devtmpfs devtmpfs /dev
|
|
mkdir -p /dev/pts /tmp /run
|
|
mount -t devpts devpts /dev/pts
|
|
mount -t tmpfs tmpfs /tmp
|
|
mount -t tmpfs tmpfs /run
|
|
|
|
# Enable sysrq for clean poweroff
|
|
echo 1 > /proc/sys/kernel/sysrq
|
|
|
|
echo "=== Guest init starting ==="
|
|
|
|
# --- load kernel modules ----------------------------------------------------
|
|
|
|
for mod in \
|
|
virtio_net \
|
|
ip_tables iptable_nat iptable_filter \
|
|
nf_nat nf_conntrack \
|
|
xt_REDIRECT xt_set xt_tcpudp \
|
|
ip_set ip_set_hash_net; do
|
|
modprobe "$mod" 2>/dev/null || echo "WARN: failed to load $mod (may be built-in)"
|
|
done
|
|
|
|
echo "=== Kernel modules loaded ==="
|
|
|
|
# --- configure network ------------------------------------------------------
|
|
|
|
# Find the non-lo network interface
|
|
IFACE=""
|
|
for f in /sys/class/net/*/type; do
|
|
iface=$(basename "$(dirname "$f")")
|
|
if [ "$iface" != "lo" ]; then
|
|
IFACE="$iface"
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ -z "$IFACE" ]; then
|
|
fail "no network interface found"
|
|
fi
|
|
|
|
echo "=== Using network interface: $IFACE ==="
|
|
|
|
ip link set lo up
|
|
ip link set "$IFACE" up
|
|
ip addr add 10.0.2.15/24 dev "$IFACE"
|
|
ip route add default via 10.0.2.2
|
|
|
|
# DNS (QEMU user-mode DNS forwarder)
|
|
echo "nameserver 10.0.2.3" > /etc/resolv.conf
|
|
|
|
# Wait for link to come up
|
|
sleep 2
|
|
|
|
# Connectivity check
|
|
echo "=== Checking connectivity to host ==="
|
|
if ! ping -c 2 -W 5 10.0.2.2; then
|
|
fail "cannot reach host at 10.0.2.2"
|
|
fi
|
|
|
|
echo "=== Host reachable ==="
|
|
|
|
# --- start ss-redir ---------------------------------------------------------
|
|
|
|
echo "=== Starting ss-redir ==="
|
|
/usr/bin/ss-redir -c /etc/ss-redir.json -v &
|
|
SS_REDIR_PID=$!
|
|
sleep 3
|
|
|
|
if ! kill -0 "$SS_REDIR_PID" 2>/dev/null; then
|
|
fail "ss-redir died immediately"
|
|
fi
|
|
|
|
echo "=== ss-redir running (PID $SS_REDIR_PID) ==="
|
|
|
|
# --- run ss-nat (set up iptables REDIRECT) ----------------------------------
|
|
|
|
echo "=== Running ss-nat ==="
|
|
if ! /usr/bin/ss-nat -s 10.0.2.2 -l 1080 -o; then
|
|
fail "ss-nat failed"
|
|
fi
|
|
|
|
echo "=== ss-nat rules applied ==="
|
|
|
|
# Show iptables state for debugging
|
|
echo "=== iptables -t nat -L -n ==="
|
|
iptables -t nat -L -n 2>&1 || true
|
|
echo "=== ipset list ==="
|
|
ipset list 2>&1 || true
|
|
|
|
# --- test with curl ---------------------------------------------------------
|
|
|
|
echo "=== Testing transparent proxy with curl ==="
|
|
|
|
# Try multiple URLs for reliability (captive portal detection endpoints
|
|
# are lightweight and highly available)
|
|
|
|
# Test 1: Mozilla captive portal
|
|
echo "--- Test 1: detectportal.firefox.com ---"
|
|
BODY=$(curl -s --connect-timeout 15 -m 30 http://detectportal.firefox.com/success.txt 2>&1) || true
|
|
echo "Response: $BODY"
|
|
if echo "$BODY" | grep -q "success"; then
|
|
echo "Test 1 PASSED"
|
|
pass
|
|
fi
|
|
|
|
# Test 2: Google (expect redirect or 200)
|
|
echo "--- Test 2: www.google.com ---"
|
|
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 15 -m 30 http://www.google.com/ 2>&1) || true
|
|
echo "HTTP code: $HTTP_CODE"
|
|
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "301" ] || [ "$HTTP_CODE" = "302" ]; then
|
|
echo "Test 2 PASSED"
|
|
pass
|
|
fi
|
|
|
|
# Test 3: Apple captive portal
|
|
echo "--- Test 3: captive.apple.com ---"
|
|
BODY=$(curl -s --connect-timeout 15 -m 30 http://captive.apple.com/ 2>&1) || true
|
|
echo "Response: $BODY"
|
|
if echo "$BODY" | grep -qi "success"; then
|
|
echo "Test 3 PASSED"
|
|
pass
|
|
fi
|
|
|
|
# If we get here, all tests failed
|
|
fail "all curl tests failed"
|