feat(v1): Gecko-обфускация, latest-stable Hysteria на сборке и forward-compatible admin
Сквозная миграция HY2XS на современную Hysteria (2.12.2) и переход на v1. Build: - версия Hysteria резолвится на этапе сборки из HyNetworks/hysteria и замораживается в metadata пакета (version + immutable url + sha256); - compatibility gate: реальный бинарник должен принять канонический конфиг HY2XS для gecko и salamander до создания пакета; - сборка прогоняет тесты оркестратора и админки. Конфигурационный контракт: - HY2XS_CONFIG_SCHEMA_VERSION=2, чужая схема отклоняется fail-fast; - obfs стал настоящим union gecko|salamander, gecko — default; - obfs-блок рендерится оркестратором целиком, два подтипа одновременно структурно невозможны; - современный baseline: congestion bbr/standard, disableLossCompensation=false, disableStatelessReset=false, полный quic-блок. Исправления: - share URI для gecko: генератор был завязан на Obfs.Salamander.Password и выдавал нерабочую ссылку при любой другой обфускации; - SNI брался только из ACME-блока и уходил пустым при HY2XS_TLS_MODE=file; - экспорт конфига выносил trafficStats.secret, access_token и obfs-пароль; - экспорт терял неизвестные upstream-поля при round-trip через типизированную модель; - renderRuntimeEnv печатал тип обфускации литералом, расходясь с конфигом; - namedotcom удалён из ACME-реестра (нет в Hysteria с 2.11.0). Тесты: - 95 тестов оркестратора: env, рендер, семантика профиля, резолвер, rollover; - тесты URI и экспорта в Go; - tools/test/e2e-hysteria.sh с реальным клиентом Hysteria. UX: - подсказки и примеры в форме создания пира. Прочее: CHANGELOG.md, .gitattributes (LF для target-side файлов), документация на русском.
This commit is contained in:
Executable
+527
@@ -0,0 +1,527 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# HY2XS end-to-end проверка связки «сервер + реальный клиент Hysteria».
|
||||
#
|
||||
# Что проверяется:
|
||||
# 1. сервер принимает сгенерированный HY2XS конфиг и стартует;
|
||||
# 2. TLS handshake;
|
||||
# 3. handshake с обфускацией (gecko и salamander);
|
||||
# 4. HTTP auth HY2XS: допуск разрешённого пира и отказ неразрешённому;
|
||||
# 5. клиент подключается ИМЕННО по сгенерированному hysteria2:// URI;
|
||||
# 6. TCP forwarding через туннель;
|
||||
# 7. UDP forwarding через туннель;
|
||||
# 8. trafficStats API: валидный и невалидный secret;
|
||||
# 9. per-peer accounting;
|
||||
# 10. перезапуск сервера и быстрое переподключение клиента.
|
||||
#
|
||||
# Скрипт самодостаточен: поднимает mock HY2XS auth endpoint, поэтому не требует
|
||||
# установленной админки и базы. Рассчитан на Debian 13 amd64.
|
||||
#
|
||||
# ./tools/test/e2e-hysteria.sh
|
||||
# HYSTERIA_BIN=/tmp/hysteria ./tools/test/e2e-hysteria.sh --obfs salamander
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
OBFS_TYPES=()
|
||||
HYSTERIA_BIN="${HYSTERIA_BIN:-/usr/local/bin/hysteria}"
|
||||
BUN_BIN="${BUN_BIN:-bun}"
|
||||
|
||||
SERVER_PORT="${E2E_SERVER_PORT:-34643}"
|
||||
STATS_PORT="${E2E_STATS_PORT:-34644}"
|
||||
AUTH_PORT="${E2E_AUTH_PORT:-34645}"
|
||||
ORIGIN_TCP_PORT="${E2E_ORIGIN_TCP_PORT:-34646}"
|
||||
ORIGIN_UDP_PORT="${E2E_ORIGIN_UDP_PORT:-34647}"
|
||||
FORWARD_TCP_PORT="${E2E_FORWARD_TCP_PORT:-34648}"
|
||||
FORWARD_UDP_PORT="${E2E_FORWARD_UDP_PORT:-34649}"
|
||||
|
||||
VALID_SECRET="hy2xs-e2e-valid-peer-secret"
|
||||
INVALID_SECRET="hy2xs-e2e-invalid-peer-secret"
|
||||
STATS_SECRET="hy2xs-e2e-traffic-secret"
|
||||
OBFS_PASSWORD="hy2xs-e2e-obfs-password"
|
||||
SNI_NAME="hy2xs-e2e.local"
|
||||
|
||||
WORK_DIR=""
|
||||
SERVER_PID=""
|
||||
CLIENT_PID=""
|
||||
AUTH_PID=""
|
||||
ORIGIN_PID=""
|
||||
|
||||
log() { printf '[hy2xs-e2e] %s\n' "$*"; }
|
||||
step() { printf '\n[hy2xs-e2e] ==> %s\n' "$*"; }
|
||||
pass() { printf '[hy2xs-e2e] ok: %s\n' "$*"; }
|
||||
die() { printf '[hy2xs-e2e] FAIL: %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
usage() {
|
||||
cat >&2 <<'EOF'
|
||||
Usage: tools/test/e2e-hysteria.sh [--obfs gecko|salamander|all]
|
||||
|
||||
Environment:
|
||||
HYSTERIA_BIN path to the Hysteria binary under test (default /usr/local/bin/hysteria)
|
||||
BUN_BIN path to bun (default: bun from PATH)
|
||||
EOF
|
||||
exit 2
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
local requested="all"
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--obfs)
|
||||
requested="${2:-}"
|
||||
shift 2 || true
|
||||
;;
|
||||
-h|--help) usage ;;
|
||||
*) die "unknown argument: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "$requested" in
|
||||
all) OBFS_TYPES=(gecko salamander) ;;
|
||||
gecko) OBFS_TYPES=(gecko) ;;
|
||||
salamander) OBFS_TYPES=(salamander) ;;
|
||||
*) die "unsupported --obfs value: $requested" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
require_tool() {
|
||||
command -v "$1" >/dev/null 2>&1 || die "required tool not found: $1"
|
||||
}
|
||||
|
||||
preflight() {
|
||||
[ -x "$HYSTERIA_BIN" ] || die "Hysteria binary not found or not executable: $HYSTERIA_BIN (set HYSTERIA_BIN)"
|
||||
require_tool openssl
|
||||
require_tool curl
|
||||
require_tool ss
|
||||
command -v "$BUN_BIN" >/dev/null 2>&1 || die "bun not found (set BUN_BIN)"
|
||||
|
||||
local port
|
||||
for port in "$SERVER_PORT" "$STATS_PORT" "$AUTH_PORT" \
|
||||
"$ORIGIN_TCP_PORT" "$ORIGIN_UDP_PORT" "$FORWARD_TCP_PORT" "$FORWARD_UDP_PORT"; do
|
||||
if ss -H -lantu 2>/dev/null | awk '{print $5}' | grep -Eq "[:.]${port}\$"; then
|
||||
die "port $port is already in use"
|
||||
fi
|
||||
done
|
||||
|
||||
log "Hysteria under test: $("$HYSTERIA_BIN" version 2>/dev/null | grep -Eo 'v[0-9]+\.[0-9]+\.[0-9]+' | head -n1)"
|
||||
}
|
||||
|
||||
kill_pid() {
|
||||
local name="$1"
|
||||
local pid="${!name}"
|
||||
[ -n "$pid" ] || return 0
|
||||
kill "$pid" 2>/dev/null || true
|
||||
wait "$pid" 2>/dev/null || true
|
||||
printf -v "$name" '%s' ""
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
kill_pid CLIENT_PID
|
||||
kill_pid SERVER_PID
|
||||
kill_pid ORIGIN_PID
|
||||
kill_pid AUTH_PID
|
||||
[ -n "$WORK_DIR" ] && rm -rf "$WORK_DIR" || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
wait_for_port() {
|
||||
local port="$1" proto_flag="$2" label="$3" attempts="${4:-30}"
|
||||
local i=0
|
||||
while [ "$i" -lt "$attempts" ]; do
|
||||
if ss -H "$proto_flag" 2>/dev/null | awk '{print $4}' | grep -Eq "[:.]${port}\$"; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
die "$label did not start listening on port $port"
|
||||
}
|
||||
|
||||
# Mock HY2XS auth endpoint повторяет контракт /hui/hysteria2/auth:
|
||||
# проверку machine access_token и отказ неразрешённому секрету.
|
||||
start_auth_endpoint() {
|
||||
cat >"$WORK_DIR/auth-server.ts" <<EOF
|
||||
const validSecret = ${VALID_SECRET@Q};
|
||||
const machineToken = ${STATS_SECRET@Q};
|
||||
|
||||
Bun.serve({
|
||||
hostname: "127.0.0.1",
|
||||
port: ${AUTH_PORT},
|
||||
async fetch(request) {
|
||||
const url = new URL(request.url);
|
||||
if (url.searchParams.get("access_token") !== machineToken) {
|
||||
return new Response("forbidden", { status: 403 });
|
||||
}
|
||||
const body = await request.json().catch(() => null);
|
||||
if (!body || typeof body.auth !== "string" || typeof body.tx !== "number") {
|
||||
return Response.json({ ok: false }, { status: 400 });
|
||||
}
|
||||
if (body.auth !== validSecret) {
|
||||
return Response.json({ ok: false });
|
||||
}
|
||||
return Response.json({ ok: true, id: "e2e-peer" });
|
||||
}
|
||||
});
|
||||
EOF
|
||||
|
||||
"$BUN_BIN" run "$WORK_DIR/auth-server.ts" >"$WORK_DIR/auth.log" 2>&1 &
|
||||
AUTH_PID=$!
|
||||
wait_for_port "$AUTH_PORT" "-ltn" "mock auth endpoint"
|
||||
pass "mock HY2XS auth endpoint is up"
|
||||
}
|
||||
|
||||
# TCP- и UDP-origin: отвечают фиксированной строкой, чтобы можно было
|
||||
# однозначно отличить рабочий туннель от неответа.
|
||||
start_origins() {
|
||||
cat >"$WORK_DIR/origins.ts" <<EOF
|
||||
Bun.listen({
|
||||
hostname: "127.0.0.1",
|
||||
port: ${ORIGIN_TCP_PORT},
|
||||
socket: {
|
||||
open(socket) {
|
||||
socket.write("HY2XS-TCP-OK\n");
|
||||
socket.flush();
|
||||
socket.end();
|
||||
},
|
||||
data() {},
|
||||
error() {}
|
||||
}
|
||||
});
|
||||
|
||||
await Bun.udpSocket({
|
||||
hostname: "127.0.0.1",
|
||||
port: ${ORIGIN_UDP_PORT},
|
||||
socket: {
|
||||
data(socket, _buf, port, address) {
|
||||
socket.send("HY2XS-UDP-OK", port, address);
|
||||
}
|
||||
}
|
||||
});
|
||||
EOF
|
||||
|
||||
"$BUN_BIN" run "$WORK_DIR/origins.ts" >"$WORK_DIR/origins.log" 2>&1 &
|
||||
ORIGIN_PID=$!
|
||||
wait_for_port "$ORIGIN_TCP_PORT" "-ltn" "tcp origin"
|
||||
wait_for_port "$ORIGIN_UDP_PORT" "-lun" "udp origin"
|
||||
pass "TCP and UDP origins are up"
|
||||
}
|
||||
|
||||
render_server_config() {
|
||||
local obfs_type="$1"
|
||||
|
||||
(
|
||||
cd orchestrator
|
||||
"$BUN_BIN" run tools/render-canonical-config.ts \
|
||||
--package-dir ../package \
|
||||
--obfs "$obfs_type" \
|
||||
--tls-mode file \
|
||||
--cert "$WORK_DIR/server.crt" \
|
||||
--key "$WORK_DIR/server.key" \
|
||||
--port "$SERVER_PORT" \
|
||||
--traffic-stats-port "$STATS_PORT" \
|
||||
--out "$WORK_DIR/server.yaml"
|
||||
) || die "could not render canonical server config for obfs=$obfs_type"
|
||||
|
||||
# Подставляем e2e-секреты и локальный auth endpoint, не трогая структуру
|
||||
# конфига: проверяется тот же YAML, который получает production.
|
||||
"$BUN_BIN" -e '
|
||||
const [path, obfs, stats, authPort] = process.argv.slice(1);
|
||||
let text = require("node:fs").readFileSync(path, "utf8");
|
||||
text = text.split("hy2xs-compat-gate-obfs-password").join(obfs);
|
||||
text = text.split("hy2xs-compat-gate-traffic-secret").join(stats);
|
||||
text = text.replace(/url: http:\/\/127\.0\.0\.1:\d+\//, `url: http://127.0.0.1:${authPort}/`);
|
||||
require("node:fs").writeFileSync(path, text);
|
||||
' "$WORK_DIR/server.yaml" "$OBFS_PASSWORD" "$STATS_SECRET" "$AUTH_PORT" \
|
||||
|| die "could not inject e2e secrets into the server config"
|
||||
|
||||
grep -q "type: $obfs_type" "$WORK_DIR/server.yaml" \
|
||||
|| die "rendered server config does not use obfs type $obfs_type"
|
||||
grep -q "127.0.0.1:${AUTH_PORT}/hui/hysteria2/auth?access_token=" "$WORK_DIR/server.yaml" \
|
||||
|| die "server config lost the HY2XS machine auth token"
|
||||
}
|
||||
|
||||
start_server() {
|
||||
HYSTERIA_DISABLE_UPDATE_CHECK=1 "$HYSTERIA_BIN" server -c "$WORK_DIR/server.yaml" \
|
||||
>>"$WORK_DIR/server.log" 2>&1 &
|
||||
SERVER_PID=$!
|
||||
|
||||
local i=0
|
||||
while [ "$i" -lt 20 ]; do
|
||||
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
|
||||
log "--- server log ---"
|
||||
cat "$WORK_DIR/server.log" >&2 || true
|
||||
die "server exited while starting"
|
||||
fi
|
||||
if ss -H -lun 2>/dev/null | awk '{print $4}' | grep -Eq "[:.]${SERVER_PORT}\$"; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
log "--- server log ---"
|
||||
cat "$WORK_DIR/server.log" >&2 || true
|
||||
die "server did not start listening on UDP $SERVER_PORT"
|
||||
}
|
||||
|
||||
urlencode() {
|
||||
"$BUN_BIN" -e 'process.stdout.write(encodeURIComponent(process.argv[1]))' "$1"
|
||||
}
|
||||
|
||||
# Ссылка строится ровно по официальной URI-схеме, как её генерирует админка.
|
||||
build_share_uri() {
|
||||
local obfs_type="$1"
|
||||
local secret="$2"
|
||||
|
||||
printf 'hysteria2://%s@127.0.0.1:%s/?obfs=%s&obfs-password=%s&sni=%s&insecure=1' \
|
||||
"$(urlencode "$secret")" "$SERVER_PORT" "$obfs_type" "$(urlencode "$OBFS_PASSWORD")" "$SNI_NAME"
|
||||
}
|
||||
|
||||
# Клиент конфигурируется ИЗ share URI, а не собирается независимо: именно это
|
||||
# ловит расхождение между генератором ссылок и реальным сервером.
|
||||
write_client_config_from_uri() {
|
||||
cat >"$WORK_DIR/client.yaml" <<EOF
|
||||
server: $1
|
||||
|
||||
tcpForwarding:
|
||||
- listen: 127.0.0.1:${FORWARD_TCP_PORT}
|
||||
remote: 127.0.0.1:${ORIGIN_TCP_PORT}
|
||||
|
||||
udpForwarding:
|
||||
- listen: 127.0.0.1:${FORWARD_UDP_PORT}
|
||||
remote: 127.0.0.1:${ORIGIN_UDP_PORT}
|
||||
timeout: 20s
|
||||
EOF
|
||||
}
|
||||
|
||||
start_client() {
|
||||
HYSTERIA_DISABLE_UPDATE_CHECK=1 "$HYSTERIA_BIN" client -c "$WORK_DIR/client.yaml" \
|
||||
>>"$WORK_DIR/client.log" 2>&1 &
|
||||
CLIENT_PID=$!
|
||||
|
||||
local i=0
|
||||
while [ "$i" -lt 25 ]; do
|
||||
if ! kill -0 "$CLIENT_PID" 2>/dev/null; then
|
||||
log "--- client log ---"
|
||||
cat "$WORK_DIR/client.log" >&2 || true
|
||||
die "client exited while connecting via the generated share URI"
|
||||
fi
|
||||
if ss -H -ltn 2>/dev/null | awk '{print $4}' | grep -Eq "[:.]${FORWARD_TCP_PORT}\$"; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
log "--- client log ---"
|
||||
cat "$WORK_DIR/client.log" >&2 || true
|
||||
die "client did not open its forwarding listeners"
|
||||
}
|
||||
|
||||
probe_tcp() {
|
||||
"$BUN_BIN" -e '
|
||||
const port = Number(process.argv[1]);
|
||||
const chunks: string[] = [];
|
||||
const socket = await Bun.connect({
|
||||
hostname: "127.0.0.1",
|
||||
port,
|
||||
socket: {
|
||||
data(_s, chunk) { chunks.push(new TextDecoder().decode(chunk)); },
|
||||
error() {}
|
||||
}
|
||||
});
|
||||
await Bun.sleep(Number(process.argv[2]));
|
||||
socket.end();
|
||||
process.stdout.write(chunks.join(""));
|
||||
' "$FORWARD_TCP_PORT" "3000" 2>/dev/null || true
|
||||
}
|
||||
|
||||
probe_udp() {
|
||||
"$BUN_BIN" -e '
|
||||
const port = Number(process.argv[1]);
|
||||
const answer = await new Promise<string>(async (resolve) => {
|
||||
const timer = setTimeout(() => resolve(""), Number(process.argv[2]));
|
||||
const socket = await Bun.udpSocket({
|
||||
hostname: "127.0.0.1",
|
||||
port: 0,
|
||||
socket: {
|
||||
data(_s, buf) {
|
||||
clearTimeout(timer);
|
||||
resolve(new TextDecoder().decode(buf));
|
||||
}
|
||||
}
|
||||
});
|
||||
socket.send("ping", port, "127.0.0.1");
|
||||
});
|
||||
process.stdout.write(answer);
|
||||
' "$FORWARD_UDP_PORT" "5000" 2>/dev/null || true
|
||||
}
|
||||
|
||||
assert_tcp_forwarding() {
|
||||
local i=0
|
||||
while [ "$i" -lt 10 ]; do
|
||||
case "$(probe_tcp)" in
|
||||
*HY2XS-TCP-OK*)
|
||||
pass "TCP forwarding works through the tunnel"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
die "TCP forwarding through the tunnel failed"
|
||||
}
|
||||
|
||||
assert_udp_forwarding() {
|
||||
local i=0
|
||||
while [ "$i" -lt 10 ]; do
|
||||
case "$(probe_udp)" in
|
||||
*HY2XS-UDP-OK*)
|
||||
pass "UDP forwarding works through the tunnel"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
die "UDP forwarding through the tunnel failed"
|
||||
}
|
||||
|
||||
assert_traffic_stats() {
|
||||
local code
|
||||
code="$(curl --silent --max-time 5 -o "$WORK_DIR/online.json" -w '%{http_code}' \
|
||||
-H "Authorization: ${STATS_SECRET}" "http://127.0.0.1:${STATS_PORT}/online" || true)"
|
||||
[ "$code" = "200" ] || die "trafficStats /online returned $code for a valid secret"
|
||||
|
||||
local denied
|
||||
denied="$(curl --silent --max-time 5 -o /dev/null -w '%{http_code}' \
|
||||
-H "Authorization: definitely-not-the-secret" "http://127.0.0.1:${STATS_PORT}/online" || true)"
|
||||
case "$denied" in
|
||||
401|403) ;;
|
||||
*) die "trafficStats /online returned $denied for an invalid secret, expected 401/403" ;;
|
||||
esac
|
||||
|
||||
local traffic_code
|
||||
traffic_code="$(curl --silent --max-time 5 -o "$WORK_DIR/traffic.json" -w '%{http_code}' \
|
||||
-H "Authorization: ${STATS_SECRET}" "http://127.0.0.1:${STATS_PORT}/traffic" || true)"
|
||||
[ "$traffic_code" = "200" ] || die "trafficStats /traffic returned $traffic_code"
|
||||
|
||||
grep -q 'e2e-peer' "$WORK_DIR/traffic.json" \
|
||||
|| die "per-peer accounting does not contain the authenticated peer: $(cat "$WORK_DIR/traffic.json")"
|
||||
|
||||
pass "trafficStats API and per-peer accounting work"
|
||||
}
|
||||
|
||||
assert_reconnect_after_restart() {
|
||||
kill_pid SERVER_PID
|
||||
start_server
|
||||
|
||||
local i=0
|
||||
while [ "$i" -lt 25 ]; do
|
||||
case "$(probe_tcp)" in
|
||||
*HY2XS-TCP-OK*)
|
||||
pass "client recovered the tunnel after a server restart (${i}s)"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
die "client did not recover the tunnel after a server restart"
|
||||
}
|
||||
|
||||
assert_auth_rejects_unknown_peer() {
|
||||
local obfs_type="$1"
|
||||
|
||||
kill_pid CLIENT_PID
|
||||
write_client_config_from_uri "$(build_share_uri "$obfs_type" "$INVALID_SECRET")"
|
||||
|
||||
HYSTERIA_DISABLE_UPDATE_CHECK=1 "$HYSTERIA_BIN" client -c "$WORK_DIR/client.yaml" \
|
||||
>"$WORK_DIR/client-denied.log" 2>&1 &
|
||||
CLIENT_PID=$!
|
||||
|
||||
# Клиент либо завершается, либо не может пробросить трафик.
|
||||
local i=0
|
||||
while [ "$i" -lt 15 ]; do
|
||||
if ! kill -0 "$CLIENT_PID" 2>/dev/null; then
|
||||
kill_pid CLIENT_PID
|
||||
pass "unauthorized peer is rejected by HY2XS HTTP auth (client exited)"
|
||||
return 0
|
||||
fi
|
||||
if grep -Eqi 'auth|denied|reject|unauthor' "$WORK_DIR/client-denied.log"; then
|
||||
kill_pid CLIENT_PID
|
||||
pass "unauthorized peer is rejected by HY2XS HTTP auth (auth failure reported)"
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
local leaked
|
||||
leaked="$(probe_tcp)"
|
||||
kill_pid CLIENT_PID
|
||||
case "$leaked" in
|
||||
*HY2XS-TCP-OK*)
|
||||
die "server accepted a peer that HY2XS auth must reject"
|
||||
;;
|
||||
esac
|
||||
pass "unauthorized peer cannot pass traffic"
|
||||
}
|
||||
|
||||
run_profile() {
|
||||
local obfs_type="$1"
|
||||
|
||||
step "profile: obfs=$obfs_type"
|
||||
: >"$WORK_DIR/server.log"
|
||||
: >"$WORK_DIR/client.log"
|
||||
|
||||
render_server_config "$obfs_type"
|
||||
|
||||
start_server
|
||||
pass "server accepted the HY2XS config and started (TLS + $obfs_type)"
|
||||
|
||||
local uri
|
||||
uri="$(build_share_uri "$obfs_type" "$VALID_SECRET")"
|
||||
log "share URI under test: ${uri//$OBFS_PASSWORD/<obfs-password>}"
|
||||
write_client_config_from_uri "$uri"
|
||||
|
||||
start_client
|
||||
pass "client connected using the generated hysteria2:// URI (TLS + $obfs_type handshake)"
|
||||
|
||||
assert_tcp_forwarding
|
||||
assert_udp_forwarding
|
||||
assert_traffic_stats
|
||||
assert_reconnect_after_restart
|
||||
assert_auth_rejects_unknown_peer "$obfs_type"
|
||||
|
||||
kill_pid CLIENT_PID
|
||||
kill_pid SERVER_PID
|
||||
pass "profile obfs=$obfs_type passed"
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_args "$@"
|
||||
preflight
|
||||
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
log "work dir: $WORK_DIR"
|
||||
|
||||
openssl req -x509 -newkey rsa:2048 -nodes -days 1 \
|
||||
-subj "/CN=${SNI_NAME}" \
|
||||
-addext "subjectAltName=DNS:${SNI_NAME}" \
|
||||
-keyout "$WORK_DIR/server.key" -out "$WORK_DIR/server.crt" >/dev/null 2>&1 \
|
||||
|| die "could not generate the e2e certificate"
|
||||
|
||||
start_auth_endpoint
|
||||
start_origins
|
||||
|
||||
local obfs_type
|
||||
for obfs_type in "${OBFS_TYPES[@]}"; do
|
||||
run_profile "$obfs_type"
|
||||
done
|
||||
|
||||
step "all HY2XS end-to-end checks passed"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user