fix: ship router package helpers

This commit is contained in:
andrey271192
2026-06-12 12:21:26 +03:00
parent 8e491b8f29
commit a686ea967e
12 changed files with 1877 additions and 13 deletions

View File

@@ -22,6 +22,8 @@ phobos-update beta # или: phobos-update dev
## stable
### v1.2.8
- **fix:** Android PhobosWG больше не получает `PresharedKey = none` в phobos payload, из-за чего импорт мог падать с «Неправильная длина ключа в PublicKey».
- **fix:** клиентский router package теперь содержит обязательные `lib-client.sh`, `install-obfuscator.sh`, `install-wireguard.sh` и дополнительные helper-скрипты из PCA repo, поэтому `wget .../init/TOKEN.sh | sh` больше не падает с `can't open './lib-client.sh'`.
- **fix:** поле имени клиента больше не даёт ошибку в консоли Chrome из-за HTML `pattern`.
- **fix:** fresh stable install теперь кладёт обязательный `lib-core.sh` рядом с `phobos-client.sh`, поэтому `/clients` не сыпется на `check_root/load_env/die/log_*: command not found`.
- **fix:** шаг `[6/9] PCA patches` больше не падает с `curl: (23) Failure writing output to destination`, если папки `repo/server/scripts` или `repo/client/templates` не были созданы upstream sparse clone.
@@ -55,6 +57,8 @@ phobos-update beta # или: phobos-update dev
|--------|----------|---------|
| 1.2.7 / 1.3.x | PhobosWG: «невозможно импортировать туннель» | `phobos://` дополняется `= none` для всех обязательных полей (`PresharedKey`) по спеке формата |
| 1.2.8 / 1.3.x | Stable не показывал кнопку скачивания APK без локального файла | кнопка **PhobosWG (APK)** всегда доступна на Android-странице; `/download/apk` ведет на public stable release |
| 1.2.8 / 1.3.x | Android PhobosWG: «Неправильная длина ключа в PublicKey» | Android payload нормализован без `PresharedKey = none`, QR/link нужно открыть заново |
| 1.2.8 / 1.3.x | Router init: `can't open './lib-client.sh'` | обязательные router helper-скрипты добавлены в PCA overlay и managed update |
| 1.2.8 / 1.3.x | Stable `/clients`: `lib-core.sh: No such file or directory`, `check_root/load_env: command not found` | `lib-core.sh` добавлен в public repo, installer и updater |
| 1.2.8 / 1.3.x | Install `[6/9] PCA patches`: `curl: (23) Failure writing output to destination` | `fetch()` создаёт папку назначения перед `curl -o` |
| 1.2.8 / 1.3.x | VPS x86_64: `ERROR: obfuscator binary for x86_64 missing` | авто-загрузка `linux-x64` из ClusterM releases + нормализация `amd64 -> x86_64` |

41
app.py
View File

@@ -713,20 +713,17 @@ def build_phone_config(cid, mode="android"):
if os.path.exists(instpath):
inst = open(instpath).read().strip()
conf = wg + "\n\n" + inst + "\n"
# PhobosWG requires ALL fields present; missing -> "= none" before base64
# (spec 2.3). Ground-Zerro configs lack PresharedKey -> import fails otherwise.
padded = _pad_conf_with_none(conf)
b64 = base64.urlsafe_b64encode(padded.encode()).decode().rstrip("=")
payload = _prepare_phoboswg_payload(conf)
b64 = base64.urlsafe_b64encode(payload.encode()).decode()
link = "phobos://" + b64 + "#" + urllib.parse.quote(cid or "none")
return conf, link
return payload, link
def _pad_conf_with_none(conf):
"""Insert '<key> = none' for every missing mandatory PhobosWG field
(phobos:// spec 2.3). Only for phobos:// payload, NOT raw .conf."""
def _prepare_phoboswg_payload(conf):
"""Normalize Android PhobosWG phobos:// payload. Do not use for raw .conf."""
mandatory = {
"[interface]": ["PrivateKey", "Address", "MTU", "DNS"],
"[peer]": ["PublicKey", "PresharedKey", "AllowedIPs", "PersistentKeepalive", "Endpoint"],
"[peer]": ["PublicKey", "AllowedIPs", "PersistentKeepalive", "Endpoint"],
"[instance]": ["source-if", "source-lport", "target", "key", "masking",
"verbose", "idle-timeout", "max-dummy"],
}
@@ -741,6 +738,27 @@ def _pad_conf_with_none(conf):
cur[1].append(ln)
out = []
for header, lines in sections:
normalized = []
for ln in lines:
if "=" not in ln:
normalized.append(ln)
continue
key, value = ln.split("=", 1)
k = key.strip().lower()
v = value.strip()
if k == "presharedkey" and v.lower() in ("", "none"):
continue
if header.lower() == "[peer]" and k == "allowedips":
parts = [p.strip() for p in v.split(",") if p.strip()]
if "0.0.0.0/0" not in parts:
parts.insert(0, "0.0.0.0/0")
if "::/0" not in parts:
parts.append("::/0")
ln = "%s = %s" % (key.strip(), ", ".join(parts))
elif header.lower() == "[instance]" and k == "masking" and v.upper() == "AUTO":
ln = "%s = STUN" % key.strip()
normalized.append(ln)
lines = normalized
keys = {ln.split("=", 1)[0].strip().lower() for ln in lines if "=" in ln}
while lines and lines[-1].strip() == "":
lines.pop()
@@ -753,6 +771,11 @@ def _pad_conf_with_none(conf):
return "\n".join(out).rstrip("\n") + "\n"
def _pad_conf_with_none(conf):
"""Legacy helper kept for old endpoints that may call it."""
return _prepare_phoboswg_payload(conf)
def qr_datauri(data):
try:
import qrcode, io, base64

View File

@@ -259,6 +259,26 @@ phobos-update stable
Потом вставь новую команду установки на роутере.
### Команда на роутере пишет `line 8: can't open './lib-client.sh'`
Причина: пакет клиента собран без обязательных helper-файлов (`lib-client.sh`, `install-obfuscator.sh`, `install-wireguard.sh`). Это происходило, когда installer брал часть templates из внешнего upstream repo, где эти файлы могли отсутствовать.
Решение на сервере:
```bash
phobos-update stable
/opt/Phobos/repo/server/scripts/phobos-client.sh package CLIENT_ID
/opt/Phobos/repo/server/scripts/phobos-client.sh link CLIENT_ID
```
Потом скопируй новую команду установки из панели и запусти её на роутере.
Проверка пакета:
```bash
tar tzf /opt/Phobos/packages/phobos-CLIENT_ID.tar.gz | grep -E 'lib-client.sh|install-obfuscator.sh|install-wireguard.sh'
```
### Пакет битый: `Bad package`, `not in gzip format`, HTML вместо tar.gz
Роутер скачал не пакет, а страницу ошибки.
@@ -533,6 +553,24 @@ phobos-update stable
Потом заново открой Android-страницу клиента и сканируй QR прямо с панели. Если не помогло, создай нового клиента для телефона. Один клиент = одно устройство; роутер и телефон должны иметь разные клиенты.
### Android PhobosWG: `Неправильная длина ключа в PublicKey`
Причина: старый phobos payload мог добавлять `PresharedKey = none` или отдавать старый QR/ссылку после обновления панели. PhobosWG воспринимает это как битый ключ.
Решение:
```bash
phobos-update stable
systemctl restart phobos-panel
```
Потом:
1. Открой клиента в панели заново.
2. Нажми Android/PhobosWG.
3. Сканируй новый QR или копируй новую `phobos://` ссылку.
4. Если клиент был создан старой версией и ошибка осталась, создай нового клиента именно для телефона.
### Android импорт прошел, но интернета нет
В `[Peer]` должны быть маршруты:

View File

@@ -206,8 +206,14 @@ fetch() {
fetch overlay/lib-core.sh "$PHOBOS_DIR/repo/server/scripts/lib-core.sh" && chmod +x "$PHOBOS_DIR/repo/server/scripts/lib-core.sh"
fetch overlay/phobos-client.sh "$PHOBOS_DIR/repo/server/scripts/phobos-client.sh" && chmod +x "$PHOBOS_DIR/repo/server/scripts/phobos-client.sh"
fetch overlay/install-router.sh.template "$PHOBOS_DIR/repo/client/templates/install-router.sh.template"
fetch overlay/lib-client.sh "$PHOBOS_DIR/repo/client/templates/lib-client.sh" && chmod +x "$PHOBOS_DIR/repo/client/templates/lib-client.sh"
fetch overlay/install-obfuscator.sh "$PHOBOS_DIR/repo/client/templates/install-obfuscator.sh" && chmod +x "$PHOBOS_DIR/repo/client/templates/install-obfuscator.sh"
fetch overlay/install-wireguard.sh "$PHOBOS_DIR/repo/client/templates/install-wireguard.sh" && chmod +x "$PHOBOS_DIR/repo/client/templates/install-wireguard.sh"
fetch overlay/router-configure-wireguard.sh "$PHOBOS_DIR/repo/client/templates/router-configure-wireguard.sh" && chmod +x "$PHOBOS_DIR/repo/client/templates/router-configure-wireguard.sh"
fetch overlay/router-configure-wireguard-openwrt.sh "$PHOBOS_DIR/repo/client/templates/router-configure-wireguard-openwrt.sh" && chmod +x "$PHOBOS_DIR/repo/client/templates/router-configure-wireguard-openwrt.sh"
fetch overlay/phobos-pull.sh "$PHOBOS_DIR/repo/client/templates/phobos-pull.sh" && chmod +x "$PHOBOS_DIR/repo/client/templates/phobos-pull.sh"
fetch overlay/phobos-uninstall.sh "$PHOBOS_DIR/repo/client/templates/phobos-uninstall.sh" && chmod +x "$PHOBOS_DIR/repo/client/templates/phobos-uninstall.sh"
fetch overlay/3xui.sh "$PHOBOS_DIR/repo/client/templates/3xui.sh" && chmod +x "$PHOBOS_DIR/repo/client/templates/3xui.sh"
fetch server/phobos-health.sh "$PHOBOS_DIR/server/phobos-health.sh" && chmod +x "$PHOBOS_DIR/server/phobos-health.sh"
fetch server/phobos-pull.sh "$PHOBOS_DIR/server/phobos-pull.sh" && chmod +x "$PHOBOS_DIR/server/phobos-pull.sh"
fetch server/phobos-router-watchdog.py "$PHOBOS_DIR/server/phobos-router-watchdog.py"

456
overlay/3xui.sh Executable file
View File

@@ -0,0 +1,456 @@
#!/bin/bash
GREEN='\033[92m'
YELLOW='\033[93m'
RED='\033[91m'
BLUE='\033[94m'
CYAN='\033[96m'
RESET='\033[0m'
BOLD='\033[1m'
print_status() {
local message="$1"
local status="${2:-info}"
local prefix=""
case "$status" in
success) prefix="${GREEN}[OK]" ;;
error) prefix="${RED}[ERR]" ;;
warning) prefix="${YELLOW}[WARN]" ;;
info) prefix="${BLUE}[INFO]" ;;
debug) prefix="${CYAN}[DBG]" ;;
esac
echo -e "${prefix} ${message}${RESET}"
}
check_dependencies() {
local missing=()
for cmd in jq sqlite3; do
if ! command -v "$cmd" >/dev/null 2>&1; then
missing+=("$cmd")
fi
done
if [[ ${#missing[@]} -gt 0 ]]; then
print_status "Installing missing dependencies: ${missing[*]}" "info"
if command -v apt-get >/dev/null 2>&1; then
apt-get update -qq && apt-get install -y -qq "${missing[@]}" >/dev/null 2>&1
elif command -v yum >/dev/null 2>&1; then
yum install -y -q "${missing[@]/sqlite3/sqlite}" >/dev/null 2>&1
elif command -v dnf >/dev/null 2>&1; then
dnf install -y -q "${missing[@]/sqlite3/sqlite}" >/dev/null 2>&1
elif command -v apk >/dev/null 2>&1; then
apk add --quiet "${missing[@]/sqlite3/sqlite}" >/dev/null 2>&1
fi
for cmd in "${missing[@]}"; do
if ! command -v "$cmd" >/dev/null 2>&1; then
print_status "Failed to install $cmd" "error"
exit 1
fi
done
print_status "Dependencies installed successfully" "success"
fi
}
parse_wireguard_conf() {
local config_path="$1"
local section=""
if [[ ! -f "$config_path" ]]; then
print_status "File $config_path not found" "error"
exit 1
fi
INTERFACE_PRIVATEKEY=""
INTERFACE_ADDRESS=""
INTERFACE_MTU="1420"
PEER_PUBLICKEY=""
PEER_ENDPOINT=""
PEER_ALLOWEDIPS=""
PEER_PRESHAREDKEY=""
PEER_KEEPALIVE="0"
while IFS= read -r line || [[ -n "$line" ]]; do
line=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
[[ -z "$line" || "$line" == \#* ]] && continue
if [[ "$line" == "["*"]" ]]; then
section=$(echo "$line" | tr -d '[]' | tr '[:upper:]' '[:lower:]')
continue
fi
if [[ "$line" == *"="* ]]; then
local key=$(echo "$line" | cut -d'=' -f1 | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
local value=$(echo "$line" | cut -d'=' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [[ "$section" == "interface" ]]; then
case "$key" in
PrivateKey) INTERFACE_PRIVATEKEY="$value" ;;
Address) INTERFACE_ADDRESS="$value" ;;
MTU) INTERFACE_MTU="$value" ;;
esac
elif [[ "$section" == "peer" ]]; then
case "$key" in
PublicKey) PEER_PUBLICKEY="$value" ;;
Endpoint) PEER_ENDPOINT="$value" ;;
AllowedIPs) PEER_ALLOWEDIPS="$value" ;;
PresharedKey|PreSharedKey) PEER_PRESHAREDKEY="$value" ;;
PersistentKeepalive) PEER_KEEPALIVE="$value" ;;
esac
fi
fi
done < "$config_path"
}
extract_endpoint() {
local endpoint="$1"
if [[ "$endpoint" == "["*"]:"* ]]; then
SERVER_ADDRESS=$(echo "$endpoint" | sed 's/^\[\([^]]*\)\]:.*/\1/')
SERVER_PORT=$(echo "$endpoint" | sed 's/.*\]:\([0-9]*\).*/\1/')
else
SERVER_ADDRESS=$(echo "$endpoint" | rev | cut -d':' -f2- | rev)
SERVER_PORT=$(echo "$endpoint" | rev | cut -d':' -f1 | rev)
fi
[[ -z "$SERVER_PORT" ]] && SERVER_PORT="51820"
}
build_local_addresses() {
local addresses="$1"
local result="[]"
IFS=',' read -ra ADDR_ARRAY <<< "$addresses"
for addr in "${ADDR_ARRAY[@]}"; do
addr=$(echo "$addr" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
[[ -z "$addr" ]] && continue
local ip_part="$addr"
if [[ "$addr" == *"/"* ]]; then
ip_part=$(echo "$addr" | cut -d'/' -f1)
fi
if [[ "$ip_part" == *":"* ]]; then
result=$(echo "$result" | jq --arg ip "${ip_part}/128" '. + [$ip]')
else
result=$(echo "$result" | jq --arg ip "${ip_part}/32" '. + [$ip]')
fi
done
echo "$result"
}
build_allowed_ips() {
local allowed="$1"
local result="[]"
if [[ -z "$allowed" ]]; then
echo '["0.0.0.0/0", "::/0"]'
return
fi
IFS=',' read -ra IP_ARRAY <<< "$allowed"
for ip in "${IP_ARRAY[@]}"; do
ip=$(echo "$ip" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
[[ -z "$ip" ]] && continue
result=$(echo "$result" | jq --arg ip "$ip" '. + [$ip]')
done
if [[ "$result" == "[]" ]]; then
echo '["0.0.0.0/0", "::/0"]'
else
echo "$result"
fi
}
convert_to_xray_outbound() {
local tag="${1:-Phobos}"
extract_endpoint "$PEER_ENDPOINT"
local local_addresses=$(build_local_addresses "$INTERFACE_ADDRESS")
local allowed_ips=$(build_allowed_ips "$PEER_ALLOWEDIPS")
local peer_config
peer_config=$(jq -n \
--arg pubkey "$PEER_PUBLICKEY" \
--argjson allowed "$allowed_ips" \
--arg endpoint "${SERVER_ADDRESS}:${SERVER_PORT}" \
'{
"publicKey": $pubkey,
"allowedIPs": $allowed,
"endpoint": $endpoint
}')
if [[ -n "$PEER_KEEPALIVE" && "$PEER_KEEPALIVE" != "0" ]]; then
peer_config=$(echo "$peer_config" | jq --argjson ka "$PEER_KEEPALIVE" '. + {"keepAlive": $ka}')
fi
if [[ -n "$PEER_PRESHAREDKEY" ]]; then
peer_config=$(echo "$peer_config" | jq --arg psk "$PEER_PRESHAREDKEY" '. + {"preSharedKey": $psk}')
fi
local mtu="${INTERFACE_MTU:-1420}"
OUTBOUND_JSON=$(jq -n \
--arg tag "$tag" \
--argjson mtu "$mtu" \
--arg secret "$INTERFACE_PRIVATEKEY" \
--argjson addresses "$local_addresses" \
--argjson peer "$peer_config" \
'{
"protocol": "wireguard",
"settings": {
"mtu": $mtu,
"secretKey": $secret,
"address": $addresses,
"workers": 2,
"peers": [$peer],
"noKernelTun": true
},
"tag": $tag
}')
}
get_default_xray_config() {
cat <<'XRAY_CONFIG'
{
"api": {
"services": ["HandlerService", "StatsService", "LoggerService"],
"tag": "api"
},
"inbounds": [
{
"listen": "127.0.0.1",
"port": 62789,
"protocol": "dokodemo-door",
"settings": {"address": "127.0.0.1"},
"tag": "api"
}
],
"outbounds": [
{"protocol": "freedom", "settings": {}, "tag": "direct"},
{"protocol": "blackhole", "settings": {}, "tag": "block"}
],
"policy": {
"levels": {"0": {"statsUserDownlink": true, "statsUserUplink": true}},
"system": {
"statsInboundDownlink": true,
"statsInboundUplink": true,
"statsOutboundDownlink": true,
"statsOutboundUplink": true
}
},
"routing": {
"domainStrategy": "AsIs",
"rules": [
{"inboundTag": ["api"], "outboundTag": "api", "type": "field"},
{"ip": ["geoip:private"], "outboundTag": "block", "type": "field"}
]
},
"stats": {}
}
XRAY_CONFIG
}
find_database() {
local paths=(
"/etc/x-ui/x-ui.db"
"/usr/local/x-ui/x-ui.db"
"/opt/x-ui/x-ui.db"
"./x-ui.db"
)
for path in "${paths[@]}"; do
if [[ -f "$path" ]]; then
print_status "Database found: $path" "success" >&2
echo "$path"
return 0
fi
done
return 1
}
diagnose_database() {
local db_path="$1"
local tables=$(sqlite3 "$db_path" "SELECT name FROM sqlite_master WHERE type='table';" 2>/dev/null)
print_status "Found tables: $tables" "debug" >&2
if echo "$tables" | grep -q "settings"; then
local keys=$(sqlite3 "$db_path" "SELECT key FROM settings;" 2>/dev/null)
print_status "Keys in settings: $keys" "debug" >&2
for key in xrayTemplateConfig xrayConfig xray_config config; do
local result=$(sqlite3 "$db_path" "SELECT value FROM settings WHERE key = '$key';" 2>/dev/null)
if [[ -n "$result" ]]; then
print_status "Found config key: $key" "success" >&2
echo "$key"
return 0
fi
done
fi
return 1
}
write_setting_to_db() {
local db_path="$1"
local config_key="$2"
local config_value="$3"
local tmp
tmp=$(mktemp)
printf '%s' "$config_value" > "$tmp"
sqlite3 "$db_path" "INSERT OR REPLACE INTO settings (key, value) VALUES ('$config_key', readfile('$tmp'));"
local ret=$?
rm -f "$tmp"
return $ret
}
import_via_database() {
local db_path="$1"
print_status "Importing to database..." "info"
if [[ ! -f "$db_path" ]]; then
print_status "Database not found: $db_path" "error"
return 1
fi
local config_key=$(diagnose_database "$db_path")
if [[ -z "$config_key" ]]; then
print_status "Xray config not found in database, creating new..." "warning"
local base_config=$(get_default_xray_config)
local new_config=$(echo "$base_config" | jq --argjson ob "$OUTBOUND_JSON" '.outbounds = [$ob] + .outbounds')
if write_setting_to_db "$db_path" "xrayTemplateConfig" "$new_config"; then
print_status "Base Xray config created with outbound" "success"
return 0
else
print_status "Failed to create config" "error"
return 1
fi
fi
local current_config=$(sqlite3 "$db_path" "SELECT value FROM settings WHERE key = '$config_key';" 2>/dev/null)
if [[ -z "$current_config" ]]; then
print_status "Xray config not found" "error"
return 1
fi
if ! echo "$current_config" | jq . >/dev/null 2>&1; then
print_status "Invalid JSON in config" "error"
return 1
fi
local tag=$(echo "$OUTBOUND_JSON" | jq -r '.tag')
local existing_tag=$(echo "$current_config" | jq -r --arg t "$tag" '.outbounds[]? | select(.tag == $t) | .tag' 2>/dev/null)
if [[ -n "$existing_tag" ]]; then
print_status "Outbound '$tag' exists, replacing..." "warning"
current_config=$(echo "$current_config" | jq --arg t "$tag" '.outbounds = [.outbounds[] | select(.tag != $t)]')
fi
local new_config=$(echo "$current_config" | jq --argjson ob "$OUTBOUND_JSON" '.outbounds = [$ob] + .outbounds')
if write_setting_to_db "$db_path" "$config_key" "$new_config"; then
print_status "Outbound added to database" "success"
return 0
else
print_status "Failed to update database" "error"
return 1
fi
}
restart_xui_service() {
print_status "Restarting x-ui service..." "info"
if systemctl restart x-ui 2>/dev/null; then
sleep 1
if systemctl is-active --quiet x-ui; then
print_status "x-ui service restarted and active" "success"
return 0
fi
fi
for cmd in "service x-ui restart" "/etc/init.d/x-ui restart" "x-ui restart"; do
if $cmd 2>/dev/null; then
print_status "Service restarted via: $cmd" "success"
return 0
fi
done
print_status "Failed to restart service automatically" "error"
print_status "Restart manually: systemctl restart x-ui" "info"
return 1
}
main() {
echo -e "\n${BOLD}=== WireGuard to 3x-ui Auto-Import Tool ===${RESET}\n"
check_dependencies
local config_path="$1"
if [[ -z "$config_path" ]]; then
echo -n "Path to wireguard.conf: "
read config_path
fi
print_status "Parsing $config_path..." "info"
parse_wireguard_conf "$config_path"
print_status "Converting to Xray format..." "info"
convert_to_xray_outbound "Phobos"
echo -e "\n${BOLD}Outbound configuration:${RESET}"
echo "$OUTBOUND_JSON" | jq .
echo
local db_path="/etc/x-ui/x-ui.db"
if [[ ! -f "$db_path" ]]; then
print_status "Database not found at: $db_path" "warning"
db_path=$(find_database)
if [[ -z "$db_path" ]]; then
print_status "Database not found in standard locations" "error"
print_status "Make sure 3x-ui is installed" "info"
exit 1
fi
fi
systemctl stop x-ui 2>/dev/null
if ! import_via_database "$db_path"; then
systemctl start x-ui 2>/dev/null
print_status "Database import failed" "error"
exit 1
fi
local output_path="${config_path%.*}_3xui_outbound.json"
echo "$OUTBOUND_JSON" | jq . > "$output_path" 2>/dev/null
if [[ $? -eq 0 ]]; then
print_status "JSON saved to: $output_path" "success"
fi
echo
local restart_ok=0
restart_xui_service && restart_ok=1
echo
print_status "Import completed successfully!" "success"
echo
print_status "${BOLD}Info:${RESET}" "info"
print_status " Outbound 'Phobos' added to config" "info"
if [[ $restart_ok -eq 1 ]]; then
print_status " x-ui service restarted" "info"
else
print_status " x-ui service needs manual restart" "warning"
fi
print_status " JSON config saved to $output_path" "info"
echo
print_status "${BOLD}Verification:${RESET}" "info"
print_status " Open 3x-ui web panel" "info"
print_status " Go to Outbounds section" "info"
print_status " Verify 'Phobos' outbound is present" "info"
echo
}
main "$@"

248
overlay/install-obfuscator.sh Executable file
View File

@@ -0,0 +1,248 @@
OBF_BINARY_NAME="wg-obfuscator"
OBF_CONF_NAME="wg-obfuscator.conf"
OBF_INIT_NAME="S49wg-obfuscator"
OBF_SERVICE_NAME="phobos-obfuscator"
OBF_WG_IFACE="phobos"
resolve_install_names() {
local binary_dir
local binary_path
if [ "$ROUTER_PLATFORM" = "openwrt" ]; then
binary_dir="/usr/bin"
elif [ "$ROUTER_PLATFORM" = "linux" ]; then
binary_dir="/usr/local/bin"
else
binary_dir="/opt/bin"
fi
binary_path="${binary_dir}/wg-obfuscator"
if [ ! -f "$binary_path" ] && [ ! -f "$PHOBOS_DIR/wg-obfuscator.conf" ]; then
return
fi
if [ -f "$binary_path" ] && [ -f "$PHOBOS_DIR/${CLIENT_NAME}.conf" ]; then
return
fi
local idx=1
while [ -f "${binary_dir}/wg-obfuscator${idx}" ]; do
idx=$((idx + 1))
done
local init_num=50
while [ -f "/opt/etc/init.d/S${init_num}wg-obfuscator" ]; do
init_num=$((init_num + 1))
done
OBF_BINARY_NAME="wg-obfuscator${idx}"
OBF_CONF_NAME="wg-obfuscator${idx}.conf"
OBF_INIT_NAME="S${init_num}wg-obfuscator"
OBF_SERVICE_NAME="phobos-obfuscator${idx}"
OBF_WG_IFACE="phobos${idx}"
}
install_obfuscator() {
local arch=$1
log "Установка wg-obfuscator для архитектуры $arch..."
if [ "$OBF_BINARY_NAME" = "wg-obfuscator" ]; then
if [ -f /opt/etc/init.d/S49wg-obfuscator ]; then
log "Остановка текущего процесса wg-obfuscator..."
/opt/etc/init.d/S49wg-obfuscator stop >/dev/null 2>&1 || true
fi
if [ -f /etc/init.d/phobos-obfuscator ]; then
log "Остановка текущего процесса wg-obfuscator..."
/etc/init.d/phobos-obfuscator stop >/dev/null 2>&1 || true
fi
fi
local binary_name="wg-obfuscator-$arch"
if [ ! -f "bin/$binary_name" ]; then
log "Ошибка: бинарник $binary_name не найден в архиве"
log "Доступные бинарники:"
ls -1 bin/
exit 1
fi
local target_path="/opt/bin/${OBF_BINARY_NAME}"
if [ "$ROUTER_PLATFORM" = "openwrt" ]; then
target_path="/usr/bin/${OBF_BINARY_NAME}"
fi
if [ -f "$target_path" ]; then
rm "$target_path"
fi
if [ "$ROUTER_PLATFORM" = "openwrt" ]; then
mkdir -p /usr/bin
else
mkdir -p /opt/bin
fi
cp "bin/$binary_name" "$target_path"
chmod +x "$target_path"
log "Бинарник wg-obfuscator установлен в $target_path"
}
create_init_script() {
log "Создание init-скрипта для obfuscator..."
cat > /opt/etc/init.d/${OBF_INIT_NAME} <<EOF
#!/bin/sh
ENABLED=yes
PROCS=${OBF_BINARY_NAME}
ARGS="--config $PHOBOS_DIR/${OBF_CONF_NAME}"
PREARGS=""
DESC=\$PROCS
PATH=/opt/sbin:/opt/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
. /opt/etc/init.d/rc.func
EOF
chmod +x /opt/etc/init.d/${OBF_INIT_NAME}
log "Init-скрипт создан: /opt/etc/init.d/${OBF_INIT_NAME}"
}
start_obfuscator() {
log "Запуск wg-obfuscator..."
if [ "$ROUTER_PLATFORM" = "keenetic" ] || [ -f /opt/etc/init.d/${OBF_INIT_NAME} ]; then
/opt/etc/init.d/${OBF_INIT_NAME} start
elif [ "$ROUTER_PLATFORM" = "openwrt" ] && [ -f /etc/init.d/${OBF_SERVICE_NAME} ]; then
/etc/init.d/${OBF_SERVICE_NAME} start
/etc/init.d/${OBF_SERVICE_NAME} enable
fi
sleep 2
local process_found=0
if command -v pidof >/dev/null 2>&1 && pidof ${OBF_BINARY_NAME} >/dev/null 2>&1; then
process_found=1
elif command -v pgrep >/dev/null 2>&1 && pgrep -f wg-obfuscator >/dev/null 2>&1; then
process_found=1
elif ps w 2>/dev/null | grep -v grep | grep -q "wg-obfuscator\|/opt/bin/wg-obfuscator\|/usr/bin/wg-obfuscator"; then
process_found=1
elif ps 2>/dev/null | grep -v grep | grep -q "wg-obfuscator"; then
process_found=1
fi
if [ $process_found -eq 0 ]; then
if [ "$ROUTER_PLATFORM" = "keenetic" ] && [ -f /opt/etc/init.d/${OBF_INIT_NAME} ]; then
local status_output=$(/opt/etc/init.d/${OBF_INIT_NAME} status 2>&1 || echo "")
if echo "$status_output" | grep -q "alive"; then
process_found=1
fi
elif [ "$ROUTER_PLATFORM" = "openwrt" ] && [ -f /etc/init.d/${OBF_SERVICE_NAME} ]; then
if /etc/init.d/${OBF_SERVICE_NAME} status >/dev/null 2>&1; then
process_found=1
fi
fi
fi
if [ $process_found -eq 1 ]; then
log "✓ wg-obfuscator успешно запущен"
else
log "✗ wg-obfuscator не запущен. Проверьте логи."
fi
}
create_procd_init_script() {
log "Создание procd init-скрипта для obfuscator..."
cat > /etc/init.d/${OBF_SERVICE_NAME} <<EOF
#!/bin/sh /etc/rc.common
START=49
STOP=51
USE_PROCD=1
PROG=/usr/bin/${OBF_BINARY_NAME}
CONFIG_FILE=/etc/Phobos/${OBF_CONF_NAME}
start_service() {
if [ ! -f "\$PROG" ]; then
echo "Error: wg-obfuscator not found at \$PROG"
return 1
fi
if [ ! -f "\$CONFIG_FILE" ]; then
echo "Error: config not found at \$CONFIG_FILE"
return 1
fi
procd_open_instance
procd_set_param command \$PROG --config \$CONFIG_FILE
procd_set_param respawn
procd_set_param stdout 1
procd_set_param stderr 1
procd_close_instance
}
EOF
chmod +x /etc/init.d/${OBF_SERVICE_NAME}
log "Procd init-скрипт создан: /etc/init.d/${OBF_SERVICE_NAME}"
}
create_systemd_obfuscator_service() {
log "Создание systemd service для obfuscator..."
cat > /etc/systemd/system/${OBF_SERVICE_NAME}.service <<EOFS
[Unit]
Description=Phobos WireGuard Obfuscator
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/${OBF_BINARY_NAME} --config $PHOBOS_DIR/${OBF_CONF_NAME}
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOFS
if [ ! -f /usr/local/bin/${OBF_BINARY_NAME} ]; then
local target_path="/usr/local/bin/${OBF_BINARY_NAME}"
if [ -f /opt/bin/${OBF_BINARY_NAME} ]; then
cp /opt/bin/${OBF_BINARY_NAME} "$target_path"
chmod +x "$target_path"
elif [ -f /usr/bin/${OBF_BINARY_NAME} ]; then
cp /usr/bin/${OBF_BINARY_NAME} "$target_path"
chmod +x "$target_path"
fi
fi
systemctl daemon-reload
systemctl enable ${OBF_SERVICE_NAME}
systemctl start ${OBF_SERVICE_NAME}
log "Ожидание запуска obfuscator (до 10 секунд)..."
local wait_count=0
while [ $wait_count -lt 10 ]; do
sleep 1
if systemctl is-active --quiet ${OBF_SERVICE_NAME}; then
log "✓ Obfuscator успешно запущен"
break
fi
wait_count=$((wait_count + 1))
done
if [ $wait_count -ge 10 ]; then
log "ПРЕДУПРЕЖДЕНИЕ: Obfuscator может быть не готов (таймаут 10 сек)"
fi
log "Systemd service создан: /etc/systemd/system/${OBF_SERVICE_NAME}.service"
}

94
overlay/install-wireguard.sh Executable file
View File

@@ -0,0 +1,94 @@
install_wireguard_linux() {
log "Установка WireGuard для Linux..."
if command -v apt-get >/dev/null 2>&1; then
apt-get update || log "ПРЕДУПРЕЖДЕНИЕ: Не удалось обновить список пакетов"
apt-get install -y wireguard wireguard-tools resolvconf net-tools || {
log "ОШИБКА: Не удалось установить WireGuard"
return 1
}
else
log "ОШИБКА: apt-get не найден. Поддерживаются только Ubuntu/Debian"
return 1
fi
log "✓ WireGuard установлен"
return 0
}
configure_wireguard_linux() {
log ""
log "==> Настройка WireGuard через systemd..."
local wg_interface="${OBF_WG_IFACE}"
local wg_config_dir="/etc/wireguard"
mkdir -p "$wg_config_dir"
cp "$PHOBOS_DIR/${CLIENT_NAME}.conf" "$wg_config_dir/${wg_interface}.conf"
chmod 600 "$wg_config_dir/${wg_interface}.conf"
log "Настройка routing: VPN как запасной интерфейс..."
local client_ip=$(grep '^Address' "$wg_config_dir/${wg_interface}.conf" | cut -d'=' -f2 | tr -d ' ' | cut -d',' -f1 | cut -d'/' -f1)
local route_target=$(echo "$client_ip" | cut -d'.' -f1-2).0.0/16
sed -i '/^MTU/a Table = off' "$wg_config_dir/${wg_interface}.conf"
sed -i "/^Table = off/a PostUp = ip route add $route_target dev %i" "$wg_config_dir/${wg_interface}.conf"
sed -i "/^PostUp/a PostDown = ip route del $route_target dev %i || true" "$wg_config_dir/${wg_interface}.conf"
log "Настройка зависимости WireGuard от obfuscator..."
mkdir -p "/etc/systemd/system/wg-quick@${wg_interface}.service.d"
cat > "/etc/systemd/system/wg-quick@${wg_interface}.service.d/override.conf" <<EOF
[Unit]
After=${OBF_SERVICE_NAME}.service
Requires=${OBF_SERVICE_NAME}.service
EOF
systemctl daemon-reload
log "Включение и запуск WireGuard интерфейса ${wg_interface}..."
systemctl enable wg-quick@${wg_interface} || log "ПРЕДУПРЕЖДЕНИЕ: Не удалось включить автозапуск"
systemctl start wg-quick@${wg_interface} || {
log "ОШИБКА: Не удалось запустить WireGuard"
return 1
}
sleep 3
if systemctl is-active --quiet wg-quick@${wg_interface}; then
log "✓ WireGuard успешно запущен (интерфейс: ${wg_interface})"
log " VPN настроен как запасной интерфейс (не перехватывает системный трафик)"
if wg show ${wg_interface} 2>/dev/null | grep -q "latest handshake"; then
log "✓ Туннель установлен и работает"
else
log " Ожидание установки туннеля..."
fi
return 0
else
log "ПРЕДУПРЕЖДЕНИЕ: WireGuard может быть не запущен, проверьте статус"
return 1
fi
}
configure_ufw_linux() {
log ""
log "==> Проверка UFW firewall..."
if ! command -v ufw >/dev/null 2>&1; then
log "UFW не установлен, пропуск настройки"
return 0
fi
log "✓ UFW обнаружен"
log " Клиентская конфигурация не требует открытия портов:"
log " - obfuscator слушает на 127.0.0.1:13255 (локальный интерфейс)"
log " - obfuscator делает исходящие соединения (разрешены по умолчанию)"
if ufw status 2>/dev/null | grep -q "Status: active"; then
log " UFW активен, клиент будет работать корректно"
else
log " UFW неактивен"
fi
}

93
overlay/lib-client.sh Executable file
View File

@@ -0,0 +1,93 @@
detect_router_platform() {
if [ -f /etc/openwrt_release ] || [ -f /etc/openwrt_version ]; then
echo "openwrt"
elif [ -f /opt/etc/.entware_install_log ] || [ -d /opt/etc/ndm ]; then
echo "keenetic"
elif [ -f /etc/debian_version ]; then
echo "linux"
elif [ -d /etc/config ] && command -v uci >/dev/null 2>&1; then
echo "openwrt"
elif [ -f /opt/etc/init.d/rc.func ]; then
echo "keenetic"
elif [ -d /run/systemd/system ] || pidof systemd >/dev/null 2>&1; then
echo "linux"
else
local uname_output=$(uname -a)
if echo "$uname_output" | grep -qi "Keenetic\|Netcraze"; then
echo "keenetic"
elif echo "$uname_output" | grep -qi "OpenWrt\|LEDE\|ImmortalWrt"; then
echo "openwrt"
elif echo "$uname_output" | grep -qi "Linux"; then
echo "linux"
else
echo "unknown"
fi
fi
}
detect_phobos_dir() {
local platform="$1"
if [ "$platform" = "keenetic" ]; then
echo "/opt/etc/Phobos"
elif [ "$platform" = "openwrt" ]; then
echo "/etc/Phobos"
elif [ "$platform" = "linux" ]; then
echo "/opt/Phobos"
else
if [ -d "/opt" ] && [ -d "/opt/etc" ]; then
echo "/opt/etc/Phobos"
else
echo "/etc/Phobos"
fi
fi
}
detect_arch() {
local arch=$(uname -m)
case "$arch" in
mips)
if [ "$(echo -n I | hexdump -o 2>/dev/null | awk 'NR==1{print $2}')" = "000111" ]; then
echo "mipsel"
else
echo "mips"
fi
;;
aarch64|arm64)
echo "aarch64"
;;
armv7l|armv6l)
echo "armv7"
;;
x86_64)
echo "x86_64"
;;
*)
echo "unknown"
;;
esac
}
check_root() {
if [ "$(id -u)" -ne 0 ]; then
echo "Этот скрипт требует root привилегии. Запустите: su -c '$0'"
exit 1
fi
}
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}
print_status() {
local status="$1"
local message="$2"
if [ "${status}" = "OK" ]; then
printf "\\033[0;32m✓\\033[0m %s\n" "${message}"
elif [ "${status}" = "WARN" ]; then
printf "\\033[1;33m⚠\\033[0m %s\n" "${message}"
else
printf "\\033[0;31m✗\\033[0m %s\n" "${message}"
fi
}

View File

@@ -216,7 +216,7 @@ action_package() {
cp "$dir/wg-obfuscator.conf" "$pkg_root/wg-obfuscator.conf"
# Fetch missing router-arch binaries from ClusterM on-demand
local _tag; _tag=$(curl -fsSL -m10 https://api.github.com/repos/ClusterM/wg-obfuscator/releases/latest 2>/dev/null | grep '"tag_name"' | head -1 | sed 's/.*"tag_name": *"\(.*\)".*//')
local _tag; _tag=$(curl -fsSL -m10 https://api.github.com/repos/ClusterM/wg-obfuscator/releases/latest 2>/dev/null | grep '"tag_name"' | head -1 | sed 's/.*"tag_name": *"\(.*\)".*/\1/')
local _cb="https://github.com/ClusterM/wg-obfuscator/releases/download/${_tag}"
declare -A _am=([aarch64]="linux-arm64" [mipsel]="linux-mipsel-mips32" [mips]="linux-mips-mips32" [armv7]="linux-armv7-hf" [x86_64]="linux-x64")
for _arch in aarch64 mipsel mips armv7 x86_64; do
@@ -235,12 +235,16 @@ action_package() {
local tpl_dir="$REPO_DIR/client/templates"
if [[ -d "$tpl_dir" ]]; then
for required in install-router.sh.template lib-client.sh install-obfuscator.sh install-wireguard.sh; do
[[ -f "$tpl_dir/$required" ]] || die "Обязательный шаблон $required не найден в $tpl_dir. Выполни phobos-update stable и пересоздай пакет."
done
cp "$tpl_dir/install-router.sh.template" "$pkg_root/install-router.sh"
sed -i "s|{{CLIENT_NAME}}|${id}|g" "$pkg_root/install-router.sh"
chmod +x "$pkg_root/install-router.sh"
[[ -f "$tpl_dir/lib-client.sh" ]] && cp "$tpl_dir/lib-client.sh" "$pkg_root/lib-client.sh"
[[ -f "$tpl_dir/install-obfuscator.sh" ]] && cp "$tpl_dir/install-obfuscator.sh" "$pkg_root/install-obfuscator.sh"
[[ -f "$tpl_dir/install-wireguard.sh" ]] && cp "$tpl_dir/install-wireguard.sh" "$pkg_root/install-wireguard.sh"
cp "$tpl_dir/lib-client.sh" "$pkg_root/lib-client.sh"
cp "$tpl_dir/install-obfuscator.sh" "$pkg_root/install-obfuscator.sh"
cp "$tpl_dir/install-wireguard.sh" "$pkg_root/install-wireguard.sh"
chmod +x "$pkg_root/lib-client.sh" "$pkg_root/install-obfuscator.sh" "$pkg_root/install-wireguard.sh"
for f in router-configure-wireguard router-configure-wireguard-openwrt phobos-uninstall 3xui; do
[[ -f "$tpl_dir/$f.sh" ]] && cp "$tpl_dir/$f.sh" "$pkg_root/$f.sh" && chmod +x "$pkg_root/$f.sh"
done

543
overlay/phobos-uninstall.sh Executable file
View File

@@ -0,0 +1,543 @@
#!/bin/sh
set -e
PHOBOS_DIR=""
ROUTER_PLATFORM=""
SELECTED_CONF=""
IS_FULL_REMOVAL="1"
OBFUSCATOR_CONFIGS=""
OBFUSCATOR_CONFIG_COUNT=0
. "$(dirname "$0")/lib-client.sh"
collect_obf_configs() {
OBFUSCATOR_CONFIGS=""
OBFUSCATOR_CONFIG_COUNT=0
[ -d "$PHOBOS_DIR" ] || return
for f in "$PHOBOS_DIR"/wg-obfuscator*.conf; do
[ -f "$f" ] || continue
OBFUSCATOR_CONFIGS="${OBFUSCATOR_CONFIGS}$(basename "$f" .conf) "
OBFUSCATOR_CONFIG_COUNT=$((OBFUSCATOR_CONFIG_COUNT + 1))
done
}
get_config_label() {
local obf_conf_base="$1"
local idx_suffix="${obf_conf_base#wg-obfuscator}"
local wg_iface="phobos${idx_suffix}"
local addr=""
if [ -f "/etc/wireguard/${wg_iface}.conf" ]; then
addr=$(grep '^Address' "/etc/wireguard/${wg_iface}.conf" 2>/dev/null | cut -d'=' -f2 | tr -d ' ' | cut -d'/' -f1 | cut -d',' -f1) || addr=""
fi
if [ -n "$addr" ]; then
printf "%s [интерфейс: %s, адрес: %s]" "$obf_conf_base" "$wg_iface" "$addr"
else
printf "%s" "$obf_conf_base"
fi
}
select_config_to_remove() {
collect_obf_configs
if [ "$OBFUSCATOR_CONFIG_COUNT" -eq 0 ]; then
IS_FULL_REMOVAL="1"
SELECTED_CONF="all"
return
fi
if [ "$OBFUSCATOR_CONFIG_COUNT" -eq 1 ]; then
SELECTED_CONF=$(echo "$OBFUSCATOR_CONFIGS" | tr -s ' ' '\n' | grep -v '^$' | head -1)
IS_FULL_REMOVAL="1"
log "Обнаружена конфигурация: ${SELECTED_CONF}"
return
fi
log ""
log "Обнаружено несколько конфигураций:"
log ""
local i=1
for conf_base in $OBFUSCATOR_CONFIGS; do
local label
label=$(get_config_label "$conf_base")
log " ${i}) ${label}"
i=$((i + 1))
done
local all_num=$i
log " ${all_num}) Удалить все"
log ""
printf "Введите номер конфигурации для удаления: "
read -r answer || answer=""
case "$answer" in
''|*[!0-9]*)
log "ОШИБКА: Неверный ввод"
exit 1
;;
esac
if [ "$answer" -eq "$all_num" ]; then
IS_FULL_REMOVAL="1"
SELECTED_CONF="all"
return
fi
if [ "$answer" -lt 1 ] || [ "$answer" -ge "$all_num" ]; then
log "ОШИБКА: Номер вне диапазона"
exit 1
fi
local selected_i=1
for conf_base in $OBFUSCATOR_CONFIGS; do
if [ "$selected_i" -eq "$answer" ]; then
SELECTED_CONF="$conf_base"
IS_FULL_REMOVAL="0"
return
fi
selected_i=$((selected_i + 1))
done
}
remove_single_obfuscator_instance() {
local obf_conf_base="$1"
local idx_suffix="${obf_conf_base#wg-obfuscator}"
local binary_name="wg-obfuscator${idx_suffix}"
local service_name="phobos-obfuscator${idx_suffix}"
local wg_iface="phobos${idx_suffix}"
log "Удаление конфигурации ${obf_conf_base}..."
local link_file="$PHOBOS_DIR/${obf_conf_base}.link"
local linked_client=""
if [ -f "$link_file" ]; then
linked_client=$(cat "$link_file" 2>/dev/null) || linked_client=""
fi
local privkey_fallback=""
if [ -z "$linked_client" ] && [ "$ROUTER_PLATFORM" = "linux" ] && [ -f "/etc/wireguard/${wg_iface}.conf" ]; then
privkey_fallback=$(grep '^PrivateKey' "/etc/wireguard/${wg_iface}.conf" 2>/dev/null | cut -d'=' -f2- | tr -d ' \t') || privkey_fallback=""
fi
for f in /opt/etc/init.d/S[0-9]*wg-obfuscator*; do
[ -f "$f" ] || continue
if grep -q "^PROCS=${binary_name}$" "$f" 2>/dev/null; then
"$f" stop >/dev/null 2>&1 || true
rm -f "$f"
log " ✓ Удален init-скрипт: $f"
break
fi
done
if [ -f "/etc/init.d/${service_name}" ]; then
/etc/init.d/${service_name} stop >/dev/null 2>&1 || true
/etc/init.d/${service_name} disable >/dev/null 2>&1 || true
rm -f "/etc/init.d/${service_name}"
log " ✓ Удален procd init-скрипт: /etc/init.d/${service_name}"
fi
if command -v systemctl >/dev/null 2>&1 && [ -f "/etc/systemd/system/${service_name}.service" ]; then
systemctl stop "${service_name}" >/dev/null 2>&1 || true
systemctl disable "${service_name}" >/dev/null 2>&1 || true
rm -f "/etc/systemd/system/${service_name}.service"
log " ✓ Удален systemd сервис: /etc/systemd/system/${service_name}.service"
fi
for dir in /opt/bin /usr/bin /usr/local/bin; do
if [ -f "${dir}/${binary_name}" ]; then
rm -f "${dir}/${binary_name}"
log " ✓ Удален бинарник: ${dir}/${binary_name}"
fi
done
if [ "$ROUTER_PLATFORM" = "linux" ] && command -v systemctl >/dev/null 2>&1; then
systemctl stop "wg-quick@${wg_iface}" >/dev/null 2>&1 || true
systemctl disable "wg-quick@${wg_iface}" >/dev/null 2>&1 || true
if [ -d "/etc/systemd/system/wg-quick@${wg_iface}.service.d" ]; then
rm -rf "/etc/systemd/system/wg-quick@${wg_iface}.service.d"
log " ✓ Удален systemd override: wg-quick@${wg_iface}"
fi
if [ -f "/etc/wireguard/${wg_iface}.conf" ]; then
rm -f "/etc/wireguard/${wg_iface}.conf"
log " ✓ Удален WireGuard конфиг: /etc/wireguard/${wg_iface}.conf"
fi
systemctl daemon-reload >/dev/null 2>&1 || true
elif [ "$ROUTER_PLATFORM" = "keenetic" ]; then
if command -v curl >/dev/null 2>&1 && command -v jq >/dev/null 2>&1 && [ -n "$linked_client" ]; then
local target_desc="Phobos-${linked_client}"
log " Удаление WireGuard интерфейса Keenetic: ${target_desc}..."
local iface_list=$(curl -s "http://127.0.0.1:79/rci/show/interface" 2>/dev/null || echo "")
if [ -n "$iface_list" ] && echo "$iface_list" | jq -e . >/dev/null 2>&1; then
local iface_id=$(echo "$iface_list" | jq -r --arg desc "$target_desc" 'to_entries[] | select((.value.description? // "") == $desc) | .key' 2>/dev/null)
if [ -n "$iface_id" ]; then
local del_json=$(cat <<EOF
{
"interface": {
"$iface_id": {
"no": true
}
}
}
EOF
)
local del_result=$(echo "$del_json" | curl -s -X POST \
-H "Content-Type: application/json" \
-d @- \
"http://127.0.0.1:79/rci/" 2>/dev/null)
if echo "$del_result" | jq -e '.status == "error"' >/dev/null 2>&1; then
local err_msg=$(echo "$del_result" | jq -r '.message // "Unknown error"' 2>/dev/null)
log " ОШИБКА при удалении ${iface_id}: $err_msg"
else
log " ✓ Интерфейс ${iface_id} (${target_desc}) удален"
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"system":{"configuration":{"save":{}}}}' \
"http://127.0.0.1:79/rci/" >/dev/null 2>&1
log " ✓ Конфигурация сохранена"
fi
else
log " Интерфейс с описанием '${target_desc}' не найден"
fi
else
log " RCI API недоступен, пропускаем удаление интерфейса Keenetic"
fi
else
log " curl/jq не доступны или имя клиента не определено, пропускаем удаление интерфейса Keenetic"
fi
elif [ "$ROUTER_PLATFORM" = "openwrt" ]; then
remove_wireguard_interfaces_openwrt
remove_firewall_zone_openwrt
fi
if [ -f "$PHOBOS_DIR/${obf_conf_base}.conf" ]; then
rm -f "$PHOBOS_DIR/${obf_conf_base}.conf"
log " ✓ Удален конфиг obfuscator: $PHOBOS_DIR/${obf_conf_base}.conf"
fi
[ -f "$link_file" ] && rm -f "$link_file"
if [ -n "$linked_client" ] && [ -f "$PHOBOS_DIR/${linked_client}.conf" ]; then
rm -f "$PHOBOS_DIR/${linked_client}.conf"
log " ✓ Удален конфиг клиента: ${linked_client}.conf"
elif [ -n "$privkey_fallback" ]; then
for client_conf in "$PHOBOS_DIR"/*.conf; do
[ -f "$client_conf" ] || continue
case "$(basename "$client_conf")" in
wg-obfuscator*.conf) continue ;;
esac
if grep -qF "PrivateKey = ${privkey_fallback}" "$client_conf" 2>/dev/null; then
rm -f "$client_conf"
log " ✓ Удален конфиг клиента: $(basename "$client_conf")"
break
fi
done
fi
local remaining=0
for f in "$PHOBOS_DIR"/*.conf; do
[ -f "$f" ] && remaining=$((remaining + 1))
done
if [ "$remaining" -eq 0 ] && [ -d "$PHOBOS_DIR" ]; then
rm -rf "$PHOBOS_DIR"
log " ✓ Удалена директория: $PHOBOS_DIR"
fi
}
stop_obfuscator() {
log "Остановка wg-obfuscator..."
for f in /opt/etc/init.d/S[0-9]*wg-obfuscator*; do
[ -f "$f" ] && "$f" stop >/dev/null 2>&1 || true
done
for f in /etc/init.d/phobos-obfuscator*; do
if [ -f "$f" ]; then
"$f" stop >/dev/null 2>&1 || true
"$f" disable >/dev/null 2>&1 || true
fi
done
if command -v systemctl >/dev/null 2>&1; then
systemctl list-units --no-legend --plain 'phobos-obfuscator*.service' | awk '{print $1}' | while read -r unit; do
systemctl stop "$unit" >/dev/null 2>&1 || true
systemctl disable "$unit" >/dev/null 2>&1 || true
done
fi
if ps | grep -v grep | grep -q wg-obfuscator; then
log " Принудительное завершение процесса..."
pkill -f wg-obfuscator >/dev/null 2>&1 || true
fi
}
remove_wireguard_interfaces_keenetic() {
log "Удаление WireGuard интерфейсов Phobos (Keenetic)..."
if ! command -v curl >/dev/null 2>&1 || ! command -v jq >/dev/null 2>&1; then
log " curl или jq не установлен, пропускаем удаление интерфейсов"
return 0
fi
local interfaces=$(curl -s "http://127.0.0.1:79/rci/show/interface" 2>/dev/null || echo "")
if [ -z "$interfaces" ] || ! echo "$interfaces" | jq -e . >/dev/null 2>&1; then
log " RCI API недоступен или вернул некорректный JSON"
return 0
fi
local removed_count=0
local phobos_interfaces=$(echo "$interfaces" | jq -r 'to_entries[] | select(.value.description? // "" | startswith("Phobos-")) | .key' 2>/dev/null)
if [ -z "$phobos_interfaces" ]; then
log " Интерфейсы Phobos не найдены в системе"
return 0
fi
echo "$phobos_interfaces" | while read -r interface_id; do
if [ -z "$interface_id" ]; then
continue
fi
local interface_desc=$(echo "$interfaces" | jq -r --arg id "$interface_id" '.[$id].description // "unknown"' 2>/dev/null)
log " Удаление интерфейса: $interface_id ($interface_desc)"
local delete_json=$(cat <<EOF
{
"interface": {
"$interface_id": {
"no": true
}
}
}
EOF
)
local result=$(echo "$delete_json" | curl -s -X POST \
-H "Content-Type: application/json" \
-d @- \
"http://127.0.0.1:79/rci/" 2>/dev/null)
if echo "$result" | jq -e '.status == "error"' >/dev/null 2>&1; then
local error_msg=$(echo "$result" | jq -r '.message // "Unknown error"' 2>/dev/null)
log " ОШИБКА при удалении $interface_id: $error_msg"
else
log " ✓ Интерфейс $interface_id удален"
removed_count=$((removed_count + 1))
fi
done
local save_result=$(curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"system":{"configuration":{"save":{}}}}' \
"http://127.0.0.1:79/rci/" 2>/dev/null)
if [ $removed_count -gt 0 ]; then
log "✓ Удалено интерфейсов: $removed_count"
log "✓ Конфигурация сохранена"
fi
}
remove_wireguard_interfaces_openwrt() {
log "Удаление WireGuard интерфейсов Phobos (OpenWRT)..."
if ! command -v uci >/dev/null 2>&1; then
log " uci не установлен, пропускаем удаление интерфейсов"
return 0
fi
local interface_name="phobos_wg"
if uci -q get network.${interface_name} >/dev/null 2>&1; then
log " Удаление интерфейса: ${interface_name}"
ifdown ${interface_name} >/dev/null 2>&1 || true
uci -q delete network.${interface_name} || true
local peers=$(uci show network 2>/dev/null | grep "wireguard_${interface_name}" | cut -d'.' -f2 | cut -d'=' -f1 | sort -u)
for peer in $peers; do
uci -q delete network.$peer || true
done
uci commit network
log " ✓ Интерфейс ${interface_name} удален"
else
log " Интерфейс ${interface_name} не найден"
fi
/etc/init.d/network reload >/dev/null 2>&1 || true
}
remove_firewall_zone_openwrt() {
log "Удаление файрволл зоны Phobos (OpenWRT)..."
if ! command -v uci >/dev/null 2>&1; then
log " uci не установлен, пропускаем удаление зоны"
return 0
fi
local zone_name="phobos"
if uci -q get firewall.${zone_name} >/dev/null 2>&1; then
log " Удаление зоны: ${zone_name}"
uci -q delete firewall.${zone_name} || true
uci commit firewall
log " ✓ Зона ${zone_name} удалена"
/etc/init.d/firewall reload >/dev/null 2>&1 || true
else
log " Зона ${zone_name} не найдена"
fi
}
remove_wireguard_interfaces_linux() {
log "Удаление WireGuard интерфейсов Phobos (Linux)..."
if ! command -v systemctl >/dev/null 2>&1; then
log " systemctl не найден, пропускаем удаление интерфейсов"
return 0
fi
for wg_conf in /etc/wireguard/phobos*.conf; do
[ -f "$wg_conf" ] || continue
local wg_interface
wg_interface=$(basename "$wg_conf" .conf)
if systemctl is-enabled --quiet wg-quick@${wg_interface} 2>/dev/null || systemctl is-active --quiet wg-quick@${wg_interface} 2>/dev/null; then
log " Остановка и удаление WireGuard сервиса: wg-quick@${wg_interface}"
systemctl stop wg-quick@${wg_interface} >/dev/null 2>&1 || true
systemctl disable wg-quick@${wg_interface} >/dev/null 2>&1 || true
log " ✓ Сервис wg-quick@${wg_interface} остановлен и отключен"
fi
if [ -d "/etc/systemd/system/wg-quick@${wg_interface}.service.d" ]; then
log " Удаление systemd override: /etc/systemd/system/wg-quick@${wg_interface}.service.d"
rm -rf "/etc/systemd/system/wg-quick@${wg_interface}.service.d"
log " ✓ Systemd override удален"
fi
log " Удаление конфигурации: $wg_conf"
rm -f "$wg_conf"
log " ✓ Конфигурационный файл WireGuard удален"
done
systemctl daemon-reload >/dev/null 2>&1 || true
}
remove_files() {
log "Удаление файлов Phobos..."
for f in /opt/etc/init.d/S[0-9]*wg-obfuscator*; do
if [ -f "$f" ]; then
rm -f "$f"
log " ✓ Удален init-скрипт: $f"
fi
done
for f in /etc/init.d/phobos-obfuscator*; do
if [ -f "$f" ]; then
rm -f "$f"
log " ✓ Удален procd init-скрипт: $f"
fi
done
for f in /opt/bin/wg-obfuscator*; do
if [ -f "$f" ]; then
rm -f "$f"
log " ✓ Удален бинарник: $f"
fi
done
for f in /usr/bin/wg-obfuscator*; do
if [ -f "$f" ]; then
rm -f "$f"
log " ✓ Удален бинарник: $f"
fi
done
for f in /usr/local/bin/wg-obfuscator*; do
if [ -f "$f" ]; then
rm -f "$f"
log " ✓ Удален бинарник: $f"
fi
done
local daemon_reload_needed=0
for f in /etc/systemd/system/phobos-obfuscator*.service; do
if [ -f "$f" ]; then
rm -f "$f"
daemon_reload_needed=1
log " ✓ Удален systemd сервис: $f"
fi
done
if [ $daemon_reload_needed -eq 1 ]; then
systemctl daemon-reload >/dev/null 2>&1 || true
fi
if [ -d "$PHOBOS_DIR" ]; then
rm -rf "$PHOBOS_DIR"
log " ✓ Удалена директория: $PHOBOS_DIR"
fi
}
show_final_info() {
log ""
log "╔════════════════════════════════════════════════════════════╗"
log "║ Phobos успешно удален ║"
log "╚════════════════════════════════════════════════════════════╝"
log ""
log "Удалены компоненты:"
log " - wg-obfuscator (процесс, init-скрипт/сервис, бинарник)"
log " - WireGuard интерфейсы Phobos"
log " - Конфигурационные файлы"
log ""
}
main() {
ROUTER_PLATFORM=$(detect_router_platform)
PHOBOS_DIR=$(detect_phobos_dir "$ROUTER_PLATFORM")
log "==> Начало удаления Phobos (платформа: $ROUTER_PLATFORM)"
log "==> Директория: $PHOBOS_DIR"
check_root
select_config_to_remove
if [ "$IS_FULL_REMOVAL" = "1" ]; then
stop_obfuscator
if [ "$ROUTER_PLATFORM" = "keenetic" ]; then
remove_wireguard_interfaces_keenetic
elif [ "$ROUTER_PLATFORM" = "openwrt" ]; then
remove_wireguard_interfaces_openwrt
remove_firewall_zone_openwrt
elif [ "$ROUTER_PLATFORM" = "linux" ]; then
remove_wireguard_interfaces_linux
else
log "ПРЕДУПРЕЖДЕНИЕ: Неизвестная платформа, пропускаем удаление WireGuard интерфейсов"
fi
remove_files
show_final_info
else
log "==> Удаление конфигурации: ${SELECTED_CONF}"
remove_single_obfuscator_instance "$SELECTED_CONF"
log ""
log "╔════════════════════════════════════════════════════════════╗"
log "║ Конфигурация успешно удалена ║"
log "╚════════════════════════════════════════════════════════════╝"
log ""
fi
log "==> Удаление завершено."
}
main "$@"

View File

@@ -0,0 +1,349 @@
#!/bin/sh
set -e
CLIENT_NAME=""
CLIENT_PRIVATE_KEY=""
CLIENT_IP=""
CLIENT_IPV6=""
SERVER_PUBLIC_KEY=""
ENDPOINT_PORT=13255
KEEPALIVE=25
MTU=1420
FALLBACK_CONFIG=""
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}
error() {
echo "[ERROR] $*" >&2
log "ERROR: $*"
}
usage() {
cat <<EOF
Usage: $0 [OPTIONS]
Options:
--client-name NAME Client name (required)
--client-private-key KEY WireGuard private key (required)
--client-ip IP Client tunnel IPv4 address (required)
--client-ipv6 IP Client tunnel IPv6 address (required)
--server-public-key KEY Server WireGuard public key (required)
--endpoint-port PORT Local obfuscator port (default: 13255)
--keepalive SECONDS Keepalive interval (default: 25)
--mtu MTU Interface MTU (default: 1420)
--fallback-config PATH Path to fallback .conf file
--help Show this help
Example:
$0 --client-name home \\
--client-private-key "ABCD..." \\
--client-ip 10.25.0.4/32 \\
--client-ipv6 fd00:10:25::4/128 \\
--server-public-key "EFGH..." \\
--endpoint-port 13255 \\
--fallback-config /opt/etc/Phobos/home.conf
EOF
exit 1
}
check_dependencies() {
local missing=""
for cmd in uci wg; do
if ! command -v "$cmd" >/dev/null 2>&1; then
missing="$missing $cmd"
fi
done
if [ -n "$missing" ]; then
error "Missing required utilities:$missing"
return 1
fi
return 0
}
parse_args() {
while [ $# -gt 0 ]; do
case "$1" in
--client-name)
CLIENT_NAME="$2"
shift 2
;;
--client-private-key)
CLIENT_PRIVATE_KEY="$2"
shift 2
;;
--client-ip)
CLIENT_IP="$2"
shift 2
;;
--client-ipv6)
CLIENT_IPV6="$2"
shift 2
;;
--server-public-key)
SERVER_PUBLIC_KEY="$2"
shift 2
;;
--endpoint-port)
ENDPOINT_PORT="$2"
shift 2
;;
--keepalive)
KEEPALIVE="$2"
shift 2
;;
--mtu)
MTU="$2"
shift 2
;;
--fallback-config)
FALLBACK_CONFIG="$2"
shift 2
;;
--help)
usage
;;
*)
error "Unknown option: $1"
usage
;;
esac
done
if [ -z "${CLIENT_NAME}" ] || [ -z "${CLIENT_PRIVATE_KEY}" ] || \
[ -z "${CLIENT_IP}" ] || [ -z "${CLIENT_IPV6}" ] || \
[ -z "${SERVER_PUBLIC_KEY}" ]; then
error "Missing required parameters"
usage
fi
}
check_existing_interface() {
local interface_name="phobos_wg"
if uci -q get network.${interface_name} >/dev/null 2>&1; then
log "Найден существующий интерфейс: ${interface_name}"
return 0
else
log "Существующий интерфейс Phobos не найден"
return 1
fi
}
remove_existing_interface() {
local interface_name="phobos_wg"
log "Удаление существующего интерфейса ${interface_name}..."
uci -q delete network.${interface_name} || true
local peers=$(uci show network | grep "wireguard_${interface_name}" | cut -d'.' -f2 | cut -d'=' -f1 | sort -u)
for peer in $peers; do
uci -q delete network.$peer || true
done
uci commit network
log "Существующий интерфейс удален"
}
configure_wireguard_interface() {
local interface_name="phobos_wg"
log "Настройка интерфейса ${interface_name} через UCI..."
uci set network.${interface_name}=interface
uci set network.${interface_name}.proto='wireguard'
uci set network.${interface_name}.private_key="${CLIENT_PRIVATE_KEY}"
uci set network.${interface_name}.mtu="${MTU}"
uci add_list network.${interface_name}.addresses="${CLIENT_IP}"
if [ "${CLIENT_IPV6}" != "none" ] && [ -n "${CLIENT_IPV6}" ]; then
uci add_list network.${interface_name}.addresses="${CLIENT_IPV6}"
fi
local peer_name="wgpeer_${interface_name}"
uci add network wireguard_${interface_name}
uci rename network.@wireguard_${interface_name}[-1]="${peer_name}"
uci set network.${peer_name}.public_key="${SERVER_PUBLIC_KEY}"
uci set network.${peer_name}.description="Phobos VPS Server"
uci set network.${peer_name}.endpoint_host='127.0.0.1'
uci set network.${peer_name}.endpoint_port="${ENDPOINT_PORT}"
uci set network.${peer_name}.persistent_keepalive="${KEEPALIVE}"
uci set network.${peer_name}.route_allowed_ips='0'
uci add_list network.${peer_name}.allowed_ips='0.0.0.0/0'
uci add_list network.${peer_name}.allowed_ips='::/0'
uci commit network
log "Интерфейс ${interface_name} настроен ✓"
return 0
}
configure_firewall_zone() {
local zone_name="phobos"
local interface_name="phobos_wg"
log "Настройка файрволла для зоны ${zone_name}..."
if uci -q get firewall.${zone_name} >/dev/null 2>&1; then
log "Зона ${zone_name} уже существует, обновляем..."
uci delete firewall.${zone_name}
fi
uci add firewall zone
uci rename firewall.@zone[-1]="${zone_name}"
uci set firewall.${zone_name}.name="${zone_name}"
uci add_list firewall.${zone_name}.network="${interface_name}"
uci set firewall.${zone_name}.input='REJECT'
uci set firewall.${zone_name}.output='ACCEPT'
uci set firewall.${zone_name}.forward='REJECT'
uci set firewall.${zone_name}.masq='0'
uci set firewall.${zone_name}.mtu_fix='1'
uci commit firewall
log "Файрволл зона ${zone_name} настроена ✓"
return 0
}
restart_network_services() {
log "Перезапуск сетевых сервисов..."
/etc/init.d/network reload >/dev/null 2>&1 || true
sleep 3
ifup phobos_wg >/dev/null 2>&1 || true
sleep 2
/etc/init.d/firewall reload >/dev/null 2>&1 || true
sleep 1
log "Сетевые сервисы перезапущены ✓"
}
verify_interface_created() {
log "Проверка создания интерфейса WireGuard..."
local max_attempts=5
local attempt=1
while [ $attempt -le $max_attempts ]; do
if ip link show phobos_wg >/dev/null 2>&1; then
log "Интерфейс phobos_wg найден (попытка $attempt/$max_attempts)"
if wg show phobos_wg >/dev/null 2>&1; then
log "WireGuard интерфейс phobos_wg активен"
if uci -q get network.phobos_wg >/dev/null 2>&1; then
log "UCI конфигурация phobos_wg найдена"
return 0
else
log "UCI конфигурация phobos_wg не найдена, но интерфейс работает"
return 0
fi
else
log "Интерфейс найден, но WireGuard не активен (попытка $attempt/$max_attempts)"
fi
else
log "Интерфейс phobos_wg еще не создан (попытка $attempt/$max_attempts)..."
fi
if [ $attempt -lt $max_attempts ]; then
sleep 2
fi
attempt=$((attempt + 1))
done
error "Интерфейс phobos_wg не найден после $max_attempts попыток"
return 1
}
show_fallback_instructions() {
cat <<EOF
╔═══════════════════════════════════════════════════════════╗
║ Требуется ручная настройка WireGuard ║
╚═══════════════════════════════════════════════════════════╝
Конфигурация сохранена в: ${FALLBACK_CONFIG}
Инструкция по ручной настройке:
1. Установите пакеты: opkg install kmod-wireguard wireguard-tools wireguard-tools
2. Импортируйте конфигурацию из ${FALLBACK_CONFIG}
3. Настройте интерфейс через UCI или LuCI веб-интерфейс
EOF
}
main() {
parse_args "$@"
if ! check_dependencies; then
show_fallback_instructions
exit 1
fi
log "=== Phobos WireGuard OpenWRT Configuration ==="
log "Клиент: ${CLIENT_NAME}"
if check_existing_interface; then
remove_existing_interface
fi
if ! configure_wireguard_interface; then
error "Не удалось настроить WireGuard через UCI"
show_fallback_instructions
exit 1
fi
if ! configure_firewall_zone; then
error "Не удалось настроить файрволл"
exit 1
fi
restart_network_services
log ""
log "Ожидание применения конфигурации..."
sleep 5
if verify_interface_created; then
log ""
log "WireGuard успешно настроен на OpenWRT"
log ""
log "Интерфейс: phobos_wg"
log "Файрволл зона: phobos (без форвардинга)"
log ""
log "Для маршрутизации трафика через туннель настройте правила"
log "файрволла и маршрутизацию вручную через LuCI или UCI."
log ""
log "Текущий статус интерфейса:"
ip addr show phobos_wg 2>/dev/null | sed 's/^/ /' || true
log ""
exit 0
else
log ""
log "Не удалось подтвердить создание интерфейса WireGuard"
log ""
log "Проверьте вручную:"
log " ip link show phobos_wg"
log " wg show phobos_wg"
log " uci show network.phobos_wg"
log ""
log "Если интерфейс существует (ip link show phobos_wg работает),"
log "то настройка прошла успешно, несмотря на ошибку проверки."
log ""
exit 1
fi
}
main "$@"

View File

@@ -50,8 +50,14 @@ $PANEL_DIR/app.py|app.py
$PHOBOS_DIR/repo/server/scripts/lib-core.sh|overlay/lib-core.sh
$PHOBOS_DIR/repo/server/scripts/phobos-client.sh|overlay/phobos-client.sh
$PHOBOS_DIR/repo/client/templates/install-router.sh.template|overlay/install-router.sh.template
$PHOBOS_DIR/repo/client/templates/lib-client.sh|overlay/lib-client.sh
$PHOBOS_DIR/repo/client/templates/install-obfuscator.sh|overlay/install-obfuscator.sh
$PHOBOS_DIR/repo/client/templates/install-wireguard.sh|overlay/install-wireguard.sh
$PHOBOS_DIR/repo/client/templates/router-configure-wireguard.sh|overlay/router-configure-wireguard.sh
$PHOBOS_DIR/repo/client/templates/router-configure-wireguard-openwrt.sh|overlay/router-configure-wireguard-openwrt.sh
$PHOBOS_DIR/repo/client/templates/phobos-pull.sh|overlay/phobos-pull.sh
$PHOBOS_DIR/repo/client/templates/phobos-uninstall.sh|overlay/phobos-uninstall.sh
$PHOBOS_DIR/repo/client/templates/3xui.sh|overlay/3xui.sh
$PHOBOS_DIR/server/phobos-health.sh|server/phobos-health.sh
$PHOBOS_DIR/server/phobos-pull.sh|server/phobos-pull.sh
$PHOBOS_DIR/server/phobos-router-watchdog.py|server/phobos-router-watchdog.py