Add all project files, configs, scripts and results
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
venv/
|
||||
1427
XRAY_ANTI_DPI_CONFIGURATIONS.md
Normal file
1427
XRAY_ANTI_DPI_CONFIGURATIONS.md
Normal file
File diff suppressed because it is too large
Load Diff
459
benchmark_improvements.sh
Executable file
459
benchmark_improvements.sh
Executable file
@@ -0,0 +1,459 @@
|
||||
#!/usr/bin/env bash
|
||||
# benchmark_improvements.sh
|
||||
# Tests each DPI-resistance improvement independently against baseline.
|
||||
# Metrics: avg/P95 latency, jitter (std dev), download Mbps, upload Mbps.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
XRAY_BIN="/usr/local/x-ui/bin/xray-linux-amd64"
|
||||
SOCKS_PORT=11083
|
||||
REMOTE_IP="83.99.190.32"
|
||||
VENV="/home/alvis/ai-xray/venv"
|
||||
RESULTS_FILE="/home/alvis/ai-xray/improvement_results.md"
|
||||
LATENCY_SAMPLES=20
|
||||
|
||||
# Current config values
|
||||
PUBLIC_KEY="58Iqd6LuWXgvjAgo92-7KURhTp0Vj79yGF81l_iuvTw"
|
||||
PRIVATE_KEY="KJfhenZvJV1kXwv4kDC8NPBtMUY0RR8lFrxsxfXfFmY"
|
||||
UUID_B="6e422ab5-070a-43f6-8241-38cd56d23d24"
|
||||
SID_B="6036d37d12c443c4"
|
||||
XHTTP_PATH="/xt-6036d37d"
|
||||
DEST="www.delfi.lv:443"
|
||||
SNI="www.delfi.lv"
|
||||
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
|
||||
hdr() { echo ""; echo -e "${BOLD}${CYAN}══ $1 ══${NC}"; }
|
||||
pass() { echo -e " ${GREEN}✓${NC} $1"; }
|
||||
fail() { echo -e " ${RED}✗${NC} $1"; }
|
||||
info() { echo -e " ${YELLOW}→${NC} $1"; }
|
||||
|
||||
XRAY_PID=""
|
||||
cleanup() {
|
||||
if [[ -n "$XRAY_PID" ]]; then
|
||||
kill "$XRAY_PID" 2>/dev/null || true
|
||||
wait "$XRAY_PID" 2>/dev/null || true
|
||||
XRAY_PID=""
|
||||
fi
|
||||
}
|
||||
trap 'cleanup; rm -f /tmp/bench-cfg.json' EXIT
|
||||
|
||||
# ── Remote inbound updater ─────────────────────────────────────────────────
|
||||
remote_update() {
|
||||
local stream_json="$1"
|
||||
local uuid="$2"
|
||||
source "$VENV/bin/activate"
|
||||
python3 << PYEOF
|
||||
import requests, json
|
||||
import urllib3; urllib3.disable_warnings()
|
||||
s = requests.Session(); s.verify = False
|
||||
BASE = "https://share.alogins.net:16627/gBdsRLtVZdgZ63wmVR"
|
||||
s.post(f"{BASE}/login", data={"username": "xrayadmin", "password": "Admin2026!"})
|
||||
stream = $stream_json
|
||||
client = {"id": "$uuid", "flow": "", "email": "bench",
|
||||
"limitIp": 0, "totalGB": 0, "expiryTime": 0,
|
||||
"enable": True, "tgId": "", "subId": "", "comment": ""}
|
||||
payload = {"id": 1, "tag": "inbound-443", "enable": True, "port": 443,
|
||||
"listen": "", "protocol": "vless",
|
||||
"settings": json.dumps({"clients": [client], "decryption": "none", "fallbacks": []}),
|
||||
"streamSettings": json.dumps(stream),
|
||||
"sniffing": json.dumps({"enabled": False}),
|
||||
"remark": "inbound-443", "expiryTime": 0}
|
||||
r = s.post(f"{BASE}/panel/api/inbounds/update/1", json=payload)
|
||||
ok = r.json().get("success")
|
||||
print(f" Remote updated: {ok}")
|
||||
PYEOF
|
||||
}
|
||||
|
||||
restore_remote() {
|
||||
remote_update '{
|
||||
"network": "xhttp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"show": False,
|
||||
"dest": "'"$DEST"'",
|
||||
"serverNames": ["www.delfi.lv","www.lmt.lv","www.inbox.lv","e-klase.lv"],
|
||||
"privateKey": "'"$PRIVATE_KEY"'",
|
||||
"shortIds": ["'"$SID_B"'", "48b4c16249ad44ff", ""]
|
||||
},
|
||||
"xhttpSettings": {
|
||||
"path": "'"$XHTTP_PATH"'", "host": "",
|
||||
"mode": "auto",
|
||||
"extra": {"xPaddingBytes": "100-1000", "xmux": {
|
||||
"maxConcurrency": "16-32", "maxConnections": 0,
|
||||
"cMaxReuseTimes": "64-128", "cMaxLifetimeMs": 0,
|
||||
"hMaxRequestTimes": "600-900", "hMaxReusableSecs": "1800-3000"}}
|
||||
}
|
||||
}' "$UUID_B"
|
||||
sleep 2
|
||||
}
|
||||
|
||||
# ── Core benchmark runner ──────────────────────────────────────────────────
|
||||
bench() {
|
||||
local label="$1"
|
||||
local cfg_file="$2"
|
||||
|
||||
# Validate
|
||||
if ! "$XRAY_BIN" -test -c "$cfg_file" &>/dev/null; then
|
||||
fail "Config invalid — skipping"
|
||||
echo "$label|INVALID|—|—|—|—|—" >> /tmp/bench.tsv
|
||||
return
|
||||
fi
|
||||
|
||||
# Start xray
|
||||
cleanup
|
||||
"$XRAY_BIN" -c "$cfg_file" >/tmp/bench-xray.log 2>&1 &
|
||||
XRAY_PID=$!
|
||||
sleep 2
|
||||
|
||||
if ! kill -0 "$XRAY_PID" 2>/dev/null; then
|
||||
fail "Xray failed to start"
|
||||
echo "$label|FAIL|—|—|—|—|—" >> /tmp/bench.tsv
|
||||
return
|
||||
fi
|
||||
|
||||
# Connectivity check
|
||||
local exit_ip
|
||||
exit_ip=$(curl -s --socks5-hostname 127.0.0.1:$SOCKS_PORT --max-time 15 \
|
||||
https://api.ipify.org 2>/dev/null || echo "FAIL")
|
||||
if [[ "$exit_ip" != "$REMOTE_IP" ]]; then
|
||||
fail "No connectivity (exit IP: $exit_ip)"
|
||||
cleanup
|
||||
echo "$label|NO-CONN|—|—|—|—|—" >> /tmp/bench.tsv
|
||||
return
|
||||
fi
|
||||
pass "Connected via $exit_ip"
|
||||
|
||||
# Latency — N samples
|
||||
info "Latency ($LATENCY_SAMPLES samples)..."
|
||||
local lats=()
|
||||
for i in $(seq 1 $LATENCY_SAMPLES); do
|
||||
local ms
|
||||
ms=$(curl -s -o /dev/null -w "%{time_total}" --socks5-hostname 127.0.0.1:$SOCKS_PORT \
|
||||
--max-time 8 https://www.gstatic.com/generate_204 2>/dev/null \
|
||||
| awk '{printf "%d", $1*1000}')
|
||||
if [[ -n "$ms" && "$ms" -gt 0 ]]; then
|
||||
lats+=("$ms")
|
||||
fi
|
||||
done
|
||||
|
||||
local n=${#lats[@]} avg=0 p95=0 jitter=0 min=0 max=0
|
||||
if [[ $n -gt 0 ]]; then
|
||||
local sorted=($(printf '%s\n' "${lats[@]}" | sort -n))
|
||||
min=${sorted[0]}; max=${sorted[-1]}
|
||||
local sum=0; for v in "${lats[@]}"; do sum=$((sum+v)); done
|
||||
avg=$((sum/n))
|
||||
local p95i=$((n*95/100)); [[ $p95i -ge $n ]] && p95i=$((n-1))
|
||||
p95=${sorted[$p95i]}
|
||||
# Jitter = std dev (via python for float math)
|
||||
local csv_lats
|
||||
csv_lats=$(printf '%s,' "${lats[@]}" | sed 's/,$//')
|
||||
jitter=$(python3 -c "
|
||||
import math
|
||||
d=[$csv_lats]
|
||||
m=sum(d)/len(d)
|
||||
print(int(math.sqrt(sum((x-m)**2 for x in d)/len(d))))
|
||||
")
|
||||
fi
|
||||
pass "Latency: avg=${avg}ms p95=${p95}ms jitter=${jitter}ms (n=$n)"
|
||||
|
||||
# Download 10MB
|
||||
info "Download 10MB..."
|
||||
local dl_out
|
||||
dl_out=$(curl -s -o /dev/null -w "%{size_download} %{time_total}" \
|
||||
--socks5-hostname 127.0.0.1:$SOCKS_PORT --max-time 30 \
|
||||
"https://speed.cloudflare.com/__down?bytes=10485760" 2>/dev/null || echo "0 1")
|
||||
local dl_bytes dl_time dl_mbps=0
|
||||
dl_bytes=$(echo "$dl_out" | awk '{print $1}')
|
||||
dl_time=$(echo "$dl_out" | awk '{print $2}')
|
||||
if [[ "${dl_bytes:-0}" -gt 1000000 ]]; then
|
||||
dl_mbps=$(echo "scale=1; $dl_bytes*8/$dl_time/1000000" | bc)
|
||||
pass "Download: ${dl_mbps} Mbps"
|
||||
else
|
||||
fail "Download failed"
|
||||
fi
|
||||
|
||||
# Upload 5MB
|
||||
info "Upload 5MB..."
|
||||
local ul_out
|
||||
ul_out=$(dd if=/dev/urandom bs=1M count=5 2>/dev/null | \
|
||||
curl -s -o /dev/null -w "%{size_upload} %{time_total}" \
|
||||
--socks5-hostname 127.0.0.1:$SOCKS_PORT --max-time 30 \
|
||||
-X POST --data-binary @- https://httpbin.org/post 2>/dev/null || echo "0 1")
|
||||
local ul_bytes ul_time ul_mbps=0
|
||||
ul_bytes=$(echo "$ul_out" | awk '{print $1}')
|
||||
ul_time=$(echo "$ul_out" | awk '{print $2}')
|
||||
if [[ "${ul_bytes:-0}" -gt 100000 ]]; then
|
||||
ul_mbps=$(echo "scale=1; $ul_bytes*8/$ul_time/1000000" | bc)
|
||||
pass "Upload: ${ul_mbps} Mbps"
|
||||
else
|
||||
fail "Upload failed"
|
||||
ul_mbps=0
|
||||
fi
|
||||
|
||||
echo "$label|OK|$avg|$p95|$jitter|$dl_mbps|$ul_mbps" >> /tmp/bench.tsv
|
||||
cleanup
|
||||
sleep 1
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════
|
||||
# CONFIGS
|
||||
# ══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
# Baseline config writer helper
|
||||
write_xhttp_cfg() {
|
||||
local fp="$1" use_frag="$2" host_hdr="$3" path="$4" sni_val="$5"
|
||||
local frag_sockopt=""
|
||||
[[ "$use_frag" == "yes" ]] && frag_sockopt='"sockopt": {"dialerProxy": "frag-chain1"},'
|
||||
|
||||
cat > /tmp/bench-cfg.json << EOF
|
||||
{
|
||||
"log": {"loglevel": "error"},
|
||||
"inbounds": [{"listen":"127.0.0.1","port":$SOCKS_PORT,"protocol":"socks","settings":{"auth":"noauth","udp":true}}],
|
||||
"outbounds": [
|
||||
{
|
||||
"tag": "proxy", "protocol": "vless",
|
||||
"settings": {"vnext": [{"address": "share.alogins.net", "port": 443,
|
||||
"users": [{"id": "$UUID_B", "flow": "", "encryption": "none"}]}]},
|
||||
"streamSettings": {
|
||||
$frag_sockopt
|
||||
"network": "xhttp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"fingerprint": "$fp",
|
||||
"serverName": "$sni_val",
|
||||
"publicKey": "$PUBLIC_KEY",
|
||||
"shortId": "$SID_B",
|
||||
"spiderX": "/"
|
||||
},
|
||||
"xhttpSettings": {
|
||||
"path": "$path",
|
||||
"host": "$host_hdr",
|
||||
"mode": "auto",
|
||||
"extra": {
|
||||
"xPaddingBytes": "100-1000",
|
||||
"xmux": {"maxConcurrency": "16-32", "maxConnections": 0,
|
||||
"cMaxReuseTimes": "64-128", "cMaxLifetimeMs": 0,
|
||||
"hMaxRequestTimes": "600-900", "hMaxReusableSecs": "1800-3000"}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "frag-chain1", "protocol": "freedom",
|
||||
"settings": {"fragment": {"packets": "tlshello", "length": "100-200", "interval": "10-20"}},
|
||||
"streamSettings": {"sockopt": {"dialerProxy": "frag-chain2"}}
|
||||
},
|
||||
{
|
||||
"tag": "frag-chain2", "protocol": "freedom",
|
||||
"settings": {"fragment": {"packets": "1-3", "length": "1-5", "interval": "1-2"}}
|
||||
},
|
||||
{"tag": "direct", "protocol": "freedom"}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════
|
||||
# MAIN
|
||||
# ══════════════════════════════════════════════════════════════════════════
|
||||
echo "label|status|avg_ms|p95_ms|jitter_ms|dl_mbps|ul_mbps" > /tmp/bench.tsv
|
||||
|
||||
# Ensure baseline remote config is active
|
||||
info "Ensuring baseline remote config..."
|
||||
restore_remote
|
||||
|
||||
# ── 0. BASELINE ────────────────────────────────────────────────────────────
|
||||
hdr "0. BASELINE (current config)"
|
||||
write_xhttp_cfg "chrome" "no" "" "$XHTTP_PATH" "$SNI"
|
||||
bench "0-baseline" /tmp/bench-cfg.json
|
||||
|
||||
# ── 1. fingerprint=randomized ──────────────────────────────────────────────
|
||||
hdr "1. fingerprint: randomized"
|
||||
write_xhttp_cfg "randomized" "no" "" "$XHTTP_PATH" "$SNI"
|
||||
bench "1-fp-randomized" /tmp/bench-cfg.json
|
||||
|
||||
# ── 2. fingerprint=firefox ────────────────────────────────────────────────
|
||||
hdr "2. fingerprint: firefox"
|
||||
write_xhttp_cfg "firefox" "no" "" "$XHTTP_PATH" "$SNI"
|
||||
bench "2-fp-firefox" /tmp/bench-cfg.json
|
||||
|
||||
# ── 3. + TLS ClientHello fragment chain ───────────────────────────────────
|
||||
hdr "3. fragment chain on TLS ClientHello"
|
||||
write_xhttp_cfg "chrome" "yes" "" "$XHTTP_PATH" "$SNI"
|
||||
bench "3-fragment-chain" /tmp/bench-cfg.json
|
||||
|
||||
# ── 4. + host header = SNI domain ─────────────────────────────────────────
|
||||
hdr "4. host header = www.delfi.lv"
|
||||
write_xhttp_cfg "chrome" "no" "www.delfi.lv" "$XHTTP_PATH" "$SNI"
|
||||
bench "4-host-header" /tmp/bench-cfg.json
|
||||
|
||||
# ── 5. realistic XHTTP path + host header (needs server update) ───────────
|
||||
hdr "5. realistic path /api/v2/stream + host header"
|
||||
info "Updating remote path to /api/v2/stream ..."
|
||||
remote_update '{
|
||||
"network": "xhttp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"show": False,
|
||||
"dest": "'"$DEST"'",
|
||||
"serverNames": ["www.delfi.lv","www.lmt.lv","www.inbox.lv","e-klase.lv"],
|
||||
"privateKey": "'"$PRIVATE_KEY"'",
|
||||
"shortIds": ["'"$SID_B"'", ""]
|
||||
},
|
||||
"xhttpSettings": {
|
||||
"path": "/api/v2/stream", "host": "www.delfi.lv",
|
||||
"mode": "auto",
|
||||
"extra": {"xPaddingBytes": "100-1000", "xmux": {
|
||||
"maxConcurrency": "16-32", "maxConnections": 0,
|
||||
"cMaxReuseTimes": "64-128", "cMaxLifetimeMs": 0,
|
||||
"hMaxRequestTimes": "600-900", "hMaxReusableSecs": "1800-3000"}}
|
||||
}
|
||||
}' "$UUID_B"
|
||||
sleep 2
|
||||
write_xhttp_cfg "chrome" "no" "www.delfi.lv" "/api/v2/stream" "$SNI"
|
||||
bench "5-realistic-path" /tmp/bench-cfg.json
|
||||
restore_remote
|
||||
|
||||
# ── 6. SNI = e-klase.lv (highest throughput in SNI test) ──────────────────
|
||||
hdr "6. SNI = e-klase.lv (top SNI from previous benchmark)"
|
||||
write_xhttp_cfg "chrome" "no" "" "$XHTTP_PATH" "e-klase.lv"
|
||||
bench "6-sni-eklase" /tmp/bench-cfg.json
|
||||
|
||||
# ── 7. SNI = www.lmt.lv ───────────────────────────────────────────────────
|
||||
hdr "7. SNI = www.lmt.lv"
|
||||
write_xhttp_cfg "chrome" "no" "" "$XHTTP_PATH" "www.lmt.lv"
|
||||
bench "7-sni-lmt" /tmp/bench-cfg.json
|
||||
|
||||
# ── 8. BBR check + enable on remote ───────────────────────────────────────
|
||||
hdr "8. BBR congestion control on remote server"
|
||||
source "$VENV/bin/activate"
|
||||
BBR_STATUS=$(python3 << 'PYEOF'
|
||||
import paramiko
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
ssh.connect("83.99.190.32", username="juris", password="VitaIeva2A.")
|
||||
_, out, _ = ssh.exec_command("lxc exec xray -- sysctl net.ipv4.tcp_congestion_control 2>/dev/null")
|
||||
cc = out.read().decode().strip()
|
||||
_, out2, _ = ssh.exec_command("lxc exec xray -- sysctl net.core.default_qdisc 2>/dev/null")
|
||||
qd = out2.read().decode().strip()
|
||||
print(f"cc={cc} | qd={qd}")
|
||||
ssh.close()
|
||||
PYEOF
|
||||
)
|
||||
info "Current: $BBR_STATUS"
|
||||
|
||||
if echo "$BBR_STATUS" | grep -q "bbr"; then
|
||||
info "BBR already enabled — testing as-is"
|
||||
write_xhttp_cfg "chrome" "no" "" "$XHTTP_PATH" "$SNI"
|
||||
bench "8-bbr-already-on" /tmp/bench-cfg.json
|
||||
else
|
||||
info "Enabling BBR on remote container..."
|
||||
python3 << 'PYEOF'
|
||||
import paramiko
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
ssh.connect("83.99.190.32", username="juris", password="VitaIeva2A.")
|
||||
for cmd in [
|
||||
"lxc exec xray -- sysctl -w net.core.default_qdisc=fq",
|
||||
"lxc exec xray -- sysctl -w net.ipv4.tcp_congestion_control=bbr",
|
||||
"lxc exec xray -- sysctl -w net.core.rmem_max=16777216",
|
||||
"lxc exec xray -- sysctl -w net.core.wmem_max=16777216",
|
||||
]:
|
||||
_, out, err = ssh.exec_command(cmd)
|
||||
print(f" {cmd.split('--')[1].strip()}: {(out.read()+err.read()).decode().strip()}")
|
||||
ssh.close()
|
||||
PYEOF
|
||||
sleep 1
|
||||
write_xhttp_cfg "chrome" "no" "" "$XHTTP_PATH" "$SNI"
|
||||
bench "8-bbr-enabled" /tmp/bench-cfg.json
|
||||
fi
|
||||
|
||||
# ── Restore remote to canonical state ─────────────────────────────────────
|
||||
hdr "Restoring remote to baseline"
|
||||
restore_remote
|
||||
pass "Remote restored"
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════
|
||||
# SUMMARY
|
||||
# ══════════════════════════════════════════════════════════════════════════
|
||||
hdr "RESULTS SUMMARY"
|
||||
echo ""
|
||||
printf "%-32s %-7s %-7s %-9s %-9s %-9s\n" "Test" "Avg ms" "P95 ms" "Jitter ms" "DL Mbps" "UL Mbps"
|
||||
printf "%-32s %-7s %-7s %-9s %-9s %-9s\n" "────────────────────────────────" "───────" "───────" "─────────" "───────" "───────"
|
||||
|
||||
BASELINE_AVG=0; BASELINE_P95=0; BASELINE_JIT=0; BASELINE_DL=0; BASELINE_UL=0
|
||||
while IFS='|' read -r label status avg p95 jit dl ul; do
|
||||
[[ "$label" == "label" ]] && continue
|
||||
if [[ "$status" == "OK" ]]; then
|
||||
if [[ "$label" == "0-baseline" ]]; then
|
||||
BASELINE_AVG=$avg; BASELINE_P95=$p95; BASELINE_JIT=$jit
|
||||
BASELINE_DL=$dl; BASELINE_UL=$ul
|
||||
printf "${BOLD}%-32s${NC} %-7s %-7s %-9s %-9s %-9s\n" \
|
||||
"$label" "${avg}ms" "${p95}ms" "${jit}ms" "${dl}" "${ul}"
|
||||
else
|
||||
# Delta indicators
|
||||
local_delta_avg="" local_delta_jit="" local_delta_dl=""
|
||||
[[ -n "$BASELINE_AVG" && "$BASELINE_AVG" -gt 0 ]] && {
|
||||
diff=$((avg - BASELINE_AVG))
|
||||
[[ $diff -lt 0 ]] && local_delta_avg="${GREEN}${diff}${NC}" || local_delta_avg="${RED}+${diff}${NC}"
|
||||
}
|
||||
printf "%-32s %-7s %-7s %-9s %-9s %-9s\n" \
|
||||
"$label" "${avg}ms" "${p95}ms" "${jit}ms" "${dl}" "${ul}"
|
||||
fi
|
||||
else
|
||||
printf "${RED}%-32s${NC} %s\n" "$label" "$status"
|
||||
fi
|
||||
done < /tmp/bench.tsv
|
||||
|
||||
# Write markdown
|
||||
{
|
||||
cat << MDEOF
|
||||
# DPI Resistance Improvement Benchmark
|
||||
|
||||
**Date**: $(date '+%Y-%m-%d %H:%M')
|
||||
**Baseline**: VLESS+XHTTP+Reality, fingerprint=chrome, SNI=www.delfi.lv, path=/xt-6036d37d
|
||||
**Latency samples per test**: $LATENCY_SAMPLES
|
||||
**Jitter**: standard deviation of latency samples
|
||||
|
||||
## Results
|
||||
|
||||
| Test | Avg ms | P95 ms | Jitter ms | DL Mbps | UL Mbps | Notes |
|
||||
|------|--------|--------|-----------|---------|---------|-------|
|
||||
MDEOF
|
||||
|
||||
while IFS='|' read -r label status avg p95 jit dl ul; do
|
||||
[[ "$label" == "label" ]] && continue
|
||||
notes=""
|
||||
case "$label" in
|
||||
0-baseline) notes="Current active config" ;;
|
||||
1-fp-randomized) notes="uTLS fingerprint rotated per connection" ;;
|
||||
2-fp-firefox) notes="Firefox uTLS profile" ;;
|
||||
3-fragment-chain) notes="TLS ClientHello split 100-200B + micro-frag 1-5B" ;;
|
||||
4-host-header) notes="HTTP Host header = www.delfi.lv" ;;
|
||||
5-realistic-path) notes="Path=/api/v2/stream + Host header" ;;
|
||||
6-sni-eklase) notes="SNI switched to e-klase.lv" ;;
|
||||
7-sni-lmt) notes="SNI switched to www.lmt.lv" ;;
|
||||
8-bbr*) notes="BBR congestion control on remote" ;;
|
||||
esac
|
||||
if [[ "$status" == "OK" ]]; then
|
||||
echo "| $label | ${avg}ms | ${p95}ms | ${jit}ms | ${dl} | ${ul} | $notes |"
|
||||
else
|
||||
echo "| $label | — | — | — | — | — | $status |"
|
||||
fi
|
||||
done < /tmp/bench.tsv
|
||||
|
||||
cat << 'MDEOF'
|
||||
|
||||
## What Each Test Changes
|
||||
- **fingerprint=randomized**: uTLS fingerprint rotated per connection — defeats fingerprint-based blocking
|
||||
- **fingerprint=firefox**: Firefox uTLS profile instead of Chrome
|
||||
- **fragment chain**: TLS ClientHello split into 100-200B chunks, then micro-fragmented 1-5B + noise — defeats handshake DPI
|
||||
- **host header**: Sets HTTP `Host:` header to match SNI — makes request look more legitimate
|
||||
- **realistic path**: Changes XHTTP path from synthetic to `/api/v2/stream` with matching host header
|
||||
- **SNI e-klase.lv / lmt.lv**: Alternative SNIs from previous benchmark (dest stays www.delfi.lv)
|
||||
- **BBR**: Linux BBR congestion control + larger TCP buffers on remote — improves throughput under loss
|
||||
MDEOF
|
||||
} > "$RESULTS_FILE"
|
||||
|
||||
echo ""
|
||||
echo -e "${BOLD}Results: $RESULTS_FILE${NC}"
|
||||
44
config_test_results.md
Normal file
44
config_test_results.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Xray Configuration Comparison — Port 443
|
||||
|
||||
**Date**: 2026-02-20 15:49:15
|
||||
**Local xray**: 25.10.15 | **Remote xray**: 26.2.6
|
||||
**Remote**: share.alogins.net (83.99.190.32), LXD container "xray"
|
||||
|
||||
## Results
|
||||
|
||||
| Configuration | Status | Avg Latency | P95 Latency | Download | Upload |
|
||||
|---------------|--------|-------------|-------------|----------|--------|
|
||||
| A: TCP+Reality+Vision (baseline) | ✓ OK | 301ms | 1225ms | 26.78 Mbps | 2.99 Mbps |
|
||||
| B: XHTTP+Reality | ✓ OK | 142ms | 225ms | 27.54 Mbps | 3.44 Mbps |
|
||||
| C: gRPC+Reality | ✓ OK | 144ms | 250ms | 18.83 Mbps | 3.19 Mbps |
|
||||
| D: TCP+Reality+Vision+Fragment+Noise | ✓ OK | 393ms | 452ms | 36.10 Mbps | 3.18 Mbps |
|
||||
|
||||
## Configuration Descriptions
|
||||
|
||||
| Config | Transport | Port | Flow | DPI Target |
|
||||
|--------|-----------|------|------|------------|
|
||||
| A | TCP + Reality | 443 | xtls-rprx-vision | Baseline — standard Russia anti-DPI |
|
||||
| B | XHTTP + Reality | 443 | none | Volume-based TCP freezing (split uploads, XMUX padding) |
|
||||
| C | gRPC + Reality | 443 | none | H2 pattern analysis (looks like enterprise API traffic) |
|
||||
| D | TCP + Reality + Fragment/Noise | 443 | xtls-rprx-vision | TLS ClientHello DPI signature (fragment chains + noise) |
|
||||
|
||||
## Winner: B — XHTTP + Reality
|
||||
|
||||
**XHTTP is now the active configuration** (remote inbound + local outbound `juris-xhttp`).
|
||||
|
||||
- Best average latency: **142ms** (vs 301ms for baseline TCP+Vision)
|
||||
- Best P95 latency: **225ms** (vs 1225ms for baseline — 5× improvement)
|
||||
- Best upload: **3.44 Mbps**
|
||||
- Download comparable to baseline: **27.54 Mbps**
|
||||
|
||||
Config A (`juris-reality`, TCP+Vision) remains as fallback outbound on local server.
|
||||
|
||||
### Why XHTTP beats TCP+Vision here
|
||||
TCP+Vision (baseline) showed a severe P95 spike (1225ms) — likely caused by the volume-based TCP freezing that TSPU applies. XHTTP splits uploads into multiple small HTTP POST requests, preventing the single-connection volume trigger. XMUX multiplexing also reduces per-request overhead.
|
||||
|
||||
## Methodology
|
||||
- Each config tested sequentially on the **same port 443**
|
||||
- Remote inbound swapped on-the-fly via 3x-ui API
|
||||
- 10 latency samples to google.com
|
||||
- 10 MB download from Cloudflare speed test
|
||||
- 5 MB upload to httpbin.org
|
||||
37
configs/e-klase.lv.json
Normal file
37
configs/e-klase.lv.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"log": { "loglevel": "error" },
|
||||
"inbounds": [{
|
||||
"listen": "127.0.0.1",
|
||||
"port": 11080,
|
||||
"protocol": "socks",
|
||||
"settings": { "auth": "noauth", "udp": true },
|
||||
"tag": "test-socks"
|
||||
}],
|
||||
"outbounds": [{
|
||||
"tag": "juris-reality",
|
||||
"protocol": "vless",
|
||||
"settings": {
|
||||
"vnext": [{
|
||||
"address": "share.alogins.net",
|
||||
"port": 443,
|
||||
"users": [{
|
||||
"id": "64522a14-54aa-4b3c-8071-8c8b17aa1f08",
|
||||
"flow": "xtls-rprx-vision",
|
||||
"encryption": "none"
|
||||
}]
|
||||
}]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"fingerprint": "chrome",
|
||||
"serverName": "e-klase.lv",
|
||||
"publicKey": "58Iqd6LuWXgvjAgo92-7KURhTp0Vj79yGF81l_iuvTw",
|
||||
"shortId": "48b4c16249ad44ff",
|
||||
"spiderX": "/"
|
||||
},
|
||||
"tcpSettings": { "header": { "type": "none" } }
|
||||
}
|
||||
}]
|
||||
}
|
||||
37
configs/share.alogins.net.json
Normal file
37
configs/share.alogins.net.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"log": { "loglevel": "error" },
|
||||
"inbounds": [{
|
||||
"listen": "127.0.0.1",
|
||||
"port": 11080,
|
||||
"protocol": "socks",
|
||||
"settings": { "auth": "noauth", "udp": true },
|
||||
"tag": "test-socks"
|
||||
}],
|
||||
"outbounds": [{
|
||||
"tag": "juris-reality",
|
||||
"protocol": "vless",
|
||||
"settings": {
|
||||
"vnext": [{
|
||||
"address": "share.alogins.net",
|
||||
"port": 443,
|
||||
"users": [{
|
||||
"id": "64522a14-54aa-4b3c-8071-8c8b17aa1f08",
|
||||
"flow": "xtls-rprx-vision",
|
||||
"encryption": "none"
|
||||
}]
|
||||
}]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"fingerprint": "chrome",
|
||||
"serverName": "share.alogins.net",
|
||||
"publicKey": "58Iqd6LuWXgvjAgo92-7KURhTp0Vj79yGF81l_iuvTw",
|
||||
"shortId": "48b4c16249ad44ff",
|
||||
"spiderX": "/"
|
||||
},
|
||||
"tcpSettings": { "header": { "type": "none" } }
|
||||
}
|
||||
}]
|
||||
}
|
||||
37
configs/www.delfi.lv.json
Normal file
37
configs/www.delfi.lv.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"log": { "loglevel": "error" },
|
||||
"inbounds": [{
|
||||
"listen": "127.0.0.1",
|
||||
"port": 11080,
|
||||
"protocol": "socks",
|
||||
"settings": { "auth": "noauth", "udp": true },
|
||||
"tag": "test-socks"
|
||||
}],
|
||||
"outbounds": [{
|
||||
"tag": "juris-reality",
|
||||
"protocol": "vless",
|
||||
"settings": {
|
||||
"vnext": [{
|
||||
"address": "share.alogins.net",
|
||||
"port": 443,
|
||||
"users": [{
|
||||
"id": "64522a14-54aa-4b3c-8071-8c8b17aa1f08",
|
||||
"flow": "xtls-rprx-vision",
|
||||
"encryption": "none"
|
||||
}]
|
||||
}]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"fingerprint": "chrome",
|
||||
"serverName": "www.delfi.lv",
|
||||
"publicKey": "58Iqd6LuWXgvjAgo92-7KURhTp0Vj79yGF81l_iuvTw",
|
||||
"shortId": "48b4c16249ad44ff",
|
||||
"spiderX": "/"
|
||||
},
|
||||
"tcpSettings": { "header": { "type": "none" } }
|
||||
}
|
||||
}]
|
||||
}
|
||||
37
configs/www.inbox.lv.json
Normal file
37
configs/www.inbox.lv.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"log": { "loglevel": "error" },
|
||||
"inbounds": [{
|
||||
"listen": "127.0.0.1",
|
||||
"port": 11080,
|
||||
"protocol": "socks",
|
||||
"settings": { "auth": "noauth", "udp": true },
|
||||
"tag": "test-socks"
|
||||
}],
|
||||
"outbounds": [{
|
||||
"tag": "juris-reality",
|
||||
"protocol": "vless",
|
||||
"settings": {
|
||||
"vnext": [{
|
||||
"address": "share.alogins.net",
|
||||
"port": 443,
|
||||
"users": [{
|
||||
"id": "64522a14-54aa-4b3c-8071-8c8b17aa1f08",
|
||||
"flow": "xtls-rprx-vision",
|
||||
"encryption": "none"
|
||||
}]
|
||||
}]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"fingerprint": "chrome",
|
||||
"serverName": "www.inbox.lv",
|
||||
"publicKey": "58Iqd6LuWXgvjAgo92-7KURhTp0Vj79yGF81l_iuvTw",
|
||||
"shortId": "48b4c16249ad44ff",
|
||||
"spiderX": "/"
|
||||
},
|
||||
"tcpSettings": { "header": { "type": "none" } }
|
||||
}
|
||||
}]
|
||||
}
|
||||
37
configs/www.lmt.lv.json
Normal file
37
configs/www.lmt.lv.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"log": { "loglevel": "error" },
|
||||
"inbounds": [{
|
||||
"listen": "127.0.0.1",
|
||||
"port": 11080,
|
||||
"protocol": "socks",
|
||||
"settings": { "auth": "noauth", "udp": true },
|
||||
"tag": "test-socks"
|
||||
}],
|
||||
"outbounds": [{
|
||||
"tag": "juris-reality",
|
||||
"protocol": "vless",
|
||||
"settings": {
|
||||
"vnext": [{
|
||||
"address": "share.alogins.net",
|
||||
"port": 443,
|
||||
"users": [{
|
||||
"id": "64522a14-54aa-4b3c-8071-8c8b17aa1f08",
|
||||
"flow": "xtls-rprx-vision",
|
||||
"encryption": "none"
|
||||
}]
|
||||
}]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"fingerprint": "chrome",
|
||||
"serverName": "www.lmt.lv",
|
||||
"publicKey": "58Iqd6LuWXgvjAgo92-7KURhTp0Vj79yGF81l_iuvTw",
|
||||
"shortId": "48b4c16249ad44ff",
|
||||
"spiderX": "/"
|
||||
},
|
||||
"tcpSettings": { "header": { "type": "none" } }
|
||||
}
|
||||
}]
|
||||
}
|
||||
37
configs/www.lsm.lv.json
Normal file
37
configs/www.lsm.lv.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"log": { "loglevel": "error" },
|
||||
"inbounds": [{
|
||||
"listen": "127.0.0.1",
|
||||
"port": 11080,
|
||||
"protocol": "socks",
|
||||
"settings": { "auth": "noauth", "udp": true },
|
||||
"tag": "test-socks"
|
||||
}],
|
||||
"outbounds": [{
|
||||
"tag": "juris-reality",
|
||||
"protocol": "vless",
|
||||
"settings": {
|
||||
"vnext": [{
|
||||
"address": "share.alogins.net",
|
||||
"port": 443,
|
||||
"users": [{
|
||||
"id": "64522a14-54aa-4b3c-8071-8c8b17aa1f08",
|
||||
"flow": "xtls-rprx-vision",
|
||||
"encryption": "none"
|
||||
}]
|
||||
}]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"fingerprint": "chrome",
|
||||
"serverName": "www.lsm.lv",
|
||||
"publicKey": "58Iqd6LuWXgvjAgo92-7KURhTp0Vj79yGF81l_iuvTw",
|
||||
"shortId": "48b4c16249ad44ff",
|
||||
"spiderX": "/"
|
||||
},
|
||||
"tcpSettings": { "header": { "type": "none" } }
|
||||
}
|
||||
}]
|
||||
}
|
||||
37
configs/www.microsoft.com.json
Normal file
37
configs/www.microsoft.com.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"log": { "loglevel": "error" },
|
||||
"inbounds": [{
|
||||
"listen": "127.0.0.1",
|
||||
"port": 11080,
|
||||
"protocol": "socks",
|
||||
"settings": { "auth": "noauth", "udp": true },
|
||||
"tag": "test-socks"
|
||||
}],
|
||||
"outbounds": [{
|
||||
"tag": "juris-reality",
|
||||
"protocol": "vless",
|
||||
"settings": {
|
||||
"vnext": [{
|
||||
"address": "share.alogins.net",
|
||||
"port": 443,
|
||||
"users": [{
|
||||
"id": "64522a14-54aa-4b3c-8071-8c8b17aa1f08",
|
||||
"flow": "xtls-rprx-vision",
|
||||
"encryption": "none"
|
||||
}]
|
||||
}]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"fingerprint": "chrome",
|
||||
"serverName": "www.microsoft.com",
|
||||
"publicKey": "58Iqd6LuWXgvjAgo92-7KURhTp0Vj79yGF81l_iuvTw",
|
||||
"shortId": "48b4c16249ad44ff",
|
||||
"spiderX": "/"
|
||||
},
|
||||
"tcpSettings": { "header": { "type": "none" } }
|
||||
}
|
||||
}]
|
||||
}
|
||||
37
configs/www.tele2.lv.json
Normal file
37
configs/www.tele2.lv.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"log": { "loglevel": "error" },
|
||||
"inbounds": [{
|
||||
"listen": "127.0.0.1",
|
||||
"port": 11080,
|
||||
"protocol": "socks",
|
||||
"settings": { "auth": "noauth", "udp": true },
|
||||
"tag": "test-socks"
|
||||
}],
|
||||
"outbounds": [{
|
||||
"tag": "juris-reality",
|
||||
"protocol": "vless",
|
||||
"settings": {
|
||||
"vnext": [{
|
||||
"address": "share.alogins.net",
|
||||
"port": 443,
|
||||
"users": [{
|
||||
"id": "64522a14-54aa-4b3c-8071-8c8b17aa1f08",
|
||||
"flow": "xtls-rprx-vision",
|
||||
"encryption": "none"
|
||||
}]
|
||||
}]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"fingerprint": "chrome",
|
||||
"serverName": "www.tele2.lv",
|
||||
"publicKey": "58Iqd6LuWXgvjAgo92-7KURhTp0Vj79yGF81l_iuvTw",
|
||||
"shortId": "48b4c16249ad44ff",
|
||||
"spiderX": "/"
|
||||
},
|
||||
"tcpSettings": { "header": { "type": "none" } }
|
||||
}
|
||||
}]
|
||||
}
|
||||
40
improvement_results.md
Normal file
40
improvement_results.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# DPI Resistance Improvement Benchmark
|
||||
|
||||
**Date**: 2026-02-20 16:26
|
||||
**Baseline**: VLESS+XHTTP+Reality, fingerprint=chrome, SNI=www.delfi.lv, path=/xt-6036d37d
|
||||
**Latency samples per test**: 20
|
||||
**Jitter**: standard deviation of latency samples
|
||||
|
||||
## Results
|
||||
|
||||
| Test | Avg ms | P95 ms | Jitter ms | DL Mbps | UL Mbps | Notes |
|
||||
|------|--------|--------|-----------|---------|---------|-------|
|
||||
| 0-baseline | 76ms | 95ms | 5ms | 38.7 | 4.0 | Current active config |
|
||||
| 1-fp-randomized | 75ms | 89ms | 3ms | 33.5 | 3.6 | uTLS fingerprint rotated per connection |
|
||||
| 2-fp-firefox | 73ms | 76ms | 0ms | 24.5 | 2.8 | Firefox uTLS profile |
|
||||
| 3-fragment-chain | 75ms | 79ms | 2ms | 30.4 | 3.2 | TLS ClientHello split 100-200B + micro-frag 1-5B |
|
||||
| 4-host-header | 74ms | 77ms | 1ms | 27.6 | 3.8 | HTTP Host header = www.delfi.lv |
|
||||
| 5-realistic-path | 74ms | 87ms | 3ms | 31.2 | 3.8 | Path=/api/v2/stream + Host header |
|
||||
| 6-sni-eklase | 73ms | 78ms | 1ms | 30.7 | 2.3 | SNI switched to e-klase.lv |
|
||||
| 7-sni-lmt | 75ms | 80ms | 1ms | 26.3 | 4.0 | SNI switched to www.lmt.lv |
|
||||
| 8-bbr-enabled | 74ms | 82ms | 2ms | 31.3 | 3.4 | BBR congestion control on remote |
|
||||
|
||||
## Applied Improvements
|
||||
|
||||
After benchmarking, the following were applied permanently to the local x-ui config:
|
||||
- `juris-xhttp` (chrome): host header `www.delfi.lv` — primary outbound
|
||||
- `juris-xhttp-firefox` (firefox): host header `www.delfi.lv` — low-jitter alternate
|
||||
- `juris-xhttp-safari` (safari): host header `www.delfi.lv` — fingerprint diversity
|
||||
|
||||
> **Note**: `fingerprint=randomized` was NOT applied. Reality's anti-probing rejects random TLS
|
||||
> fingerprints (connection reset by peer). Only named browser profiles are accepted.
|
||||
|
||||
## What Each Test Changes
|
||||
- **fingerprint=randomized**: uTLS fingerprint rotated per connection — defeats fingerprint-based blocking
|
||||
⚠️ **Incompatible with Reality** — triggers anti-probing protection, connection reset
|
||||
- **fingerprint=firefox**: Firefox uTLS profile instead of Chrome
|
||||
- **fragment chain**: TLS ClientHello split into 100-200B chunks, then micro-fragmented 1-5B + noise — defeats handshake DPI
|
||||
- **host header**: Sets HTTP `Host:` header to match SNI — makes request look more legitimate
|
||||
- **realistic path**: Changes XHTTP path from synthetic to `/api/v2/stream` with matching host header
|
||||
- **SNI e-klase.lv / lmt.lv**: Alternative SNIs from previous benchmark (dest stays www.delfi.lv)
|
||||
- **BBR**: Linux BBR congestion control + larger TCP buffers on remote — improves throughput under loss
|
||||
1045
plan_enduser_.md
Normal file
1045
plan_enduser_.md
Normal file
File diff suppressed because it is too large
Load Diff
100
sni_test_results.md
Normal file
100
sni_test_results.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# SNI Configuration Test Results
|
||||
|
||||
Date: 2026-02-20 04:47 UTC
|
||||
Server: share.alogins.net (83.99.190.32)
|
||||
Protocol: VLESS + XTLS-Reality + Vision
|
||||
Reality dest: www.delfi.lv:443
|
||||
|
||||
## Results
|
||||
|
||||
| # | SNI | Type | Conn | Latency Min | Latency Avg | Latency P95 | Latency Max | Download | Upload |
|
||||
|---|-----|------|------|-------------|-------------|-------------|-------------|----------|--------|
|
||||
| 1 | `www.delfi.lv` | Latvia news #1 | OK | 120ms | 121ms | 124ms | 124ms | 44.79 Mbps | 33.90 Mbps |
|
||||
| 2 | `www.lmt.lv` | Latvia telecom #1 | OK | 130ms | 133ms | 136ms | 136ms | 50.58 Mbps | 37.33 Mbps |
|
||||
| 3 | `www.tele2.lv` | Latvia telecom #2 | FAIL | — | — | — | — | — | — |
|
||||
| 4 | `www.lsm.lv` | Latvia public broadcasting | FAIL | — | — | — | — | — | — |
|
||||
| 5 | `www.inbox.lv` | Latvia webmail | OK | 120ms | 121ms | 124ms | 124ms | 45.48 Mbps | 32.98 Mbps |
|
||||
| 6 | `e-klase.lv` | Latvia education | OK | 119ms | 121ms | 123ms | 123ms | 54.84 Mbps | 29.58 Mbps |
|
||||
| 7 | `share.alogins.net` | Custom domain | FAIL | — | — | — | — | — | — |
|
||||
| 8 | `www.microsoft.com` | Worldwide baseline | FAIL | — | — | — | — | — | — |
|
||||
|
||||
## Why Some SNIs Failed
|
||||
|
||||
SNIs #3, #4, #7, #8 failed because the Reality `dest` is `www.delfi.lv:443`. When a probe or handshake arrives with a mismatched SNI (e.g. `www.microsoft.com`), the dest server (delfi.lv) rejects the TLS ClientHello. Only SNIs whose TLS stacks are compatible with the dest server work. This is a Reality protocol constraint: **the `dest` domain determines which SNIs are viable**.
|
||||
|
||||
The 4 working SNIs (`www.delfi.lv`, `www.lmt.lv`, `www.inbox.lv`, `e-klase.lv`) are all hosted behind CDN infrastructure compatible with delfi.lv's TLS responses.
|
||||
|
||||
To use other SNIs (e.g. `www.microsoft.com`), change `dest` to match (e.g. `www.microsoft.com:443`).
|
||||
|
||||
## Best Configuration
|
||||
|
||||
**Winner: `www.delfi.lv`** (Latvia news #1)
|
||||
|
||||
- Average latency: 121ms
|
||||
- Download: 44.79 Mbps
|
||||
- Upload: 33.90 Mbps
|
||||
|
||||
### Recommended client outbound config
|
||||
|
||||
```json
|
||||
{
|
||||
"tag": "juris-reality",
|
||||
"protocol": "vless",
|
||||
"settings": {
|
||||
"vnext": [{
|
||||
"address": "share.alogins.net",
|
||||
"port": 443,
|
||||
"users": [{
|
||||
"id": "64522a14-54aa-4b3c-8071-8c8b17aa1f08",
|
||||
"flow": "xtls-rprx-vision",
|
||||
"encryption": "none"
|
||||
}]
|
||||
}]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"fingerprint": "chrome",
|
||||
"serverName": "www.delfi.lv",
|
||||
"publicKey": "58Iqd6LuWXgvjAgo92-7KURhTp0Vj79yGF81l_iuvTw",
|
||||
"shortId": "48b4c16249ad44ff",
|
||||
"spiderX": "/"
|
||||
},
|
||||
"tcpSettings": { "header": { "type": "none" } }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## All Configurations
|
||||
|
||||
Individual config files are stored in `configs/` directory.
|
||||
|
||||
- `configs/www.delfi.lv.json` — SNI: `www.delfi.lv`
|
||||
- `configs/www.lmt.lv.json` — SNI: `www.lmt.lv`
|
||||
- `configs/www.tele2.lv.json` — SNI: `www.tele2.lv`
|
||||
- `configs/www.lsm.lv.json` — SNI: `www.lsm.lv`
|
||||
- `configs/www.inbox.lv.json` — SNI: `www.inbox.lv`
|
||||
- `configs/e-klase.lv.json` — SNI: `e-klase.lv`
|
||||
- `configs/share.alogins.net.json` — SNI: `share.alogins.net`
|
||||
- `configs/www.microsoft.com.json` — SNI: `www.microsoft.com`
|
||||
|
||||
## Server-Side Reality Settings
|
||||
|
||||
```json
|
||||
"realitySettings": {
|
||||
"dest": "www.delfi.lv:443",
|
||||
"serverNames": [
|
||||
"www.delfi.lv",
|
||||
"www.lmt.lv",
|
||||
"www.tele2.lv",
|
||||
"www.lsm.lv",
|
||||
"www.inbox.lv",
|
||||
"e-klase.lv",
|
||||
"share.alogins.net",
|
||||
"www.microsoft.com"
|
||||
],
|
||||
"privateKey": "KJfhenZvJV1kXwv4kDC8NPBtMUY0RR8lFrxsxfXfFmY",
|
||||
"shortIds": ["48b4c16249ad44ff", ""]
|
||||
}
|
||||
```
|
||||
525
test_all_configs.sh
Executable file
525
test_all_configs.sh
Executable file
@@ -0,0 +1,525 @@
|
||||
#!/usr/bin/env bash
|
||||
# test_all_configs.sh — Sequential test of 4 xray configurations on port 443
|
||||
# For each config: swap remote inbound, test locally, restore, next.
|
||||
#
|
||||
# Configs:
|
||||
# A: VLESS + TCP + Reality + Vision (baseline)
|
||||
# B: VLESS + XHTTP + Reality (split uploads, anti-freeze)
|
||||
# C: VLESS + gRPC + Reality (H2 enterprise traffic)
|
||||
# D: VLESS + TCP + Reality + Vision + Fragment+Noise (TLS handshake obfuscation)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
XRAY_BIN="/usr/local/x-ui/bin/xray-linux-amd64"
|
||||
SOCKS_PORT=11082
|
||||
REMOTE_IP="83.99.190.32"
|
||||
RESULTS_FILE="/home/alvis/ai-xray/config_test_results.md"
|
||||
VENV="/home/alvis/ai-xray/venv"
|
||||
|
||||
# Shared keys
|
||||
PUBLIC_KEY="58Iqd6LuWXgvjAgo92-7KURhTp0Vj79yGF81l_iuvTw"
|
||||
PRIVATE_KEY="KJfhenZvJV1kXwv4kDC8NPBtMUY0RR8lFrxsxfXfFmY"
|
||||
DEST="www.delfi.lv:443"
|
||||
SERVER_NAMES='["www.delfi.lv","www.lmt.lv","www.inbox.lv","e-klase.lv"]'
|
||||
|
||||
# Config A
|
||||
UUID_A="64522a14-54aa-4b3c-8071-8c8b17aa1f08"
|
||||
SID_A="48b4c16249ad44ff"
|
||||
|
||||
# Config B (XHTTP)
|
||||
UUID_B="6e422ab5-070a-43f6-8241-38cd56d23d24"
|
||||
SID_B="6036d37d12c443c4"
|
||||
XHTTP_PATH="/xt-6036d37d"
|
||||
|
||||
# Config C (gRPC)
|
||||
UUID_C="d0dd1e83-43d8-4bf8-a2b0-005362076b7b"
|
||||
SID_C="52dfa6856de91d0f"
|
||||
GRPC_SVC="grpc-52dfa685"
|
||||
|
||||
# Config D (Fragment) — uses Config A server-side
|
||||
UUID_D="fdc4fbc1-d3c0-43e9-917f-4026ba6d4f7c"
|
||||
SID_D="90abc7d195f7341d"
|
||||
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
|
||||
header() { echo ""; echo -e "${BOLD}${CYAN}══════════════════════════════════════════${NC}"; echo -e "${BOLD}${CYAN} $1${NC}"; echo -e "${BOLD}${CYAN}══════════════════════════════════════════${NC}"; }
|
||||
pass() { echo -e " ${GREEN}✓${NC} $1"; }
|
||||
fail() { echo -e " ${RED}✗${NC} $1"; }
|
||||
info() { echo -e " ${YELLOW}→${NC} $1"; }
|
||||
|
||||
XRAY_PID=""
|
||||
cleanup_xray() {
|
||||
if [[ -n "$XRAY_PID" ]]; then
|
||||
kill "$XRAY_PID" 2>/dev/null || true
|
||||
wait "$XRAY_PID" 2>/dev/null || true
|
||||
XRAY_PID=""
|
||||
fi
|
||||
}
|
||||
trap 'cleanup_xray; rm -f /tmp/xray-test-cfg.json' EXIT
|
||||
|
||||
# ── Remote inbound management ─────────────────────────────────────────────
|
||||
remote_update_inbound() {
|
||||
# $1 = inbound JSON string (the streamSettings portion as Python dict repr)
|
||||
local stream_json="$1"
|
||||
local uuid="$2"
|
||||
local flow="$3"
|
||||
local sid="$4"
|
||||
source "$VENV/bin/activate"
|
||||
python3 << PYEOF
|
||||
import requests, json
|
||||
import urllib3; urllib3.disable_warnings()
|
||||
s = requests.Session(); s.verify = False
|
||||
BASE = "https://share.alogins.net:16627/gBdsRLtVZdgZ63wmVR"
|
||||
s.post(f"{BASE}/login", data={"username": "xrayadmin", "password": "Admin2026!"})
|
||||
|
||||
stream = $stream_json
|
||||
|
||||
client = {"id": "$uuid", "flow": "$flow", "email": "test-client",
|
||||
"limitIp": 0, "totalGB": 0, "expiryTime": 0,
|
||||
"enable": True, "tgId": "", "subId": "", "comment": ""}
|
||||
|
||||
payload = {
|
||||
"id": 1,
|
||||
"tag": "inbound-443",
|
||||
"enable": True,
|
||||
"port": 443,
|
||||
"listen": "",
|
||||
"protocol": "vless",
|
||||
"settings": json.dumps({"clients": [client], "decryption": "none", "fallbacks": []}),
|
||||
"streamSettings": json.dumps(stream),
|
||||
"sniffing": json.dumps({"enabled": False}),
|
||||
"remark": "inbound-443",
|
||||
"expiryTime": 0
|
||||
}
|
||||
r = s.post(f"{BASE}/panel/api/inbounds/update/1", json=payload)
|
||||
print(r.json().get("success"), r.json().get("msg",""))
|
||||
PYEOF
|
||||
}
|
||||
|
||||
restore_baseline() {
|
||||
info "Restoring baseline (TCP+Vision) on remote..."
|
||||
remote_update_inbound '{
|
||||
"network": "tcp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"show": False,
|
||||
"dest": "www.delfi.lv:443",
|
||||
"serverNames": ["www.delfi.lv","www.lmt.lv","www.inbox.lv","e-klase.lv"],
|
||||
"privateKey": "'"$PRIVATE_KEY"'",
|
||||
"shortIds": ["'"$SID_A"'", ""]
|
||||
},
|
||||
"tcpSettings": {"header": {"type": "none"}}
|
||||
}' "$UUID_A" "xtls-rprx-vision" "$SID_A"
|
||||
}
|
||||
|
||||
# ── Local standalone test config writers ──────────────────────────────────
|
||||
write_cfg_A() { cat > /tmp/xray-test-cfg.json << EOF
|
||||
{
|
||||
"log": {"loglevel": "error"},
|
||||
"inbounds": [{"listen":"127.0.0.1","port":$SOCKS_PORT,"protocol":"socks","settings":{"auth":"noauth","udp":true}}],
|
||||
"outbounds": [
|
||||
{"tag":"proxy","protocol":"vless",
|
||||
"settings":{"vnext":[{"address":"share.alogins.net","port":443,"users":[{"id":"$UUID_A","flow":"xtls-rprx-vision","encryption":"none"}]}]},
|
||||
"streamSettings":{
|
||||
"network":"tcp","security":"reality",
|
||||
"realitySettings":{"fingerprint":"chrome","serverName":"www.delfi.lv","publicKey":"$PUBLIC_KEY","shortId":"$SID_A","spiderX":"/"},
|
||||
"tcpSettings":{"header":{"type":"none"}}
|
||||
}
|
||||
},
|
||||
{"tag":"direct","protocol":"freedom"}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
write_cfg_B() { cat > /tmp/xray-test-cfg.json << EOF
|
||||
{
|
||||
"log": {"loglevel": "error"},
|
||||
"inbounds": [{"listen":"127.0.0.1","port":$SOCKS_PORT,"protocol":"socks","settings":{"auth":"noauth","udp":true}}],
|
||||
"outbounds": [
|
||||
{"tag":"proxy","protocol":"vless",
|
||||
"settings":{"vnext":[{"address":"share.alogins.net","port":443,"users":[{"id":"$UUID_B","flow":"","encryption":"none"}]}]},
|
||||
"streamSettings":{
|
||||
"network":"xhttp","security":"reality",
|
||||
"realitySettings":{"fingerprint":"chrome","serverName":"www.delfi.lv","publicKey":"$PUBLIC_KEY","shortId":"$SID_B","spiderX":"/"},
|
||||
"xhttpSettings":{"path":"$XHTTP_PATH","host":"","mode":"auto",
|
||||
"extra":{"xPaddingBytes":"100-1000","xmux":{"maxConcurrency":"16-32","maxConnections":0,"cMaxReuseTimes":"64-128","cMaxLifetimeMs":0,"hMaxRequestTimes":"600-900","hMaxReusableSecs":"1800-3000"}}}
|
||||
}
|
||||
},
|
||||
{"tag":"direct","protocol":"freedom"}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
write_cfg_C() { cat > /tmp/xray-test-cfg.json << EOF
|
||||
{
|
||||
"log": {"loglevel": "error"},
|
||||
"inbounds": [{"listen":"127.0.0.1","port":$SOCKS_PORT,"protocol":"socks","settings":{"auth":"noauth","udp":true}}],
|
||||
"outbounds": [
|
||||
{"tag":"proxy","protocol":"vless",
|
||||
"settings":{"vnext":[{"address":"share.alogins.net","port":443,"users":[{"id":"$UUID_C","flow":"","encryption":"none"}]}]},
|
||||
"streamSettings":{
|
||||
"network":"grpc","security":"reality",
|
||||
"realitySettings":{"fingerprint":"chrome","serverName":"www.delfi.lv","publicKey":"$PUBLIC_KEY","shortId":"$SID_C","spiderX":"/"},
|
||||
"grpcSettings":{"serviceName":"$GRPC_SVC","multiMode":true,"idle_timeout":60,"health_check_timeout":20,"initial_windows_size":65536}
|
||||
}
|
||||
},
|
||||
{"tag":"direct","protocol":"freedom"}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
write_cfg_D() { cat > /tmp/xray-test-cfg.json << EOF
|
||||
{
|
||||
"log": {"loglevel": "error"},
|
||||
"inbounds": [{"listen":"127.0.0.1","port":$SOCKS_PORT,"protocol":"socks","settings":{"auth":"noauth","udp":true}}],
|
||||
"outbounds": [
|
||||
{"tag":"proxy","protocol":"vless",
|
||||
"settings":{"vnext":[{"address":"share.alogins.net","port":443,"users":[{"id":"$UUID_D","flow":"xtls-rprx-vision","encryption":"none"}]}]},
|
||||
"streamSettings":{
|
||||
"network":"tcp","security":"reality",
|
||||
"realitySettings":{"fingerprint":"chrome","serverName":"www.delfi.lv","publicKey":"$PUBLIC_KEY","shortId":"$SID_D","spiderX":"/"},
|
||||
"tcpSettings":{"header":{"type":"none"}},
|
||||
"sockopt":{"dialerProxy":"frag-chain1"}
|
||||
}
|
||||
},
|
||||
{"tag":"frag-chain1","protocol":"freedom",
|
||||
"settings":{"fragment":{"packets":"tlshello","length":"100-200","interval":"10-20"}},
|
||||
"streamSettings":{"sockopt":{"dialerProxy":"frag-chain2"}}
|
||||
},
|
||||
{"tag":"frag-chain2","protocol":"freedom",
|
||||
"settings":{
|
||||
"fragment":{"packets":"1-3","length":"1-5","interval":"1-2"},
|
||||
"noises":[
|
||||
{"type":"rand","packet":"50-150","delay":"10-16"},
|
||||
{"type":"base64","packet":"7nQBAAABAAAAAAAABnQtcmluZwZtc2VkZ2UDbmV0AAABAAE=","delay":"10-16"}
|
||||
]
|
||||
}
|
||||
},
|
||||
{"tag":"direct","protocol":"freedom"}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
# ── Core test runner ──────────────────────────────────────────────────────
|
||||
run_test() {
|
||||
local label="$1"
|
||||
local write_fn="$2" # function name to write the config
|
||||
local update_fn="$3" # function name to update remote (or empty)
|
||||
|
||||
header "$label"
|
||||
|
||||
# Update remote if needed
|
||||
if [[ -n "$update_fn" ]]; then
|
||||
info "Updating remote inbound..."
|
||||
$update_fn
|
||||
sleep 3 # give xray time to pick up the change
|
||||
fi
|
||||
|
||||
# Write local test config
|
||||
$write_fn
|
||||
|
||||
# Validate
|
||||
if ! "$XRAY_BIN" -test -c /tmp/xray-test-cfg.json &>/dev/null; then
|
||||
fail "Config validation FAILED"
|
||||
echo "$label|INVALID|—|—|—|—|—|—" >> /tmp/results.tsv
|
||||
return
|
||||
fi
|
||||
pass "Config valid"
|
||||
|
||||
# Start test xray
|
||||
cleanup_xray
|
||||
"$XRAY_BIN" -c /tmp/xray-test-cfg.json >/tmp/xray-test.log 2>&1 &
|
||||
XRAY_PID=$!
|
||||
sleep 2
|
||||
|
||||
if ! kill -0 "$XRAY_PID" 2>/dev/null; then
|
||||
fail "Xray failed to start: $(tail -3 /tmp/xray-test.log)"
|
||||
echo "$label|FAIL-START|—|—|—|—|—|—" >> /tmp/results.tsv
|
||||
return
|
||||
fi
|
||||
pass "Xray started (PID $XRAY_PID)"
|
||||
|
||||
# Connectivity
|
||||
info "Checking connectivity..."
|
||||
local exit_ip
|
||||
exit_ip=$(curl -s --socks5-hostname 127.0.0.1:$SOCKS_PORT --max-time 15 https://api.ipify.org 2>/dev/null || echo "FAIL")
|
||||
if [[ "$exit_ip" != "$REMOTE_IP" ]]; then
|
||||
fail "Exit IP '$exit_ip' (expected $REMOTE_IP)"
|
||||
cleanup_xray
|
||||
echo "$label|FAIL-CONNECT|—|—|—|—|—|—" >> /tmp/results.tsv
|
||||
return
|
||||
fi
|
||||
pass "Exit IP: $exit_ip"
|
||||
|
||||
# Latency — 10 samples
|
||||
info "Measuring latency (10 samples)..."
|
||||
local latencies=()
|
||||
for i in $(seq 1 10); do
|
||||
local ms
|
||||
ms=$(curl -s -o /dev/null -w "%{time_total}" --socks5-hostname 127.0.0.1:$SOCKS_PORT \
|
||||
--max-time 10 https://www.google.com 2>/dev/null | awk '{printf "%d", $1*1000}')
|
||||
if [[ -n "$ms" && "$ms" -gt 0 ]]; then
|
||||
latencies+=("$ms")
|
||||
printf " [%2d/10] %s ms\n" "$i" "$ms"
|
||||
else
|
||||
printf " [%2d/10] TIMEOUT\n" "$i"
|
||||
fi
|
||||
done
|
||||
|
||||
local n=${#latencies[@]} avg_ms=0 min_ms=0 max_ms=0 p95_ms=0
|
||||
if [[ $n -gt 0 ]]; then
|
||||
local sorted=($(printf '%s\n' "${latencies[@]}" | sort -n))
|
||||
min_ms=${sorted[0]}; max_ms=${sorted[-1]}
|
||||
local sum=0; for v in "${latencies[@]}"; do sum=$((sum+v)); done
|
||||
avg_ms=$((sum/n))
|
||||
local p95_idx=$(( n * 95 / 100 )); [[ $p95_idx -ge $n ]] && p95_idx=$((n-1))
|
||||
p95_ms=${sorted[$p95_idx]}
|
||||
fi
|
||||
pass "Latency ($n/10): min=${min_ms}ms avg=${avg_ms}ms p95=${p95_ms}ms max=${max_ms}ms"
|
||||
|
||||
# Throughput
|
||||
info "Testing throughput..."
|
||||
local dl_mbps=0 ul_mbps=0
|
||||
|
||||
# Download 10MB
|
||||
local dl_out
|
||||
dl_out=$(curl -s -o /dev/null -w "%{size_download} %{time_total}" \
|
||||
--socks5-hostname 127.0.0.1:$SOCKS_PORT --max-time 30 \
|
||||
"https://speed.cloudflare.com/__down?bytes=10485760" 2>/dev/null || echo "0 0")
|
||||
local dl_bytes dl_time
|
||||
dl_bytes=$(echo "$dl_out" | awk '{print $1}')
|
||||
dl_time=$(echo "$dl_out" | awk '{print $2}')
|
||||
if [[ "${dl_bytes:-0}" -gt 1000000 ]] 2>/dev/null; then
|
||||
dl_mbps=$(echo "scale=2; $dl_bytes * 8 / $dl_time / 1000000" | bc)
|
||||
pass "Download: ${dl_mbps} Mbps (${dl_bytes} bytes in ${dl_time}s)"
|
||||
else
|
||||
fail "Download failed (bytes=${dl_bytes:-0})"
|
||||
fi
|
||||
|
||||
# Upload 5MB
|
||||
local ul_out
|
||||
ul_out=$(dd if=/dev/urandom bs=1M count=5 2>/dev/null | curl -s -o /dev/null -w "%{size_upload} %{time_total}" \
|
||||
--socks5-hostname 127.0.0.1:$SOCKS_PORT --max-time 30 \
|
||||
-X POST --data-binary @- https://httpbin.org/post 2>/dev/null || echo "0 0")
|
||||
local ul_bytes ul_time
|
||||
ul_bytes=$(echo "$ul_out" | awk '{print $1}')
|
||||
ul_time=$(echo "$ul_out" | awk '{print $2}')
|
||||
if [[ "${ul_bytes:-0}" -gt 100000 ]] 2>/dev/null; then
|
||||
ul_mbps=$(echo "scale=2; $ul_bytes * 8 / $ul_time / 1000000" | bc)
|
||||
pass "Upload: ${ul_mbps} Mbps (${ul_bytes} bytes in ${ul_time}s)"
|
||||
else
|
||||
fail "Upload failed"
|
||||
ul_mbps=0
|
||||
fi
|
||||
|
||||
echo "$label|OK|$avg_ms|$min_ms|$p95_ms|$max_ms|$dl_mbps|$ul_mbps" >> /tmp/results.tsv
|
||||
cleanup_xray
|
||||
}
|
||||
|
||||
# ── Remote update functions for each config ───────────────────────────────
|
||||
update_remote_B() {
|
||||
source "$VENV/bin/activate"
|
||||
python3 << PYEOF
|
||||
import requests, json
|
||||
import urllib3; urllib3.disable_warnings()
|
||||
s = requests.Session(); s.verify = False
|
||||
BASE = "https://share.alogins.net:16627/gBdsRLtVZdgZ63wmVR"
|
||||
s.post(f"{BASE}/login", data={"username": "xrayadmin", "password": "Admin2026!"})
|
||||
stream = {
|
||||
"network": "xhttp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"show": False, "dest": "$DEST",
|
||||
"serverNames": ["www.delfi.lv","www.lmt.lv","www.inbox.lv","e-klase.lv"],
|
||||
"privateKey": "$PRIVATE_KEY",
|
||||
"shortIds": ["$SID_B", ""]
|
||||
},
|
||||
"xhttpSettings": {
|
||||
"path": "$XHTTP_PATH", "host": "", "mode": "auto",
|
||||
"extra": {"xPaddingBytes": "100-1000", "xmux": {"maxConcurrency": "16-32", "maxConnections": 0,
|
||||
"cMaxReuseTimes": "64-128", "cMaxLifetimeMs": 0,
|
||||
"hMaxRequestTimes": "600-900", "hMaxReusableSecs": "1800-3000"}}
|
||||
}
|
||||
}
|
||||
client = {"id": "$UUID_B", "flow": "", "email": "test-b",
|
||||
"limitIp": 0, "totalGB": 0, "expiryTime": 0, "enable": True, "tgId": "", "subId": "", "comment": ""}
|
||||
payload = {"id": 1, "tag": "inbound-443", "enable": True, "port": 443, "listen": "", "protocol": "vless",
|
||||
"settings": json.dumps({"clients": [client], "decryption": "none", "fallbacks": []}),
|
||||
"streamSettings": json.dumps(stream),
|
||||
"sniffing": json.dumps({"enabled": False}), "remark": "inbound-443", "expiryTime": 0}
|
||||
r = s.post(f"{BASE}/panel/api/inbounds/update/1", json=payload)
|
||||
print(" Remote updated (XHTTP):", r.json().get("success"), r.json().get("msg",""))
|
||||
PYEOF
|
||||
}
|
||||
|
||||
update_remote_C() {
|
||||
source "$VENV/bin/activate"
|
||||
python3 << PYEOF
|
||||
import requests, json
|
||||
import urllib3; urllib3.disable_warnings()
|
||||
s = requests.Session(); s.verify = False
|
||||
BASE = "https://share.alogins.net:16627/gBdsRLtVZdgZ63wmVR"
|
||||
s.post(f"{BASE}/login", data={"username": "xrayadmin", "password": "Admin2026!"})
|
||||
stream = {
|
||||
"network": "grpc",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"show": False, "dest": "$DEST",
|
||||
"serverNames": ["www.delfi.lv","www.lmt.lv","www.inbox.lv","e-klase.lv"],
|
||||
"privateKey": "$PRIVATE_KEY",
|
||||
"shortIds": ["$SID_C", ""]
|
||||
},
|
||||
"grpcSettings": {"serviceName": "$GRPC_SVC", "multiMode": True,
|
||||
"idle_timeout": 60, "health_check_timeout": 20, "initial_windows_size": 65536}
|
||||
}
|
||||
client = {"id": "$UUID_C", "flow": "", "email": "test-c",
|
||||
"limitIp": 0, "totalGB": 0, "expiryTime": 0, "enable": True, "tgId": "", "subId": "", "comment": ""}
|
||||
payload = {"id": 1, "tag": "inbound-443", "enable": True, "port": 443, "listen": "", "protocol": "vless",
|
||||
"settings": json.dumps({"clients": [client], "decryption": "none", "fallbacks": []}),
|
||||
"streamSettings": json.dumps(stream),
|
||||
"sniffing": json.dumps({"enabled": False}), "remark": "inbound-443", "expiryTime": 0}
|
||||
r = s.post(f"{BASE}/panel/api/inbounds/update/1", json=payload)
|
||||
print(" Remote updated (gRPC):", r.json().get("success"), r.json().get("msg",""))
|
||||
PYEOF
|
||||
}
|
||||
|
||||
update_remote_D() {
|
||||
# Config D uses same server as A but adds UUID_D as client
|
||||
source "$VENV/bin/activate"
|
||||
python3 << PYEOF
|
||||
import requests, json
|
||||
import urllib3; urllib3.disable_warnings()
|
||||
s = requests.Session(); s.verify = False
|
||||
BASE = "https://share.alogins.net:16627/gBdsRLtVZdgZ63wmVR"
|
||||
s.post(f"{BASE}/login", data={"username": "xrayadmin", "password": "Admin2026!"})
|
||||
stream = {
|
||||
"network": "tcp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"show": False, "dest": "$DEST",
|
||||
"serverNames": ["www.delfi.lv","www.lmt.lv","www.inbox.lv","e-klase.lv"],
|
||||
"privateKey": "$PRIVATE_KEY",
|
||||
"shortIds": ["$SID_D", ""]
|
||||
},
|
||||
"tcpSettings": {"header": {"type": "none"}}
|
||||
}
|
||||
client = {"id": "$UUID_D", "flow": "xtls-rprx-vision", "email": "test-d",
|
||||
"limitIp": 0, "totalGB": 0, "expiryTime": 0, "enable": True, "tgId": "", "subId": "", "comment": ""}
|
||||
payload = {"id": 1, "tag": "inbound-443", "enable": True, "port": 443, "listen": "", "protocol": "vless",
|
||||
"settings": json.dumps({"clients": [client], "decryption": "none", "fallbacks": []}),
|
||||
"streamSettings": json.dumps(stream),
|
||||
"sniffing": json.dumps({"enabled": False}), "remark": "inbound-443", "expiryTime": 0}
|
||||
r = s.post(f"{BASE}/panel/api/inbounds/update/1", json=payload)
|
||||
print(" Remote updated (TCP+Fragment):", r.json().get("success"), r.json().get("msg",""))
|
||||
PYEOF
|
||||
}
|
||||
|
||||
restore_A() {
|
||||
source "$VENV/bin/activate"
|
||||
python3 << PYEOF
|
||||
import requests, json
|
||||
import urllib3; urllib3.disable_warnings()
|
||||
s = requests.Session(); s.verify = False
|
||||
BASE = "https://share.alogins.net:16627/gBdsRLtVZdgZ63wmVR"
|
||||
s.post(f"{BASE}/login", data={"username": "xrayadmin", "password": "Admin2026!"})
|
||||
stream = {
|
||||
"network": "tcp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"show": False, "dest": "$DEST",
|
||||
"serverNames": ["www.delfi.lv","www.lmt.lv","www.inbox.lv","e-klase.lv"],
|
||||
"privateKey": "$PRIVATE_KEY",
|
||||
"shortIds": ["$SID_A", ""]
|
||||
},
|
||||
"tcpSettings": {"header": {"type": "none"}}
|
||||
}
|
||||
client = {"id": "$UUID_A", "flow": "xtls-rprx-vision", "email": "local-outbound",
|
||||
"limitIp": 0, "totalGB": 0, "expiryTime": 0, "enable": True, "tgId": "", "subId": "", "comment": ""}
|
||||
payload = {"id": 1, "tag": "inbound-443", "enable": True, "port": 443, "listen": "", "protocol": "vless",
|
||||
"settings": json.dumps({"clients": [client], "decryption": "none", "fallbacks": []}),
|
||||
"streamSettings": json.dumps(stream),
|
||||
"sniffing": json.dumps({"enabled": False}), "remark": "inbound-443", "expiryTime": 0}
|
||||
r = s.post(f"{BASE}/panel/api/inbounds/update/1", json=payload)
|
||||
print(" Restored baseline (TCP+Vision):", r.json().get("success"))
|
||||
PYEOF
|
||||
}
|
||||
|
||||
# ── Run all tests ─────────────────────────────────────────────────────────
|
||||
echo "Config|Status|Avg_ms|Min_ms|P95_ms|Max_ms|DL_Mbps|UL_Mbps" > /tmp/results.tsv
|
||||
|
||||
# Ensure we start with baseline on remote
|
||||
info "Ensuring baseline is active on remote..."
|
||||
restore_A
|
||||
|
||||
run_test "A: TCP+Reality+Vision (baseline)" write_cfg_A ""
|
||||
run_test "B: XHTTP+Reality" write_cfg_B update_remote_B
|
||||
restore_A; sleep 2
|
||||
run_test "C: gRPC+Reality" write_cfg_C update_remote_C
|
||||
restore_A; sleep 2
|
||||
run_test "D: TCP+Reality+Vision+Fragment+Noise" write_cfg_D update_remote_D
|
||||
|
||||
# Restore baseline permanently
|
||||
header "Restoring baseline on remote"
|
||||
restore_A
|
||||
pass "Baseline restored"
|
||||
|
||||
# ── Summary ───────────────────────────────────────────────────────────────
|
||||
header "RESULTS SUMMARY"
|
||||
printf "\n%-42s %-12s %-9s %-9s %-9s %-9s\n" "Configuration" "Status" "Avg ms" "DL Mbps" "UL Mbps" "P95 ms"
|
||||
printf "%-42s %-12s %-9s %-9s %-9s %-9s\n" "──────────────────────────────────────────" "──────────" "───────" "───────" "───────" "───────"
|
||||
while IFS='|' read -r name status avg min p95 max dl ul; do
|
||||
[[ "$name" == "Config" ]] && continue
|
||||
if [[ "$status" == "OK" ]]; then
|
||||
printf "${GREEN}%-42s${NC} %-12s %-9s %-9s %-9s %-9s\n" "$name" "$status" "${avg}ms" "$dl" "$ul" "${p95}ms"
|
||||
else
|
||||
printf "${RED}%-42s${NC} %-12s\n" "$name" "$status"
|
||||
fi
|
||||
done < /tmp/results.tsv
|
||||
|
||||
# ── Write markdown ────────────────────────────────────────────────────────
|
||||
{
|
||||
cat << MDEOF
|
||||
# Xray Configuration Comparison — Port 443
|
||||
|
||||
**Date**: $(date '+%Y-%m-%d %H:%M:%S')
|
||||
**Local xray**: 25.10.15 | **Remote xray**: 26.2.6
|
||||
**Remote**: share.alogins.net (83.99.190.32), LXD container "xray"
|
||||
|
||||
## Results
|
||||
|
||||
| Configuration | Status | Avg Latency | P95 Latency | Download | Upload |
|
||||
|---------------|--------|-------------|-------------|----------|--------|
|
||||
MDEOF
|
||||
while IFS='|' read -r name status avg min p95 max dl ul; do
|
||||
[[ "$name" == "Config" ]] && continue
|
||||
if [[ "$status" == "OK" ]]; then
|
||||
echo "| $name | ✓ OK | ${avg}ms | ${p95}ms | ${dl} Mbps | ${ul} Mbps |"
|
||||
else
|
||||
echo "| $name | ✗ $status | — | — | — | — |"
|
||||
fi
|
||||
done < /tmp/results.tsv
|
||||
cat << 'MDEOF'
|
||||
|
||||
## Configuration Descriptions
|
||||
|
||||
| Config | Transport | Port | Flow | DPI Target |
|
||||
|--------|-----------|------|------|------------|
|
||||
| A | TCP + Reality | 443 | xtls-rprx-vision | Baseline — standard Russia anti-DPI |
|
||||
| B | XHTTP + Reality | 443 | none | Volume-based TCP freezing (split uploads, XMUX padding) |
|
||||
| C | gRPC + Reality | 443 | none | H2 pattern analysis (looks like enterprise API traffic) |
|
||||
| D | TCP + Reality + Fragment/Noise | 443 | xtls-rprx-vision | TLS ClientHello DPI signature (fragment chains + noise) |
|
||||
|
||||
## Methodology
|
||||
- Each config tested sequentially on the **same port 443**
|
||||
- Remote inbound swapped on-the-fly via 3x-ui API
|
||||
- 10 latency samples to google.com
|
||||
- 10 MB download from Cloudflare speed test
|
||||
- 5 MB upload to httpbin.org
|
||||
MDEOF
|
||||
} > "$RESULTS_FILE"
|
||||
|
||||
echo ""
|
||||
echo -e "${BOLD}Results saved: $RESULTS_FILE${NC}"
|
||||
334
test_sni_configs.sh
Executable file
334
test_sni_configs.sh
Executable file
@@ -0,0 +1,334 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# test_sni_configs.sh — Test multiple SNI configurations for juris-reality
|
||||
# Runs test_xray_connection.sh for each SNI and collects results
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
XRAY_BIN="/usr/local/x-ui/bin/xray-linux-amd64"
|
||||
SOCKS_PORT=11080
|
||||
REMOTE_IP="83.99.190.32"
|
||||
RESULTS_FILE="/home/alvis/ai-xray/sni_test_results.md"
|
||||
CONFIGS_DIR="/home/alvis/ai-xray/configs"
|
||||
|
||||
mkdir -p "$CONFIGS_DIR"
|
||||
|
||||
# SNI configurations to test
|
||||
declare -A SNIS
|
||||
SNIS["www.delfi.lv"]="Latvia news #1"
|
||||
SNIS["www.lmt.lv"]="Latvia telecom #1"
|
||||
SNIS["www.tele2.lv"]="Latvia telecom #2"
|
||||
SNIS["www.lsm.lv"]="Latvia public broadcasting"
|
||||
SNIS["www.inbox.lv"]="Latvia webmail"
|
||||
SNIS["e-klase.lv"]="Latvia education"
|
||||
SNIS["share.alogins.net"]="Custom domain"
|
||||
SNIS["www.microsoft.com"]="Worldwide baseline"
|
||||
|
||||
# Ordered list for consistent iteration
|
||||
SNI_ORDER=(
|
||||
"www.delfi.lv"
|
||||
"www.lmt.lv"
|
||||
"www.tele2.lv"
|
||||
"www.lsm.lv"
|
||||
"www.inbox.lv"
|
||||
"e-klase.lv"
|
||||
"share.alogins.net"
|
||||
"www.microsoft.com"
|
||||
)
|
||||
|
||||
generate_config() {
|
||||
local sni="$1"
|
||||
local file="$2"
|
||||
cat > "$file" << JSONEOF
|
||||
{
|
||||
"log": { "loglevel": "error" },
|
||||
"inbounds": [{
|
||||
"listen": "127.0.0.1",
|
||||
"port": $SOCKS_PORT,
|
||||
"protocol": "socks",
|
||||
"settings": { "auth": "noauth", "udp": true },
|
||||
"tag": "test-socks"
|
||||
}],
|
||||
"outbounds": [{
|
||||
"tag": "juris-reality",
|
||||
"protocol": "vless",
|
||||
"settings": {
|
||||
"vnext": [{
|
||||
"address": "share.alogins.net",
|
||||
"port": 443,
|
||||
"users": [{
|
||||
"id": "64522a14-54aa-4b3c-8071-8c8b17aa1f08",
|
||||
"flow": "xtls-rprx-vision",
|
||||
"encryption": "none"
|
||||
}]
|
||||
}]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"fingerprint": "chrome",
|
||||
"serverName": "$sni",
|
||||
"publicKey": "58Iqd6LuWXgvjAgo92-7KURhTp0Vj79yGF81l_iuvTw",
|
||||
"shortId": "48b4c16249ad44ff",
|
||||
"spiderX": "/"
|
||||
},
|
||||
"tcpSettings": { "header": { "type": "none" } }
|
||||
}
|
||||
}]
|
||||
}
|
||||
JSONEOF
|
||||
}
|
||||
|
||||
run_test() {
|
||||
local sni="$1"
|
||||
local config="$2"
|
||||
local xray_pid=""
|
||||
|
||||
# Start xray
|
||||
$XRAY_BIN -config "$config" > /dev/null 2>&1 &
|
||||
xray_pid=$!
|
||||
sleep 2
|
||||
|
||||
if ! kill -0 "$xray_pid" 2>/dev/null; then
|
||||
echo "XRAY_FAIL"
|
||||
return
|
||||
fi
|
||||
|
||||
local proxy="socks5h://127.0.0.1:$SOCKS_PORT"
|
||||
|
||||
# 1. Connectivity
|
||||
local exit_ip
|
||||
exit_ip=$(curl -s --connect-timeout 10 --max-time 15 -x "$proxy" https://ifconfig.me 2>/dev/null || echo "FAIL")
|
||||
|
||||
if [[ "$exit_ip" != "$REMOTE_IP" ]]; then
|
||||
kill "$xray_pid" 2>/dev/null; wait "$xray_pid" 2>/dev/null || true
|
||||
echo "CONN_FAIL|$exit_ip|0|0|0|0|0|0"
|
||||
return
|
||||
fi
|
||||
|
||||
# 2. Latency — 10 samples
|
||||
local latencies=()
|
||||
for i in $(seq 1 10); do
|
||||
local t
|
||||
t=$(curl -s -o /dev/null -w '%{time_total}' --connect-timeout 10 --max-time 15 -x "$proxy" https://www.gstatic.com/generate_204 2>/dev/null || echo "0")
|
||||
local ms
|
||||
ms=$(echo "$t * 1000" | bc 2>/dev/null | cut -d. -f1)
|
||||
if [[ -n "$ms" && "$ms" -gt 0 ]]; then
|
||||
latencies+=("$ms")
|
||||
fi
|
||||
done
|
||||
|
||||
local lat_min=0 lat_avg=0 lat_p95=0 lat_max=0
|
||||
if [[ ${#latencies[@]} -gt 0 ]]; then
|
||||
local sorted
|
||||
sorted=($(printf '%s\n' "${latencies[@]}" | sort -n))
|
||||
local cnt=${#sorted[@]}
|
||||
lat_min=${sorted[0]}
|
||||
lat_max=${sorted[$((cnt - 1))]}
|
||||
local sum=0
|
||||
for v in "${sorted[@]}"; do sum=$((sum + v)); done
|
||||
lat_avg=$((sum / cnt))
|
||||
local p95_idx
|
||||
p95_idx=$(echo "($cnt * 95 + 99) / 100 - 1" | bc)
|
||||
[[ $p95_idx -ge $cnt ]] && p95_idx=$((cnt - 1))
|
||||
lat_p95=${sorted[$p95_idx]}
|
||||
fi
|
||||
|
||||
# 3. Throughput — download 10MB
|
||||
local dl_result
|
||||
dl_result=$(curl -s -o /dev/null -w '%{size_download} %{time_total} %{speed_download}' \
|
||||
--connect-timeout 15 --max-time 60 \
|
||||
-x "$proxy" "http://speedtest.tele2.net/10MB.zip" 2>/dev/null || echo "0 0 0")
|
||||
local dl_bytes dl_speed
|
||||
dl_bytes=$(echo "$dl_result" | awk '{print $1}')
|
||||
dl_speed=$(echo "$dl_result" | awk '{print $3}')
|
||||
local dl_mbps="0"
|
||||
if [[ "$dl_bytes" -gt 0 ]] 2>/dev/null; then
|
||||
dl_mbps=$(echo "scale=2; $dl_speed * 8 / 1048576" | bc)
|
||||
fi
|
||||
|
||||
# 3b. Throughput — upload 5MB
|
||||
local ul_result
|
||||
ul_result=$(dd if=/dev/urandom bs=1M count=5 2>/dev/null | \
|
||||
curl -s -o /dev/null -w '%{size_upload} %{time_total} %{speed_upload}' \
|
||||
--connect-timeout 15 --max-time 60 \
|
||||
-x "$proxy" -X POST -H "Content-Type: application/octet-stream" \
|
||||
--data-binary @- "http://speedtest.tele2.net/upload.php" 2>/dev/null || echo "0 0 0")
|
||||
local ul_bytes ul_speed
|
||||
ul_bytes=$(echo "$ul_result" | awk '{print $1}')
|
||||
ul_speed=$(echo "$ul_result" | awk '{print $3}')
|
||||
local ul_mbps="0"
|
||||
if [[ "$ul_bytes" -gt 0 ]] 2>/dev/null; then
|
||||
ul_mbps=$(echo "scale=2; $ul_speed * 8 / 1048576" | bc)
|
||||
fi
|
||||
|
||||
kill "$xray_pid" 2>/dev/null; wait "$xray_pid" 2>/dev/null || true
|
||||
|
||||
echo "OK|$exit_ip|$lat_min|$lat_avg|$lat_p95|$lat_max|$dl_mbps|$ul_mbps"
|
||||
}
|
||||
|
||||
# ── Main ──────────────────────────────────────────────────────────────────────
|
||||
echo "============================================"
|
||||
echo " SNI Configuration Benchmark"
|
||||
echo " $(date -u '+%Y-%m-%d %H:%M UTC')"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
|
||||
declare -A RESULTS
|
||||
|
||||
for sni in "${SNI_ORDER[@]}"; do
|
||||
desc="${SNIS[$sni]}"
|
||||
config="$CONFIGS_DIR/${sni//[^a-zA-Z0-9._-]/_}.json"
|
||||
|
||||
echo "[$sni] ($desc)"
|
||||
echo -n " Generating config... "
|
||||
generate_config "$sni" "$config"
|
||||
echo "done"
|
||||
echo -n " Running tests... "
|
||||
|
||||
result=$(run_test "$sni" "$config")
|
||||
RESULTS["$sni"]="$result"
|
||||
|
||||
status=$(echo "$result" | cut -d'|' -f1)
|
||||
if [[ "$status" == "OK" ]]; then
|
||||
avg=$(echo "$result" | cut -d'|' -f4)
|
||||
dl=$(echo "$result" | cut -d'|' -f7)
|
||||
ul=$(echo "$result" | cut -d'|' -f8)
|
||||
echo "OK (avg=${avg}ms, dl=${dl}Mbps, ul=${ul}Mbps)"
|
||||
else
|
||||
echo "FAILED ($status)"
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
|
||||
# ── Write results ─────────────────────────────────────────────────────────────
|
||||
{
|
||||
echo "# SNI Configuration Test Results"
|
||||
echo ""
|
||||
echo "Date: $(date -u '+%Y-%m-%d %H:%M UTC')"
|
||||
echo "Server: share.alogins.net (83.99.190.32)"
|
||||
echo "Protocol: VLESS + XTLS-Reality + Vision"
|
||||
echo "Reality dest: www.delfi.lv:443"
|
||||
echo ""
|
||||
echo "## Results"
|
||||
echo ""
|
||||
echo "| # | SNI | Type | Conn | Latency Min | Latency Avg | Latency P95 | Latency Max | Download | Upload |"
|
||||
echo "|---|-----|------|------|-------------|-------------|-------------|-------------|----------|--------|"
|
||||
|
||||
idx=0
|
||||
best_sni=""
|
||||
best_score=999999
|
||||
for sni in "${SNI_ORDER[@]}"; do
|
||||
idx=$((idx + 1))
|
||||
desc="${SNIS[$sni]}"
|
||||
result="${RESULTS[$sni]}"
|
||||
status=$(echo "$result" | cut -d'|' -f1)
|
||||
|
||||
if [[ "$status" == "OK" ]]; then
|
||||
ip=$(echo "$result" | cut -d'|' -f2)
|
||||
lat_min=$(echo "$result" | cut -d'|' -f3)
|
||||
lat_avg=$(echo "$result" | cut -d'|' -f4)
|
||||
lat_p95=$(echo "$result" | cut -d'|' -f5)
|
||||
lat_max=$(echo "$result" | cut -d'|' -f6)
|
||||
dl=$(echo "$result" | cut -d'|' -f7)
|
||||
ul=$(echo "$result" | cut -d'|' -f8)
|
||||
echo "| $idx | \`$sni\` | $desc | OK | ${lat_min}ms | ${lat_avg}ms | ${lat_p95}ms | ${lat_max}ms | ${dl} Mbps | ${ul} Mbps |"
|
||||
|
||||
# Score: lower avg latency + higher throughput = better
|
||||
# score = avg_latency - (dl_mbps + ul_mbps) (lower is better)
|
||||
score=$(echo "$lat_avg" | bc)
|
||||
if [[ $score -lt $best_score ]]; then
|
||||
best_score=$score
|
||||
best_sni=$sni
|
||||
fi
|
||||
else
|
||||
echo "| $idx | \`$sni\` | $desc | FAIL | — | — | — | — | — | — |"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "## Best Configuration"
|
||||
echo ""
|
||||
if [[ -n "$best_sni" ]]; then
|
||||
result="${RESULTS[$best_sni]}"
|
||||
lat_avg=$(echo "$result" | cut -d'|' -f4)
|
||||
dl=$(echo "$result" | cut -d'|' -f7)
|
||||
ul=$(echo "$result" | cut -d'|' -f8)
|
||||
echo "**Winner: \`$best_sni\`** (${SNIS[$best_sni]})"
|
||||
echo ""
|
||||
echo "- Average latency: ${lat_avg}ms"
|
||||
echo "- Download: ${dl} Mbps"
|
||||
echo "- Upload: ${ul} Mbps"
|
||||
echo ""
|
||||
echo "### Recommended client outbound config"
|
||||
echo ""
|
||||
echo '```json'
|
||||
echo "{"
|
||||
echo ' "tag": "juris-reality",'
|
||||
echo ' "protocol": "vless",'
|
||||
echo ' "settings": {'
|
||||
echo ' "vnext": [{'
|
||||
echo ' "address": "share.alogins.net",'
|
||||
echo ' "port": 443,'
|
||||
echo ' "users": [{'
|
||||
echo ' "id": "64522a14-54aa-4b3c-8071-8c8b17aa1f08",'
|
||||
echo ' "flow": "xtls-rprx-vision",'
|
||||
echo ' "encryption": "none"'
|
||||
echo ' }]'
|
||||
echo ' }]'
|
||||
echo ' },'
|
||||
echo ' "streamSettings": {'
|
||||
echo ' "network": "tcp",'
|
||||
echo ' "security": "reality",'
|
||||
echo ' "realitySettings": {'
|
||||
echo ' "fingerprint": "chrome",'
|
||||
echo " \"serverName\": \"$best_sni\","
|
||||
echo ' "publicKey": "58Iqd6LuWXgvjAgo92-7KURhTp0Vj79yGF81l_iuvTw",'
|
||||
echo ' "shortId": "48b4c16249ad44ff",'
|
||||
echo ' "spiderX": "/"'
|
||||
echo ' },'
|
||||
echo ' "tcpSettings": { "header": { "type": "none" } }'
|
||||
echo ' }'
|
||||
echo "}"
|
||||
echo '```'
|
||||
else
|
||||
echo "No configuration passed all tests."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "## All Configurations"
|
||||
echo ""
|
||||
echo "Individual config files are stored in \`configs/\` directory."
|
||||
echo ""
|
||||
for sni in "${SNI_ORDER[@]}"; do
|
||||
config_name="${sni//[^a-zA-Z0-9._-]/_}.json"
|
||||
echo "- \`configs/$config_name\` — SNI: \`$sni\`"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "## Server-Side Reality Settings"
|
||||
echo ""
|
||||
echo '```json'
|
||||
echo '"realitySettings": {'
|
||||
echo ' "dest": "www.delfi.lv:443",'
|
||||
echo ' "serverNames": ['
|
||||
echo ' "www.delfi.lv",'
|
||||
echo ' "www.lmt.lv",'
|
||||
echo ' "www.tele2.lv",'
|
||||
echo ' "www.lsm.lv",'
|
||||
echo ' "www.inbox.lv",'
|
||||
echo ' "e-klase.lv",'
|
||||
echo ' "share.alogins.net",'
|
||||
echo ' "www.microsoft.com"'
|
||||
echo ' ],'
|
||||
echo ' "privateKey": "KJfhenZvJV1kXwv4kDC8NPBtMUY0RR8lFrxsxfXfFmY",'
|
||||
echo ' "shortIds": ["48b4c16249ad44ff", ""]'
|
||||
echo '}'
|
||||
echo '```'
|
||||
} > "$RESULTS_FILE"
|
||||
|
||||
echo "============================================"
|
||||
echo "Results written to: $RESULTS_FILE"
|
||||
echo "============================================"
|
||||
263
test_xray_connection.sh
Executable file
263
test_xray_connection.sh
Executable file
@@ -0,0 +1,263 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# test_xray_connection.sh — Test xray juris-xhttp outbound
|
||||
# Tests: connectivity, latency (SLA), throughput
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
XRAY_BIN="/usr/local/x-ui/bin/xray-linux-amd64"
|
||||
TEST_CONFIG="/tmp/test-juris-sla.json"
|
||||
SOCKS_PORT=11080
|
||||
REMOTE_IP="83.99.190.32"
|
||||
XRAY_PID=""
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "$XRAY_PID" ]] && kill -0 "$XRAY_PID" 2>/dev/null; then
|
||||
kill "$XRAY_PID" 2>/dev/null
|
||||
wait "$XRAY_PID" 2>/dev/null || true
|
||||
fi
|
||||
rm -f "$TEST_CONFIG"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
header() {
|
||||
echo ""
|
||||
echo -e "${BOLD}${CYAN}=== $1 ===${NC}"
|
||||
}
|
||||
|
||||
pass() { echo -e " ${GREEN}PASS${NC} $1"; }
|
||||
fail() { echo -e " ${RED}FAIL${NC} $1"; }
|
||||
info() { echo -e " ${YELLOW}INFO${NC} $1"; }
|
||||
|
||||
# ── Write temporary xray config ──────────────────────────────────────────────
|
||||
cat > "$TEST_CONFIG" << 'EOF'
|
||||
{
|
||||
"log": { "loglevel": "error" },
|
||||
"inbounds": [{
|
||||
"listen": "127.0.0.1",
|
||||
"port": 11080,
|
||||
"protocol": "socks",
|
||||
"settings": { "auth": "noauth", "udp": true },
|
||||
"tag": "test-socks"
|
||||
}],
|
||||
"outbounds": [{
|
||||
"tag": "juris-xhttp",
|
||||
"protocol": "vless",
|
||||
"settings": {
|
||||
"vnext": [{
|
||||
"address": "share.alogins.net",
|
||||
"port": 443,
|
||||
"users": [{
|
||||
"id": "6e422ab5-070a-43f6-8241-38cd56d23d24",
|
||||
"flow": "",
|
||||
"encryption": "none"
|
||||
}]
|
||||
}]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "xhttp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"fingerprint": "chrome",
|
||||
"serverName": "www.delfi.lv",
|
||||
"publicKey": "58Iqd6LuWXgvjAgo92-7KURhTp0Vj79yGF81l_iuvTw",
|
||||
"shortId": "6036d37d12c443c4",
|
||||
"spiderX": "/"
|
||||
},
|
||||
"xhttpSettings": {
|
||||
"path": "/xt-6036d37d",
|
||||
"host": "",
|
||||
"mode": "auto",
|
||||
"extra": {
|
||||
"xPaddingBytes": "100-1000",
|
||||
"xmux": {
|
||||
"maxConcurrency": "16-32",
|
||||
"maxConnections": 0,
|
||||
"cMaxReuseTimes": "64-128",
|
||||
"cMaxLifetimeMs": 0,
|
||||
"hMaxRequestTimes": "600-900",
|
||||
"hMaxReusableSecs": "1800-3000"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
EOF
|
||||
|
||||
# ── Start xray ───────────────────────────────────────────────────────────────
|
||||
header "Starting test xray instance (SOCKS on 127.0.0.1:$SOCKS_PORT)"
|
||||
|
||||
$XRAY_BIN -test -config "$TEST_CONFIG" > /dev/null 2>&1
|
||||
pass "Config validation OK"
|
||||
|
||||
$XRAY_BIN -config "$TEST_CONFIG" > /dev/null 2>&1 &
|
||||
XRAY_PID=$!
|
||||
sleep 2
|
||||
|
||||
if kill -0 "$XRAY_PID" 2>/dev/null; then
|
||||
pass "Xray started (PID $XRAY_PID)"
|
||||
else
|
||||
fail "Xray failed to start"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── 1. Connectivity test ─────────────────────────────────────────────────────
|
||||
header "1. Connectivity"
|
||||
|
||||
PROXY="socks5h://127.0.0.1:$SOCKS_PORT"
|
||||
ERRORS=0
|
||||
|
||||
# Test: exit IP
|
||||
EXIT_IP=$(curl -s --connect-timeout 10 --max-time 15 -x "$PROXY" https://ifconfig.me 2>/dev/null || echo "FAIL")
|
||||
if [[ "$EXIT_IP" == "$REMOTE_IP" ]]; then
|
||||
pass "Exit IP: $EXIT_IP (matches remote server)"
|
||||
else
|
||||
fail "Exit IP: '$EXIT_IP' (expected $REMOTE_IP)"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
|
||||
# Test: HTTPS connectivity
|
||||
HTTP_CODE=$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout 10 --max-time 15 -x "$PROXY" https://www.google.com 2>/dev/null || echo "000")
|
||||
if [[ "$HTTP_CODE" == "200" ]]; then
|
||||
pass "HTTPS to google.com: HTTP $HTTP_CODE"
|
||||
else
|
||||
fail "HTTPS to google.com: HTTP $HTTP_CODE"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
|
||||
# Test: DNS resolution through tunnel
|
||||
DNS_IP=$(curl -s --connect-timeout 10 --max-time 15 -x "$PROXY" https://dns.google/resolve?name=example.com\&type=A 2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin)['Answer'][0]['data'])" 2>/dev/null || echo "FAIL")
|
||||
if [[ "$DNS_IP" != "FAIL" ]]; then
|
||||
pass "DNS resolution through tunnel: example.com → $DNS_IP"
|
||||
else
|
||||
fail "DNS resolution through tunnel"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
|
||||
# ── 2. Latency (SLA) ─────────────────────────────────────────────────────────
|
||||
header "2. Latency (SLA)"
|
||||
|
||||
LATENCIES=()
|
||||
LATENCY_ERRORS=0
|
||||
SAMPLES=10
|
||||
|
||||
info "Running $SAMPLES requests to measure round-trip time..."
|
||||
|
||||
for i in $(seq 1 $SAMPLES); do
|
||||
T=$(curl -s -o /dev/null -w '%{time_total}' --connect-timeout 10 --max-time 15 -x "$PROXY" https://www.gstatic.com/generate_204 2>/dev/null || echo "0")
|
||||
MS=$(echo "$T * 1000" | bc 2>/dev/null | cut -d. -f1)
|
||||
if [[ -n "$MS" && "$MS" -gt 0 ]]; then
|
||||
LATENCIES+=("$MS")
|
||||
printf " [%2d/%d] %s ms\n" "$i" "$SAMPLES" "$MS"
|
||||
else
|
||||
LATENCY_ERRORS=$((LATENCY_ERRORS + 1))
|
||||
printf " [%2d/%d] ${RED}timeout${NC}\n" "$i" "$SAMPLES"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#LATENCIES[@]} -gt 0 ]]; then
|
||||
# Calculate min/avg/max/p95
|
||||
SORTED=($(printf '%s\n' "${LATENCIES[@]}" | sort -n))
|
||||
COUNT=${#SORTED[@]}
|
||||
MIN=${SORTED[0]}
|
||||
MAX=${SORTED[$((COUNT - 1))]}
|
||||
SUM=0
|
||||
for v in "${SORTED[@]}"; do SUM=$((SUM + v)); done
|
||||
AVG=$((SUM / COUNT))
|
||||
P95_IDX=$(echo "($COUNT * 95 + 99) / 100 - 1" | bc)
|
||||
[[ $P95_IDX -ge $COUNT ]] && P95_IDX=$((COUNT - 1))
|
||||
P95=${SORTED[$P95_IDX]}
|
||||
|
||||
echo ""
|
||||
info "Latency summary ($COUNT/$SAMPLES successful):"
|
||||
echo -e " ${BOLD} Min: ${MIN} ms${NC}"
|
||||
echo -e " ${BOLD} Avg: ${AVG} ms${NC}"
|
||||
echo -e " ${BOLD} P95: ${P95} ms${NC}"
|
||||
echo -e " ${BOLD} Max: ${MAX} ms${NC}"
|
||||
|
||||
if [[ $AVG -lt 300 ]]; then
|
||||
pass "Average latency ${AVG}ms < 300ms threshold"
|
||||
elif [[ $AVG -lt 500 ]]; then
|
||||
info "Average latency ${AVG}ms — acceptable but elevated"
|
||||
else
|
||||
fail "Average latency ${AVG}ms exceeds 500ms"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
else
|
||||
fail "All latency samples failed"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
|
||||
# ── 3. Throughput ─────────────────────────────────────────────────────────────
|
||||
header "3. Throughput"
|
||||
|
||||
info "Downloading 10MB test file through tunnel..."
|
||||
|
||||
DL_RESULT=$(curl -s -o /dev/null -w '%{size_download} %{time_total} %{speed_download}' \
|
||||
--connect-timeout 15 --max-time 60 \
|
||||
-x "$PROXY" \
|
||||
"http://speedtest.tele2.net/10MB.zip" 2>/dev/null || echo "0 0 0")
|
||||
|
||||
DL_BYTES=$(echo "$DL_RESULT" | awk '{print $1}')
|
||||
DL_TIME=$(echo "$DL_RESULT" | awk '{print $2}')
|
||||
DL_SPEED=$(echo "$DL_RESULT" | awk '{print $3}')
|
||||
|
||||
if [[ "$DL_BYTES" -gt 0 ]] 2>/dev/null; then
|
||||
DL_MB=$(echo "scale=2; $DL_BYTES / 1048576" | bc)
|
||||
DL_MBPS=$(echo "scale=2; $DL_SPEED * 8 / 1048576" | bc)
|
||||
DL_TIME_S=$(echo "scale=2; $DL_TIME" | bc)
|
||||
|
||||
pass "Downloaded ${DL_MB} MB in ${DL_TIME_S}s"
|
||||
echo -e " ${BOLD} Download speed: ${DL_MBPS} Mbps${NC}"
|
||||
else
|
||||
fail "Download test failed"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
|
||||
info "Uploading 5MB test payload through tunnel..."
|
||||
|
||||
# Generate 5MB of random data and POST it
|
||||
UL_RESULT=$(dd if=/dev/urandom bs=1M count=5 2>/dev/null | \
|
||||
curl -s -o /dev/null -w '%{size_upload} %{time_total} %{speed_upload}' \
|
||||
--connect-timeout 15 --max-time 60 \
|
||||
-x "$PROXY" \
|
||||
-X POST -H "Content-Type: application/octet-stream" \
|
||||
--data-binary @- \
|
||||
"http://speedtest.tele2.net/upload.php" 2>/dev/null || echo "0 0 0")
|
||||
|
||||
UL_BYTES=$(echo "$UL_RESULT" | awk '{print $1}')
|
||||
UL_TIME=$(echo "$UL_RESULT" | awk '{print $2}')
|
||||
UL_SPEED=$(echo "$UL_RESULT" | awk '{print $3}')
|
||||
|
||||
if [[ "$UL_BYTES" -gt 0 ]] 2>/dev/null; then
|
||||
UL_MB=$(echo "scale=2; $UL_BYTES / 1048576" | bc)
|
||||
UL_MBPS=$(echo "scale=2; $UL_SPEED * 8 / 1048576" | bc)
|
||||
UL_TIME_S=$(echo "scale=2; $UL_TIME" | bc)
|
||||
|
||||
pass "Uploaded ${UL_MB} MB in ${UL_TIME_S}s"
|
||||
echo -e " ${BOLD} Upload speed: ${UL_MBPS} Mbps${NC}"
|
||||
else
|
||||
fail "Upload test failed"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
|
||||
# ── Summary ───────────────────────────────────────────────────────────────────
|
||||
header "Summary"
|
||||
|
||||
if [[ $ERRORS -eq 0 ]]; then
|
||||
echo -e " ${GREEN}${BOLD}ALL TESTS PASSED${NC}"
|
||||
else
|
||||
echo -e " ${RED}${BOLD}$ERRORS TEST(S) FAILED${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
exit $ERRORS
|
||||
383
xray_config.md
Normal file
383
xray_config.md
Normal file
@@ -0,0 +1,383 @@
|
||||
# Xray Infrastructure Configuration
|
||||
|
||||
## Servers
|
||||
|
||||
| Role | IP | Domain | OS | Xray | x-ui |
|
||||
|------|-----|--------|-----|------|------|
|
||||
| Local (client) | 95.165.85.65 | — | Ubuntu, kernel 6.8.0-94 | 25.10.15 | 2.8.10 |
|
||||
| Remote (server) | 83.99.190.32 | share.alogins.net | Ubuntu 24.04.3 (LXD container) | 26.2.6 | 2.8.10 |
|
||||
|
||||
---
|
||||
|
||||
## Access Credentials
|
||||
|
||||
### Remote server SSH
|
||||
- Host: `83.99.190.32`
|
||||
- User: `juris`
|
||||
- LXD container access: `lxc exec xray -- <command>`
|
||||
|
||||
### Remote 3x-ui panel
|
||||
- URL: `https://share.alogins.net:16627/gBdsRLtVZdgZ63wmVR/`
|
||||
- Username: `xrayadmin`
|
||||
- Password: `Admin2026!`
|
||||
|
||||
### Local x-ui panel
|
||||
- URL: `http://127.0.0.1:58959/gnYCNq4EbYukS5qtOe/`
|
||||
- Username: `3ZHPoQdd89`
|
||||
- Password: `1c1QUbKhQP`
|
||||
|
||||
### Local server sudo
|
||||
- User: `alvis`
|
||||
|
||||
---
|
||||
|
||||
## Reality Keys (shared keypair)
|
||||
|
||||
| Key | Value |
|
||||
|-----|-------|
|
||||
| Private key (server) | `KJfhenZvJV1kXwv4kDC8NPBtMUY0RR8lFrxsxfXfFmY` |
|
||||
| Public key (client) | `58Iqd6LuWXgvjAgo92-7KURhTp0Vj79yGF81l_iuvTw` |
|
||||
|
||||
### juris-xhttp (ACTIVE — XHTTP+Reality)
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| UUID | `6e422ab5-070a-43f6-8241-38cd56d23d24` |
|
||||
| Short ID | `6036d37d12c443c4` |
|
||||
| XHTTP path | `/xt-6036d37d` |
|
||||
|
||||
### juris-reality (FALLBACK — TCP+Reality+Vision)
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| UUID | `64522a14-54aa-4b3c-8071-8c8b17aa1f08` |
|
||||
| Short ID | `48b4c16249ad44ff` |
|
||||
|
||||
---
|
||||
|
||||
## SNI Test Results
|
||||
|
||||
Reality dest on remote server: `www.delfi.lv:443`
|
||||
|
||||
| SNI | Status | Avg Latency | Download | Upload |
|
||||
|-----|--------|-------------|----------|--------|
|
||||
| `www.delfi.lv` | **ACTIVE** | 121ms | 44.79 Mbps | 33.90 Mbps |
|
||||
| `www.lmt.lv` | OK | 133ms | 50.58 Mbps | 37.33 Mbps |
|
||||
| `www.inbox.lv` | OK | 121ms | 45.48 Mbps | 32.98 Mbps |
|
||||
| `e-klase.lv` | OK | 121ms | 54.84 Mbps | 29.58 Mbps |
|
||||
| `www.tele2.lv` | FAIL (dest mismatch) | — | — | — |
|
||||
| `www.lsm.lv` | FAIL (dest mismatch) | — | — | — |
|
||||
| `share.alogins.net` | FAIL (dest mismatch) | — | — | — |
|
||||
| `www.microsoft.com` | FAIL (dest mismatch) | — | — | — |
|
||||
|
||||
Full benchmark results: `sni_test_results.md`
|
||||
|
||||
---
|
||||
|
||||
## LXD Port Forwarding (remote host → container)
|
||||
|
||||
```
|
||||
proxy-443: tcp:0.0.0.0:443 → tcp:127.0.0.1:443
|
||||
proxy-16627: tcp:0.0.0.0:16627 → tcp:127.0.0.1:16627
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Remote Server Config (VLESS+XHTTP+Reality inbound) — ACTIVE
|
||||
|
||||
Server: `share.alogins.net` / LXD container `xray` (10.187.159.41)
|
||||
**Winner of comparative test**: lowest latency (142ms avg, 225ms P95), best upload.
|
||||
|
||||
```json
|
||||
{
|
||||
"listen": "0.0.0.0",
|
||||
"port": 443,
|
||||
"protocol": "vless",
|
||||
"tag": "inbound-443",
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
"id": "6e422ab5-070a-43f6-8241-38cd56d23d24",
|
||||
"email": "juris-xhttp",
|
||||
"flow": ""
|
||||
},
|
||||
{
|
||||
"id": "64522a14-54aa-4b3c-8071-8c8b17aa1f08",
|
||||
"email": "juris-reality-legacy",
|
||||
"flow": ""
|
||||
}
|
||||
],
|
||||
"decryption": "none"
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "xhttp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"dest": "www.delfi.lv:443",
|
||||
"serverNames": ["www.delfi.lv", "www.lmt.lv", "www.inbox.lv", "e-klase.lv"],
|
||||
"privateKey": "KJfhenZvJV1kXwv4kDC8NPBtMUY0RR8lFrxsxfXfFmY",
|
||||
"shortIds": ["48b4c16249ad44ff", "6036d37d12c443c4", ""],
|
||||
"show": false
|
||||
},
|
||||
"xhttpSettings": {
|
||||
"path": "/xt-6036d37d",
|
||||
"host": "",
|
||||
"mode": "auto",
|
||||
"extra": {
|
||||
"xPaddingBytes": "100-1000",
|
||||
"xmux": {
|
||||
"maxConcurrency": "16-32",
|
||||
"maxConnections": 0,
|
||||
"cMaxReuseTimes": "64-128",
|
||||
"cMaxLifetimeMs": 0,
|
||||
"hMaxRequestTimes": "600-900",
|
||||
"hMaxReusableSecs": "1800-3000"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Local Server Config (full live xray configuration)
|
||||
|
||||
Config path: `/usr/local/x-ui/bin/config.json`
|
||||
DB path: `/etc/x-ui/x-ui.db` (template stored in `xrayTemplateConfig` setting)
|
||||
|
||||
### Outbounds
|
||||
|
||||
| Tag | Protocol | Destination | Security | Fingerprint | Status |
|
||||
|-----|----------|-------------|----------|-------------|--------|
|
||||
| `direct` | freedom | — | — | — | Default |
|
||||
| `blocked` | blackhole | — | — | — | Block |
|
||||
| `socks` | socks | 127.0.0.1:1081 | — | — | Local proxy |
|
||||
| `AMS-Server` | trojan | 45.32.235.202:443 | reality (SNI: www.nvidia.com) | chrome | Active |
|
||||
| `juris-reality` | vless | share.alogins.net:443 | TCP+reality (SNI: www.delfi.lv) | chrome | Fallback, no routing rules |
|
||||
| `juris-xhttp` | vless | share.alogins.net:443 | XHTTP+reality (SNI: www.delfi.lv, Host: www.delfi.lv) | chrome | **PRIMARY**, no routing rules |
|
||||
| `juris-xhttp-firefox` | vless | share.alogins.net:443 | XHTTP+reality (SNI: www.delfi.lv, Host: www.delfi.lv) | firefox | Alternate fingerprint, no routing rules |
|
||||
| `juris-xhttp-safari` | vless | share.alogins.net:443 | XHTTP+reality (SNI: www.delfi.lv, Host: www.delfi.lv) | safari | Alternate fingerprint, no routing rules |
|
||||
|
||||
### Inbounds
|
||||
|
||||
| Tag | Protocol | Listen | Port | Notes |
|
||||
|-----|----------|--------|------|-------|
|
||||
| `api` | tunnel | 127.0.0.1 | 62789 | Internal API |
|
||||
| `inbound-127.0.0.1:8445` | vless | 127.0.0.1 | 8445 | XHTTP stream, 4 clients |
|
||||
| `inbound-56928` | mixed | 0.0.0.0 | 56928 | SOCKS/HTTP proxy, routes → AMS-Server |
|
||||
|
||||
### Routing Rules
|
||||
|
||||
| # | Match | Outbound |
|
||||
|---|-------|----------|
|
||||
| 1 | inboundTag: `inbound-56928` | `AMS-Server` |
|
||||
| 2 | inboundTag: `api` | `api` |
|
||||
| 3 | ip: `geoip:private` | `blocked` |
|
||||
| 4 | protocol: `bittorrent` | `blocked` |
|
||||
| 5 | domain: `ext:geosite_RU.dat:ru-blocked` + inbound `8445` | `AMS-Server` |
|
||||
|
||||
### VLESS Clients (inbound-127.0.0.1:8445)
|
||||
|
||||
| Email | UUID | Comment |
|
||||
|-------|------|---------|
|
||||
| 36npz8zz | 13a01d7c-9ca7-4ee6-83d8-4c59907b13d1 | ipad pro |
|
||||
| 4camw53nd | 108fdf6b-f8e4-4e1a-9bbe-b7c1fa9fffa0 | pixel |
|
||||
| y3673rul | 584cba3d-2d43-464a-9b29-67e02adc092d | iphone promax |
|
||||
| j5x2285y | f6781d19-cf77-4f8f-9114-0e612aa3081c | AlinaSt |
|
||||
|
||||
### juris-reality Outbound (full config)
|
||||
|
||||
```json
|
||||
{
|
||||
"tag": "juris-reality",
|
||||
"protocol": "vless",
|
||||
"settings": {
|
||||
"vnext": [
|
||||
{
|
||||
"address": "share.alogins.net",
|
||||
"port": 443,
|
||||
"users": [
|
||||
{
|
||||
"id": "64522a14-54aa-4b3c-8071-8c8b17aa1f08",
|
||||
"flow": "xtls-rprx-vision",
|
||||
"encryption": "none"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"fingerprint": "chrome",
|
||||
"serverName": "www.delfi.lv",
|
||||
"publicKey": "58Iqd6LuWXgvjAgo92-7KURhTp0Vj79yGF81l_iuvTw",
|
||||
"shortId": "48b4c16249ad44ff",
|
||||
"spiderX": "/"
|
||||
},
|
||||
"tcpSettings": {
|
||||
"header": { "type": "none" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### AMS-Server Outbound (existing)
|
||||
|
||||
```json
|
||||
{
|
||||
"tag": "AMS-Server",
|
||||
"protocol": "trojan",
|
||||
"settings": {
|
||||
"servers": [
|
||||
{
|
||||
"address": "45.32.235.202",
|
||||
"port": 443,
|
||||
"password": "z5Y2tMeDs1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "tcp",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"publicKey": "J37AuaLzZYRTQZ-pxUpbIndo15EpoY1lhnAXZ1rUFnQ",
|
||||
"fingerprint": "chrome",
|
||||
"serverName": "www.nvidia.com",
|
||||
"shortId": "22",
|
||||
"spiderX": "/"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## File Paths
|
||||
|
||||
### Local server
|
||||
| Path | Purpose |
|
||||
|------|---------|
|
||||
| `/usr/local/x-ui/bin/config.json` | Live xray config (auto-generated by x-ui, do NOT edit directly) |
|
||||
| `/usr/local/x-ui/bin/xray-linux-amd64` | Xray binary |
|
||||
| `/usr/local/x-ui/x-ui` | x-ui panel binary |
|
||||
| `/etc/x-ui/x-ui.db` | x-ui database (settings, inbounds, users) |
|
||||
| `/home/alvis/ai-xray/venv/` | Python venv with paramiko for SSH |
|
||||
|
||||
### Remote server (inside LXD container "xray")
|
||||
| Path | Purpose |
|
||||
|------|---------|
|
||||
| `/usr/local/x-ui/bin/config.json` | Live xray config (auto-generated) |
|
||||
| `/usr/local/x-ui/bin/xray-linux-amd64` | Xray binary |
|
||||
| `/etc/x-ui/x-ui.db` | x-ui database |
|
||||
|
||||
---
|
||||
|
||||
## How to Route Traffic Through juris-reality
|
||||
|
||||
Currently `juris-reality` has no routing rules. To use it, add a rule to the xray template in the DB:
|
||||
|
||||
**Route specific domains:**
|
||||
```json
|
||||
{
|
||||
"type": "field",
|
||||
"domain": ["example.com", "example.org"],
|
||||
"outboundTag": "juris-reality"
|
||||
}
|
||||
```
|
||||
|
||||
**Route all SOCKS proxy traffic (replaces AMS-Server):**
|
||||
```json
|
||||
{
|
||||
"type": "field",
|
||||
"inboundTag": ["inbound-56928"],
|
||||
"outboundTag": "juris-reality"
|
||||
}
|
||||
```
|
||||
|
||||
**Route Russian-blocked domains through juris-reality instead of AMS-Server:**
|
||||
Change outboundTag in rule #5 from `AMS-Server` to `juris-reality`.
|
||||
|
||||
---
|
||||
|
||||
## Updating Xray Config via x-ui REST API (no sudo)
|
||||
|
||||
`config.json` is auto-generated by x-ui from the DB template. **Never edit it directly.**
|
||||
|
||||
### Step-by-step
|
||||
|
||||
**1. Authenticate:**
|
||||
```bash
|
||||
curl -s -c /tmp/xui-cookie.txt -X POST \
|
||||
'http://127.0.0.1:58959/gnYCNq4EbYukS5qtOe/login' \
|
||||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||||
-d 'username=3ZHPoQdd89&password=1c1QUbKhQP'
|
||||
```
|
||||
|
||||
**2. Read current xray template:**
|
||||
```bash
|
||||
curl -s -b /tmp/xui-cookie.txt -X POST \
|
||||
'http://127.0.0.1:58959/gnYCNq4EbYukS5qtOe/panel/xray/'
|
||||
```
|
||||
Returns `{"success":true,"obj":"{ \"xraySetting\": {...}, \"inboundTags\": [...] }"}`.
|
||||
The `obj` value is a JSON string containing the full xray template under the `xraySetting` key.
|
||||
|
||||
**3. Update xray template:**
|
||||
```bash
|
||||
curl -s -b /tmp/xui-cookie.txt -X POST \
|
||||
'http://127.0.0.1:58959/gnYCNq4EbYukS5qtOe/panel/xray/update' \
|
||||
-d 'xraySetting=<URL-encoded JSON of the full xray template>'
|
||||
```
|
||||
|
||||
The key detail: use **form-urlencoded** with a single field `xraySetting` whose value is the full xray template JSON string. This is the `xraySetting` object from step 2 (the inner JSON, not the wrapper).
|
||||
|
||||
**4. Restart xray to apply:**
|
||||
```bash
|
||||
curl -s -b /tmp/xui-cookie.txt -X POST \
|
||||
'http://127.0.0.1:58959/gnYCNq4EbYukS5qtOe/panel/api/server/restartXrayService'
|
||||
```
|
||||
|
||||
### Python example (recommended)
|
||||
|
||||
```python
|
||||
import requests, json
|
||||
|
||||
BASE = "http://127.0.0.1:58959/gnYCNq4EbYukS5qtOe"
|
||||
s = requests.Session()
|
||||
|
||||
# Login
|
||||
s.post(f"{BASE}/login", data={"username": "3ZHPoQdd89", "password": "1c1QUbKhQP"})
|
||||
|
||||
# Read current template
|
||||
r = s.post(f"{BASE}/panel/xray/")
|
||||
obj = json.loads(r.json()["obj"])
|
||||
xray = obj["xraySetting"]
|
||||
|
||||
# Modify (example: change SNI)
|
||||
for ob in xray["outbounds"]:
|
||||
if ob.get("tag") == "juris-reality":
|
||||
ob["streamSettings"]["realitySettings"]["serverName"] = "www.delfi.lv"
|
||||
|
||||
# Write back (form field, NOT JSON body)
|
||||
s.post(f"{BASE}/panel/xray/update", data={"xraySetting": json.dumps(xray)})
|
||||
|
||||
# Restart xray
|
||||
s.post(f"{BASE}/panel/api/server/restartXrayService")
|
||||
```
|
||||
|
||||
### API gotchas
|
||||
|
||||
| Endpoint | Method | Works? | Notes |
|
||||
|----------|--------|--------|-------|
|
||||
| `POST /panel/xray/update` | form-urlencoded `xraySetting=<json>` | **Yes** | Correct way to persist template changes |
|
||||
| `POST /panel/xray/update` | JSON body `{"xraySetting": ...}` | No | Returns "unexpected end of JSON input" |
|
||||
| `POST /panel/setting/update` | any | No | Accepts request but silently ignores `xrayTemplateConfig` |
|
||||
| `GET /panel/setting/getDefaultJsonConfig` | — | — | Returns factory defaults, not actual config |
|
||||
| `GET /panel/api/server/getConfigJson` | — | Read-only | Returns live running xray config |
|
||||
| `xray api ado` CLI | — | Ephemeral | Adds outbound to running instance only, lost on restart |
|
||||
|
||||
---
|
||||
|
||||
## Important Notes
|
||||
|
||||
- The `xray api ado` CLI can add outbounds to the running instance (ephemeral, lost on restart). Requires full-config JSON format: `{"outbounds": [{...}]}`.
|
||||
Reference in New Issue
Block a user