Files
harbor-net/tools/test-singbox-client-rc.sh

124 lines
4.4 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
readonly RC_VERSION='1.14.0-rc.5'
readonly IMAGE="harbor-singbox-client-rc-test:${RC_VERSION}"
FIXTURES="$(mktemp -d)"
SUFFIX="${FIXTURES##*/}"
readonly SUFFIX="${SUFFIX//[^[:alnum:]]/}"
readonly NETWORK="harbor-singbox-rc-${SUFFIX}"
readonly TARGET="harbor-singbox-rc-target-${SUFFIX}"
readonly PROXY="harbor-singbox-rc-proxy-${SUFFIX}"
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
NETWORK_CREATED=false
TARGET_CREATED=false
PROXY_CREATED=false
cleanup() {
[[ "$PROXY_CREATED" == true ]] && docker rm -f "$PROXY" >/dev/null 2>&1 || true
[[ "$TARGET_CREATED" == true ]] && docker rm -f "$TARGET" >/dev/null 2>&1 || true
[[ "$NETWORK_CREATED" == true ]] && docker network rm "$NETWORK" >/dev/null 2>&1 || true
docker image rm "$IMAGE" >/dev/null 2>&1 || true
rm -rf "$FIXTURES"
}
trap cleanup EXIT
check_config() {
local name="$1"
local output
local unexpected
if ! output="$(docker run --rm \
-v "$FIXTURES:/fixtures:ro" \
--entrypoint sing-box \
"$IMAGE" check -c "/fixtures/${name}.json" 2>&1)"; then
printf '%s\n' "$output" >&2
return 1
fi
unexpected="$(printf '%s\n' "$output" \
| grep -Ei 'warn|deprecated' \
| grep -Evi 'independent_cache.*DNS option is deprecated' || true)"
if [[ -n "$unexpected" ]]; then
printf 'unexpected warning for %s:\n%s\n' "$name" "$unexpected" >&2
return 1
fi
printf 'PASS config %s\n' "$name"
}
docker build \
--build-arg "SINGBOX_VERSION=${RC_VERSION}" \
-t "$IMAGE" \
-f "$ROOT/Dockerfile.client" \
"$ROOT"
version_output="$(docker run --rm --entrypoint sing-box "$IMAGE" version)"
printf '%s\n' "$version_output"
grep -Fxq "sing-box version ${RC_VERSION}" <<< "$version_output"
cat > "$FIXTURES/generate-configs.mjs" <<'EOF'
import fs from 'node:fs';
import { parseSubscriptionBody } from '/app/dist/server/subscription.js';
import { buildGatewayConfig } from '/app/dist/server/singbox.js';
const reality = 'vless://00000000-0000-4000-8000-000000000001@reality.example.test:443?security=reality&type=tcp&pbk=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&sid=0123456789abcdef&sni=cover.example.test&fp=chrome#Reality';
const websocket = 'vless://00000000-0000-4000-8000-000000000002@ws.example.test:8443?encryption=none&security=tls&sni=edge.example.test&fp=firefox&alpn=h2%2Chttp%2F1.1&type=ws&host=edge.example.test&path=%2Fsocket%3Fed%3D2048#TLS%20WS';
function generated(link, clientDirect = false) {
const parsed = parseSubscriptionBody(Buffer.from(link).toString('base64'));
return buildGatewayConfig(parsed.config, parsed.servers[0].id, { clientDirect });
}
for (const [name, config] of [
['local-vpn', generated(websocket)],
['gateway-direct', generated(websocket, true)],
['vless-reality', generated(reality)],
['vless-tls-websocket', generated(websocket)],
]) {
fs.writeFileSync(`/fixtures/${name}.json`, JSON.stringify(config));
}
EOF
docker run --rm \
-e APP_MODE=client \
-e PROXY_PORT=18081 \
-e DIAGNOSTICS_PROXY_PORT=18082 \
-e DATA_DIR=/tmp/harbor-rc \
-e SING_BOX_CACHE=/tmp/harbor-rc-cache.db \
-v "$FIXTURES:/fixtures" \
--entrypoint node \
"$IMAGE" /fixtures/generate-configs.mjs
check_config local-vpn
check_config gateway-direct
check_config vless-reality
check_config vless-tls-websocket
docker network create "$NETWORK" >/dev/null
NETWORK_CREATED=true
docker create --name "$TARGET" --network "$NETWORK" --entrypoint node "$IMAGE" \
-e 'require("node:http").createServer((request,response)=>response.end(request.url)).listen(18080,"0.0.0.0")' >/dev/null
TARGET_CREATED=true
docker start "$TARGET" >/dev/null
docker create --name "$PROXY" --network "$NETWORK" \
-v "$FIXTURES:/fixtures:ro" --entrypoint sing-box "$IMAGE" run -c /fixtures/gateway-direct.json >/dev/null
PROXY_CREATED=true
docker start "$PROXY" >/dev/null
for _ in {1..30}; do
if docker run --rm --network "$NETWORK" --entrypoint curl "$IMAGE" \
--noproxy '' -fsS -x "http://${PROXY}:18081" "http://${TARGET}:18080/http" | grep -Fxq '/http'; then
break
fi
sleep 0.2
done
http_body="$(docker run --rm --network "$NETWORK" --entrypoint curl "$IMAGE" \
--noproxy '' -fsS -x "http://${PROXY}:18081" "http://${TARGET}:18080/http")"
socks_body="$(docker run --rm --network "$NETWORK" --entrypoint curl "$IMAGE" \
--noproxy '' -fsS --socks5-hostname "${PROXY}:18081" "http://${TARGET}:18080/socks")"
[[ "$http_body" == '/http' ]]
[[ "$socks_body" == '/socks' ]]
printf 'PASS mixed inbound HTTP\nPASS mixed inbound SOCKS5\n'