Files
BFR/box/scripts/box.wgobfs
2026-08-13 11:56:37 +03:00

183 lines
5.4 KiB
Bash

#!/system/bin/sh
# VeilBox - WireGuard Obfuscator (ClusterM/wg-obfuscator) lifecycle
# Docs: https://github.com/ClusterM/wg-obfuscator
#
# Client flow:
# sing-box WireGuard peer → 127.0.0.1:wgobfs_listen_port
# → wg-obfuscator → real server obfuscator (or server WG via obfuscator)
#
# REQUIRED: the same key + matching ports on the VPS (server-side obfuscator).
# Without the server half this cannot work.
if ! /system/bin/sh -n /data/adb/box/settings.ini 2>/dev/null; then
echo "Err: settings.ini syntax error" >&2
exit 1
fi
scripts_dir="${0%/*}"
. /data/adb/box/settings.ini
: "${wgobfs_enable:=false}"
: "${wgobfs_bin:=${box_dir}/bin/wg-obfuscator}"
: "${wgobfs_config:=${box_dir}/sidecar/wg-obfuscator.conf}"
: "${wgobfs_listen_port:=41320}"
: "${wgobfs_masking:=AUTO}"
: "${wgobfs_patch_config:=true}"
wgobfs_pid="${box_run}/wg-obfuscator.pid"
wgobfs_log="${box_run}/wg-obfuscator.log"
wgobfs_stop() {
if [ -f "${wgobfs_pid}" ]; then
spid=$(cat "${wgobfs_pid}" 2>/dev/null)
[ -n "${spid}" ] && kill -15 "${spid}" >/dev/null 2>&1
rm -f "${wgobfs_pid}"
fi
busybox pkill -15 wg-obfuscator >/dev/null 2>&1
}
wgobfs_wait_udp() {
# UDP listen is hard to probe with nc -z; rely on process + brief sleep
sleep 1
if [ -f "${wgobfs_pid}" ]; then
spid=$(cat "${wgobfs_pid}" 2>/dev/null)
kill -0 "${spid}" 2>/dev/null && return 0
fi
busybox pidof wg-obfuscator >/dev/null 2>&1
}
# Rewrite sing-box wireguard peers to localhost obfuscator listen port.
# Real endpoints are saved to ${box_run}/wgobfs-real-peers.txt (sing-box rejects unknown JSON fields).
wgobfs_patch_singbox() {
[ "${wgobfs_patch_config}" = "true" ] || return 0
[ "${bin_name}" != "sing-box" ] && return 0
[ ! -f "${sing_config}" ] && return 0
yq="${box_dir}/bin/yq"
[ -x "${yq}" ] || yq="yq"
if ! command -v "${yq}" >/dev/null 2>&1 && [ ! -x "${yq}" ]; then
log Warning "[wgobfs] yq missing - cannot patch WireGuard endpoint"
return 1
fi
if ! "${yq}" -e '.endpoints[] | select(.type == "wireguard")' "${sing_config}" >/dev/null 2>&1; then
log Warning "[wgobfs] no wireguard endpoint in ${sing_config}"
return 1
fi
state="${box_run}/wgobfs-real-peers.txt"
mkdir -p "${box_run}"
: > "${state}"
# Capture current non-loopback peers, then rewrite to local obfuscator
"${yq}" -r '
.endpoints[]? | select(.type == "wireguard") | .peers[]? |
((.address // "") + ":" + ((.port // 0)|tostring))
' "${sing_config}" 2>/dev/null | while read -r ep; do
case "${ep}" in
127.*|""|:0) continue ;;
*) echo "${ep}" >> "${state}" ;;
esac
done
"${yq}" -i -o=json '
(.endpoints[] | select(.type == "wireguard") | .peers[] | .address) = "127.0.0.1" |
(.endpoints[] | select(.type == "wireguard") | .peers[] | .port) = '"${wgobfs_listen_port}"'
' "${sing_config}" && \
log Info "[wgobfs] WireGuard peers → 127.0.0.1:${wgobfs_listen_port}"
# Direct route for real WG/obfuscator server IPs (avoid TPROXY loop)
if [ -f "${state}" ]; then
while read -r ep; do
ip="${ep%%:*}"
case "${ip}" in
127.*|"") continue ;;
esac
if ! "${yq}" -e ".route.rules[] | select(.ip_cidr[] == \"${ip}/32\")" "${sing_config}" >/dev/null 2>&1; then
"${yq}" -i -o=json ".route.rules = [{\"ip_cidr\":[\"${ip}/32\"],\"outbound\":\"direct\"}] + (.route.rules // [])" "${sing_config}" && \
log Info "[wgobfs] route direct ${ip}/32"
fi
done < "${state}"
fi
}
wgobfs_ensure_conf() {
mkdir -p "$(dirname "${wgobfs_config}")" "${box_run}"
if [ -f "${wgobfs_config}" ]; then
return 0
fi
if [ -f "${box_dir}/sidecar/wg-obfuscator.example.conf" ]; then
cp -f "${box_dir}/sidecar/wg-obfuscator.example.conf" "${wgobfs_config}"
log Warning "[wgobfs] created ${wgobfs_config} from example - edit key/target"
return 0
fi
log Error "[wgobfs] missing ${wgobfs_config}"
return 1
}
wgobfs_start() {
if [ "${wgobfs_enable}" != "true" ]; then
return 0
fi
if [ ! -x "${wgobfs_bin}" ]; then
log Error "[wgobfs] binary not found: ${wgobfs_bin}"
log Error "[wgobfs] download linux-arm64 from https://github.com/ClusterM/wg-obfuscator/releases"
return 1
fi
wgobfs_ensure_conf || return 1
wgobfs_stop
# Prefer source-if / source-lport from settings if config still has placeholders
# Launch with config file (multi-section supported)
nohup busybox setuidgid "${box_user_group}" \
"${wgobfs_bin}" -c "${wgobfs_config}" >> "${wgobfs_log}" 2>&1 &
echo -n "$!" > "${wgobfs_pid}"
log Info "[wgobfs] starting pid=$! config=${wgobfs_config}"
if wgobfs_wait_udp; then
log Info "[wgobfs] running (listen see config source-lport, default ${wgobfs_listen_port})"
else
log Error "[wgobfs] process died - see ${wgobfs_log}"
tail -n 15 "${wgobfs_log}" 2>/dev/null | while read -r line; do log Error "[wgobfs] ${line}"; done
return 1
fi
wgobfs_patch_singbox
return 0
}
wgobfs_status() {
echo "wgobfs_enable=${wgobfs_enable}"
echo "config=${wgobfs_config}"
echo "bin=${wgobfs_bin}"
if busybox pidof wg-obfuscator >/dev/null 2>&1; then
echo "pid=$(busybox pidof wg-obfuscator)"
echo "state=running"
else
echo "state=stopped"
fi
[ -f "${wgobfs_log}" ] && tail -n 8 "${wgobfs_log}"
}
case "$1" in
start)
wgobfs_start
;;
stop)
wgobfs_stop
log Info "[wgobfs] stopped"
;;
patch)
wgobfs_patch_singbox
;;
status)
wgobfs_status
;;
*)
echo "usage: $0 {start|stop|patch|status}"
exit 1
;;
esac