mirror of
https://github.com/andrey271192/PCA_Phobos.git
synced 2026-09-20 11:55:32 +00:00
fix: ship router package helpers
This commit is contained in:
@@ -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
41
app.py
@@ -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
|
||||
|
||||
@@ -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]` должны быть маршруты:
|
||||
|
||||
@@ -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
456
overlay/3xui.sh
Executable 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
248
overlay/install-obfuscator.sh
Executable 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
94
overlay/install-wireguard.sh
Executable 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
93
overlay/lib-client.sh
Executable 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
|
||||
}
|
||||
@@ -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
543
overlay/phobos-uninstall.sh
Executable 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 "$@"
|
||||
349
overlay/router-configure-wireguard-openwrt.sh
Executable file
349
overlay/router-configure-wireguard-openwrt.sh
Executable 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 "$@"
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user