mirror of
https://github.com/andrey271192/PCA_Phobos.git
synced 2026-09-21 12:01:56 +00:00
turnkey installer: full primary stack one-command + session improvements
- install.sh: rewritten as self-contained turnkey primary installer (deps, wg-obfuscator from Ground-Zerro, wg0, obfuscator services, Phobos repo + PCA overlay patches, web panel, nginx, router watchdog). - app.py: current panel (RU/EN, tunnel-pull config endpoint, fan-out, load-aware rebalance, online-anywhere status, '?' help). - overlay/: patched onboarding scripts (phobos-client.sh 403 fix, install-router.sh.template tunnel-pull+cron+client_id, router-configure-wireguard public WG, phobos-pull.sh tunnel-first). - server/: phobos-health.sh (self-heal+apply-server), phobos-pull.sh, phobos-router-watchdog.py, api.py (agent + /api/router-config).
This commit is contained in:
669
overlay/install-router.sh.template
Normal file
669
overlay/install-router.sh.template
Normal file
@@ -0,0 +1,669 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
CLIENT_NAME="{{CLIENT_NAME}}"
|
||||
PHOBOS_DIR=""
|
||||
ROUTER_PLATFORM=""
|
||||
|
||||
. "$(dirname "$0")/lib-client.sh"
|
||||
. "$(dirname "$0")/install-obfuscator.sh"
|
||||
. "$(dirname "$0")/install-wireguard.sh"
|
||||
|
||||
detect_3xui_mode() {
|
||||
local platform="$1"
|
||||
|
||||
if [ "$platform" = "linux" ] && [ -f /etc/x-ui/x-ui.db ]; then
|
||||
echo "true"
|
||||
else
|
||||
echo "false"
|
||||
fi
|
||||
}
|
||||
|
||||
check_dependencies() {
|
||||
local platform="$1"
|
||||
local missing=""
|
||||
local base_deps="grep cut date tee tar curl jq"
|
||||
local platform_deps=""
|
||||
|
||||
if [ "$platform" = "openwrt" ]; then
|
||||
platform_deps="uci"
|
||||
fi
|
||||
|
||||
for cmd in $base_deps $platform_deps; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
missing="$missing $cmd"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$missing" ]; then
|
||||
log "ВНИМАНИЕ: Отсутствуют необходимые утилиты:$missing"
|
||||
|
||||
if [ "$platform" = "linux" ]; then
|
||||
log "Устанавливаю недостающие пакеты через apt-get..."
|
||||
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
if ! apt-get update; then
|
||||
log "ОШИБКА: Не удалось обновить список пакетов apt-get"
|
||||
return 1
|
||||
fi
|
||||
|
||||
for cmd in $missing; do
|
||||
log "Установка $cmd..."
|
||||
if ! apt-get install -y "$cmd" 2>/dev/null; then
|
||||
log "ПРЕДУПРЕЖДЕНИЕ: Не удалось установить $cmd через apt-get"
|
||||
fi
|
||||
done
|
||||
else
|
||||
log "ОШИБКА: apt-get не найден. Невозможно установить зависимости."
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log "Устанавливаю недостающие пакеты через opkg..."
|
||||
|
||||
if command -v opkg >/dev/null 2>&1; then
|
||||
if ! opkg update; then
|
||||
log "ОШИБКА: Не удалось обновить список пакетов opkg"
|
||||
return 1
|
||||
fi
|
||||
|
||||
for cmd in $missing; do
|
||||
log "Установка $cmd..."
|
||||
if ! opkg install "$cmd" 2>/dev/null; then
|
||||
log "ПРЕДУПРЕЖДЕНИЕ: Не удалось установить $cmd через opkg"
|
||||
fi
|
||||
done
|
||||
else
|
||||
log "ОШИБКА: opkg не найден. Невозможно установить зависимости."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
setup_configs() {
|
||||
log "Настройка конфигураций..."
|
||||
|
||||
mkdir -p "$PHOBOS_DIR"
|
||||
|
||||
cp wg-obfuscator.conf "$PHOBOS_DIR/${OBF_CONF_NAME}"
|
||||
chmod 600 "$PHOBOS_DIR/${OBF_CONF_NAME}"
|
||||
|
||||
cp "${CLIENT_NAME}.conf" "$PHOBOS_DIR/${CLIENT_NAME}.conf"
|
||||
chmod 600 "$PHOBOS_DIR/${CLIENT_NAME}.conf"
|
||||
|
||||
printf '%s' "${CLIENT_NAME}" > "$PHOBOS_DIR/${OBF_CONF_NAME%.conf}.link"
|
||||
|
||||
if [ "$OBF_BINARY_NAME" != "wg-obfuscator" ]; then
|
||||
local used_ports=""
|
||||
for existing_conf in "$PHOBOS_DIR"/wg-obfuscator*.conf; do
|
||||
[ -f "$existing_conf" ] || continue
|
||||
[ "$(basename "$existing_conf")" = "${OBF_CONF_NAME}" ] && continue
|
||||
local port
|
||||
port=$(grep 'source-lport' "$existing_conf" 2>/dev/null | tr -d ' ' | cut -d'=' -f2)
|
||||
[ -n "$port" ] && used_ports="${used_ports} ${port}"
|
||||
done
|
||||
|
||||
local new_port=13255
|
||||
local port_taken=1
|
||||
while [ "$port_taken" -eq 1 ]; do
|
||||
port_taken=0
|
||||
for p in $used_ports; do
|
||||
if [ "$p" = "$new_port" ]; then
|
||||
port_taken=1
|
||||
new_port=$((new_port + 1))
|
||||
break
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
sed -i "s/^source-lport = [0-9]*/source-lport = ${new_port}/" "$PHOBOS_DIR/${OBF_CONF_NAME}"
|
||||
sed -i "s/^Endpoint = 127\.0\.0\.1:[0-9]*/Endpoint = 127.0.0.1:${new_port}/" "$PHOBOS_DIR/${CLIENT_NAME}.conf"
|
||||
|
||||
log " Назначен локальный порт obfuscator: $new_port"
|
||||
fi
|
||||
|
||||
log "Конфигурации установлены:"
|
||||
log " - Obfuscator: $PHOBOS_DIR/${OBF_CONF_NAME}"
|
||||
log " - WireGuard: $PHOBOS_DIR/${CLIENT_NAME}.conf"
|
||||
}
|
||||
|
||||
deploy_lib_client() {
|
||||
if [ -f "lib-client.sh" ]; then
|
||||
cp "lib-client.sh" "$PHOBOS_DIR/lib-client.sh"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
deploy_uninstall_script() {
|
||||
log "Развертывание скрипта удаления Phobos..."
|
||||
|
||||
if [ -f "phobos-uninstall.sh" ]; then
|
||||
cp "phobos-uninstall.sh" "$PHOBOS_DIR/phobos-uninstall.sh"
|
||||
chmod +x "$PHOBOS_DIR/phobos-uninstall.sh"
|
||||
log "Скрипт удаления установлен: $PHOBOS_DIR/phobos-uninstall.sh"
|
||||
else
|
||||
log "ПРЕДУПРЕЖДЕНИЕ: phobos-uninstall.sh не найден в архиве"
|
||||
fi
|
||||
}
|
||||
|
||||
deploy_3xui_script() {
|
||||
log "Развертывание скрипта 3xui.sh..."
|
||||
|
||||
if [ -f "3xui.sh" ]; then
|
||||
cp "3xui.sh" "$PHOBOS_DIR/3xui.sh"
|
||||
chmod +x "$PHOBOS_DIR/3xui.sh"
|
||||
log "Скрипт 3xui.sh установлен: $PHOBOS_DIR/3xui.sh"
|
||||
else
|
||||
log "ПРЕДУПРЕЖДЕНИЕ: 3xui.sh не найден в архиве"
|
||||
fi
|
||||
}
|
||||
|
||||
run_3xui_integration() {
|
||||
log ""
|
||||
log "==> Интеграция WireGuard конфигурации в 3x-ui..."
|
||||
|
||||
if [ ! -f "$PHOBOS_DIR/3xui.sh" ]; then
|
||||
log "ОШИБКА: Скрипт $PHOBOS_DIR/3xui.sh не найден"
|
||||
return 1
|
||||
fi
|
||||
|
||||
for cmd in jq sqlite3; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
log "Устанавливаю $cmd..."
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
apt-get update -qq && apt-get install -y -qq "$cmd" >/dev/null 2>&1
|
||||
elif command -v yum >/dev/null 2>&1; then
|
||||
yum install -y -q "${cmd/sqlite3/sqlite}" >/dev/null 2>&1
|
||||
elif command -v dnf >/dev/null 2>&1; then
|
||||
dnf install -y -q "${cmd/sqlite3/sqlite}" >/dev/null 2>&1
|
||||
elif command -v apk >/dev/null 2>&1; then
|
||||
apk add --quiet "${cmd/sqlite3/sqlite}" >/dev/null 2>&1
|
||||
fi
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
log "ОШИБКА: не удалось установить $cmd"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
local wg_config="$PHOBOS_DIR/${CLIENT_NAME}.conf"
|
||||
|
||||
if [ ! -f "$wg_config" ]; then
|
||||
log "ОШИБКА: Конфигурация WireGuard не найдена: $wg_config"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log "Запуск интеграции: $PHOBOS_DIR/3xui.sh $wg_config"
|
||||
|
||||
if "$PHOBOS_DIR/3xui.sh" "$wg_config"; then
|
||||
log "[OK] WireGuard конфигурация успешно интегрирована в 3x-ui"
|
||||
log "[OK] Outbound 'Phobos' добавлен в конфигурацию 3x-ui"
|
||||
return 0
|
||||
else
|
||||
log "ОШИБКА: Не удалось интегрировать конфигурацию в 3x-ui"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup_3xui_script() {
|
||||
local keep_script="$1"
|
||||
|
||||
if [ "$keep_script" = "true" ]; then
|
||||
log "Скрипт 3xui.sh сохранен для использования в 3x-ui режиме"
|
||||
else
|
||||
if [ -f "$PHOBOS_DIR/3xui.sh" ]; then
|
||||
rm -f "$PHOBOS_DIR/3xui.sh"
|
||||
log "Скрипт 3xui.sh удален (не требуется в обычном режиме)"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
install_wireguard_openwrt() {
|
||||
log "Установка пакетов WireGuard для OpenWRT..."
|
||||
|
||||
if ! command -v wg >/dev/null 2>&1; then
|
||||
log "Установка kmod-wireguard и wireguard-tools..."
|
||||
opkg update
|
||||
opkg install kmod-wireguard wireguard-tools luci-proto-wireguard
|
||||
else
|
||||
log "WireGuard уже установлен"
|
||||
fi
|
||||
|
||||
log "Установка luci-app-wireguard для веб-интерфейса..."
|
||||
opkg install luci-app-wireguard >/dev/null 2>&1 || log "ПРЕДУПРЕЖДЕНИЕ: luci-app-wireguard не удалось установить"
|
||||
|
||||
log "✓ Пакеты WireGuard установлены"
|
||||
}
|
||||
|
||||
configure_wireguard_openwrt() {
|
||||
log ""
|
||||
log "==> Автоматическая настройка WireGuard через UCI..."
|
||||
|
||||
extract_wireguard_params
|
||||
|
||||
if [ ! -f "./router-configure-wireguard-openwrt.sh" ]; then
|
||||
log "⚠ Скрипт router-configure-wireguard-openwrt.sh не найден"
|
||||
log " Используйте ручную настройку через LuCI или UCI"
|
||||
return 1
|
||||
fi
|
||||
|
||||
chmod +x ./router-configure-wireguard-openwrt.sh
|
||||
|
||||
if ./router-configure-wireguard-openwrt.sh \
|
||||
--client-name "$CLIENT_NAME" \
|
||||
--client-private-key "$WG_PRIVATE_KEY" \
|
||||
--client-ip "$CLIENT_IP" \
|
||||
--client-ipv6 "$CLIENT_IPV6" \
|
||||
--server-public-key "$WG_SERVER_PUBKEY" \
|
||||
--endpoint-port "$WG_ENDPOINT_PORT" \
|
||||
--keepalive 25 \
|
||||
--mtu 1420 \
|
||||
--fallback-config "$PHOBOS_DIR/${CLIENT_NAME}.conf"; then
|
||||
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
extract_wireguard_params() {
|
||||
log "Извлечение параметров WireGuard из конфигурации..."
|
||||
|
||||
local config_file="$PHOBOS_DIR/${CLIENT_NAME}.conf"
|
||||
|
||||
WG_PRIVATE_KEY=$(grep '^PrivateKey' "$config_file" | cut -d'=' -f2- | tr -d ' \t\n\r')
|
||||
WG_ADDRESS=$(grep '^Address' "$config_file" | cut -d'=' -f2- | tr -d ' \t\n\r')
|
||||
WG_SERVER_PUBKEY=$(grep '^PublicKey' "$config_file" | cut -d'=' -f2- | tr -d ' \t\n\r')
|
||||
WG_ENDPOINT_PORT=$(grep '^Endpoint' "$config_file" | cut -d':' -f2 | tr -d ' \t\n\r')
|
||||
|
||||
CLIENT_IP=$(echo "$WG_ADDRESS" | cut -d',' -f1 | tr -d ' ')
|
||||
CLIENT_IPV6=$(echo "$WG_ADDRESS" | cut -d',' -f2 | tr -d ' ')
|
||||
|
||||
if [ -z "$CLIENT_IPV6" ] || [ "$CLIENT_IPV6" = "$CLIENT_IP" ]; then
|
||||
CLIENT_IPV6=$(grep -A 10 '\[Interface\]' "$config_file" | grep '^Address' | grep -o 'fd[0-9a-f:\/]*' | head -1)
|
||||
fi
|
||||
|
||||
if [ -z "$CLIENT_IPV6" ]; then
|
||||
CLIENT_IPV6="none"
|
||||
fi
|
||||
|
||||
if [ -z "$WG_ENDPOINT_PORT" ]; then
|
||||
WG_ENDPOINT_PORT=13255
|
||||
fi
|
||||
|
||||
log " Private Key: ${WG_PRIVATE_KEY:0:20}..."
|
||||
log " IPv4: $CLIENT_IP"
|
||||
log " IPv6: $CLIENT_IPV6"
|
||||
log " Server PubKey: ${WG_SERVER_PUBKEY:0:20}..."
|
||||
log " Endpoint port: $WG_ENDPOINT_PORT"
|
||||
}
|
||||
|
||||
configure_wireguard_rci() {
|
||||
log ""
|
||||
log "==> Автоматическая настройка WireGuard через RCI API..."
|
||||
|
||||
extract_wireguard_params
|
||||
|
||||
if [ ! -f "./router-configure-wireguard.sh" ]; then
|
||||
log "⚠ Скрипт router-configure-wireguard.sh не найден"
|
||||
log " Используйте ручной импорт (см. инструкции ниже)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
chmod +x ./router-configure-wireguard.sh
|
||||
|
||||
if ./router-configure-wireguard.sh \
|
||||
--client-name "$CLIENT_NAME" \
|
||||
--client-private-key "$WG_PRIVATE_KEY" \
|
||||
--client-ip "$CLIENT_IP" \
|
||||
--client-ipv6 "$CLIENT_IPV6" \
|
||||
--server-public-key "$WG_SERVER_PUBKEY" \
|
||||
--endpoint-port "$WG_ENDPOINT_PORT" \
|
||||
--keepalive 25 \
|
||||
--mtu 1420 \
|
||||
--fallback-config "$PHOBOS_DIR/${CLIENT_NAME}.conf"; then
|
||||
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
show_manual_instructions() {
|
||||
log ""
|
||||
log "╔════════════════════════════════════════════════════════════╗"
|
||||
log "║ Требуется ручной импорт WireGuard конфигурации ║"
|
||||
log "╚════════════════════════════════════════════════════════════╝"
|
||||
log ""
|
||||
log "Выполните следующие шаги:"
|
||||
log ""
|
||||
|
||||
if [ "$ROUTER_PLATFORM" = "keenetic" ]; then
|
||||
log "1. Откройте веб-панель администрирования роутера Keenetic"
|
||||
log " (http://192.168.1.1 или http://my.keenetic.net)"
|
||||
log ""
|
||||
log "2. Перейдите в раздел: Интернет → Другие подключения"
|
||||
log ""
|
||||
log "3. Выберите 'Загрузить конфигурацию из файла' в разделе 'WireGuard'"
|
||||
log ""
|
||||
log "4. Укажите путь к файлу:"
|
||||
log " $PHOBOS_DIR/${CLIENT_NAME}.conf"
|
||||
log ""
|
||||
log "5. Активируйте подключение"
|
||||
elif [ "$ROUTER_PLATFORM" = "openwrt" ]; then
|
||||
log "1. Откройте веб-интерфейс LuCI роутера OpenWRT"
|
||||
log " (обычно http://192.168.1.1)"
|
||||
log ""
|
||||
log "2. Перейдите в: Network → Interfaces"
|
||||
log ""
|
||||
log "3. Создайте новый интерфейс с протоколом WireGuard"
|
||||
log ""
|
||||
log "4. Используйте параметры из файла:"
|
||||
log " $PHOBOS_DIR/${CLIENT_NAME}.conf"
|
||||
log ""
|
||||
log "5. Настройте файрволл зону для интерфейса"
|
||||
fi
|
||||
|
||||
log ""
|
||||
}
|
||||
|
||||
show_final_info() {
|
||||
log ""
|
||||
log "╔════════════════════════════════════════════════════════════╗"
|
||||
log "║ Информация об установке ║"
|
||||
log "╚════════════════════════════════════════════════════════════╝"
|
||||
log ""
|
||||
log "Файлы установки:"
|
||||
|
||||
if [ "$ROUTER_PLATFORM" = "keenetic" ]; then
|
||||
log " Бинарник: /opt/bin/${OBF_BINARY_NAME}"
|
||||
elif [ "$ROUTER_PLATFORM" = "openwrt" ]; then
|
||||
log " Бинарник: /usr/bin/${OBF_BINARY_NAME}"
|
||||
elif [ "$ROUTER_PLATFORM" = "linux" ]; then
|
||||
log " Бинарник: /usr/local/bin/${OBF_BINARY_NAME}"
|
||||
fi
|
||||
|
||||
log " Конфиг obfuscator: $PHOBOS_DIR/${OBF_CONF_NAME}"
|
||||
log " Конфиг WireGuard: $PHOBOS_DIR/${CLIENT_NAME}.conf"
|
||||
|
||||
if [ "$ROUTER_PLATFORM" = "keenetic" ] || [ -f /opt/etc/init.d/${OBF_INIT_NAME} ]; then
|
||||
log " Init-скрипт: /opt/etc/init.d/${OBF_INIT_NAME}"
|
||||
elif [ "$ROUTER_PLATFORM" = "openwrt" ] && [ -f /etc/init.d/${OBF_SERVICE_NAME} ]; then
|
||||
log " Init-скрипт: /etc/init.d/${OBF_SERVICE_NAME}"
|
||||
elif [ "$ROUTER_PLATFORM" = "linux" ]; then
|
||||
log " Systemd service: /etc/systemd/system/${OBF_SERVICE_NAME}.service"
|
||||
log " WireGuard конфиг: /etc/wireguard/${OBF_WG_IFACE}.conf"
|
||||
fi
|
||||
|
||||
log " Uninstall: $PHOBOS_DIR/phobos-uninstall.sh"
|
||||
log " Health monitor: $PHOBOS_DIR/phobos-health.sh"
|
||||
log " Failover config: $PHOBOS_DIR/failover.conf"
|
||||
log ""
|
||||
log "Управление:"
|
||||
|
||||
if [ "$ROUTER_PLATFORM" = "keenetic" ] || [ -f /opt/etc/init.d/${OBF_INIT_NAME} ]; then
|
||||
log " /opt/etc/init.d/${OBF_INIT_NAME} status # Проверить что obfuscator запущен"
|
||||
elif [ "$ROUTER_PLATFORM" = "openwrt" ] && [ -f /etc/init.d/${OBF_SERVICE_NAME} ]; then
|
||||
log " /etc/init.d/${OBF_SERVICE_NAME} status # Проверить что obfuscator запущен"
|
||||
elif [ "$ROUTER_PLATFORM" = "linux" ]; then
|
||||
log " systemctl status ${OBF_SERVICE_NAME} # Проверить что obfuscator запущен"
|
||||
log " systemctl status wg-quick@${OBF_WG_IFACE} # Проверить что WireGuard запущен"
|
||||
fi
|
||||
|
||||
log " $PHOBOS_DIR/phobos-uninstall.sh # Удалить Phobos"
|
||||
log ""
|
||||
}
|
||||
|
||||
|
||||
deploy_health_monitor() {
|
||||
log "Развертывание монитора здоровья Phobos..."
|
||||
|
||||
if [ ! -f "phobos-health.sh" ]; then
|
||||
log "ПРЕДУПРЕЖДЕНИЕ: phobos-health.sh не найден в архиве"
|
||||
return 0
|
||||
fi
|
||||
|
||||
cp "phobos-health.sh" "$PHOBOS_DIR/phobos-health.sh"
|
||||
chmod +x "$PHOBOS_DIR/phobos-health.sh"
|
||||
|
||||
if [ -f "failover.conf" ]; then
|
||||
cp "failover.conf" "$PHOBOS_DIR/failover.conf"
|
||||
chmod 600 "$PHOBOS_DIR/failover.conf"
|
||||
fi
|
||||
|
||||
mkdir -p "$PHOBOS_DIR/state"
|
||||
|
||||
# --- Phobos pull agent: panel -> router config sync (NAT-friendly) ---
|
||||
# Routers behind NAT cannot be reached by the panel over SSH, so they PULL
|
||||
# their failover.conf over HTTP. Ships client_id + pull_token from the package.
|
||||
if [ -f "phobos-pull.sh" ]; then
|
||||
cp "phobos-pull.sh" "$PHOBOS_DIR/phobos-pull.sh"
|
||||
chmod +x "$PHOBOS_DIR/phobos-pull.sh"
|
||||
echo "{{CLIENT_NAME}}" > "$PHOBOS_DIR/client_id"
|
||||
[ -f "pull_token" ] && cp "pull_token" "$PHOBOS_DIR/pull_token"
|
||||
log " Pull-агент установлен (client_id={{CLIENT_NAME}})"
|
||||
fi
|
||||
|
||||
local cron_line="*/1 * * * * $PHOBOS_DIR/phobos-health.sh"
|
||||
local pull_line="*/1 * * * * $PHOBOS_DIR/phobos-pull.sh"
|
||||
[ -f "$PHOBOS_DIR/phobos-pull.sh" ] || pull_line=""
|
||||
|
||||
if [ "$ROUTER_PLATFORM" = "keenetic" ]; then
|
||||
# Install cron if not present (Entware)
|
||||
if ! command -v crontab >/dev/null 2>&1 && command -v opkg >/dev/null 2>&1; then
|
||||
log " Установка cron через opkg..."
|
||||
opkg update >/dev/null 2>&1
|
||||
opkg install cron >/dev/null 2>&1
|
||||
/opt/etc/init.d/S10cron start >/dev/null 2>&1 || true
|
||||
fi
|
||||
local cron_file="/opt/etc/crontab"
|
||||
# Create crontab file if missing
|
||||
if [ ! -f "$cron_file" ]; then
|
||||
mkdir -p /opt/etc
|
||||
touch "$cron_file"
|
||||
fi
|
||||
if ! grep -q "phobos-health" "$cron_file" 2>/dev/null; then
|
||||
echo "$cron_line" >> "$cron_file"
|
||||
log " Cron (health) добавлен в $cron_file"
|
||||
fi
|
||||
if [ -n "$pull_line" ] && ! grep -q "phobos-pull" "$cron_file" 2>/dev/null; then
|
||||
echo "$pull_line" >> "$cron_file"
|
||||
log " Cron (pull) добавлен в $cron_file"
|
||||
fi
|
||||
/opt/etc/init.d/S10cron restart >/dev/null 2>&1 || true
|
||||
elif [ "$ROUTER_PLATFORM" = "openwrt" ]; then
|
||||
(crontab -l 2>/dev/null | grep -v "phobos-health" | grep -v "phobos-pull"; echo "$cron_line"; [ -n "$pull_line" ] && echo "$pull_line") | crontab -
|
||||
log " Cron добавлен через crontab (health + pull)"
|
||||
elif [ "$ROUTER_PLATFORM" = "linux" ]; then
|
||||
{ echo "$cron_line"; [ -n "$pull_line" ] && echo "$pull_line"; } > /etc/cron.d/phobos-health
|
||||
chmod 644 /etc/cron.d/phobos-health
|
||||
log " Cron добавлен в /etc/cron.d/phobos-health (health + pull)"
|
||||
fi
|
||||
|
||||
log " Монитор здоровья: $PHOBOS_DIR/phobos-health.sh"
|
||||
log " Failover конфиг: $PHOBOS_DIR/failover.conf"
|
||||
log " Проверка каждую минуту через cron"
|
||||
}
|
||||
|
||||
main() {
|
||||
ROUTER_PLATFORM=$(detect_router_platform)
|
||||
PHOBOS_DIR=$(detect_phobos_dir "$ROUTER_PLATFORM")
|
||||
IS_3XUI_MODE=$(detect_3xui_mode "$ROUTER_PLATFORM")
|
||||
|
||||
log "==> Определена платформа: $ROUTER_PLATFORM"
|
||||
log "==> Директория Phobos: $PHOBOS_DIR"
|
||||
|
||||
resolve_install_names
|
||||
log "==> Режим установки obfuscator: binary=${OBF_BINARY_NAME}, conf=${OBF_CONF_NAME}, init=${OBF_INIT_NAME}, service=${OBF_SERVICE_NAME}"
|
||||
|
||||
if [ "$IS_3XUI_MODE" = "true" ]; then
|
||||
log "==> Обнаружен режим установки: 3x-ui"
|
||||
log "==> В этом режиме WireGuard не устанавливается"
|
||||
log "==> Будет развернут только wg-obfuscator и интеграция с 3x-ui"
|
||||
fi
|
||||
|
||||
if [ "$ROUTER_PLATFORM" = "unknown" ]; then
|
||||
log "ОШИБКА: Неподдерживаемая платформа"
|
||||
log "Вывод uname -a: $(uname -a)"
|
||||
log ""
|
||||
log "Поддерживаемые платформы:"
|
||||
log " - Keenetic/Netcraze (определяется по 'Keenetic' или 'Netcraze' в uname)"
|
||||
log " - OpenWRT (определяется по 'OpenWrt', 'LEDE' или 'ImmortalWrt' в uname)"
|
||||
log " - Linux (Ubuntu/Debian)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$PHOBOS_DIR"
|
||||
log "==> Начало установки Phobos на роутер $ROUTER_PLATFORM"
|
||||
log "==> Клиент: $CLIENT_NAME"
|
||||
|
||||
check_root
|
||||
|
||||
if ! check_dependencies "$ROUTER_PLATFORM"; then
|
||||
log "ОШИБКА: Не удалось установить зависимости"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ARCH=$(detect_arch)
|
||||
log "Определена архитектура: $ARCH"
|
||||
|
||||
if [ "$ARCH" = "unknown" ]; then
|
||||
log "Ошибка: неподдерживаемая архитектура"
|
||||
log "Вывод uname -m: $(uname -m)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
install_obfuscator "$ARCH"
|
||||
setup_configs
|
||||
deploy_lib_client
|
||||
deploy_uninstall_script
|
||||
deploy_health_monitor
|
||||
|
||||
if [ "$IS_3XUI_MODE" = "true" ]; then
|
||||
deploy_3xui_script
|
||||
fi
|
||||
|
||||
if [ "$ROUTER_PLATFORM" = "keenetic" ]; then
|
||||
create_init_script
|
||||
start_obfuscator
|
||||
|
||||
if configure_wireguard_rci; then
|
||||
log ""
|
||||
log "╔════════════════════════════════════════════════════════════╗"
|
||||
log "║ ✓ Установка завершена успешно! ║"
|
||||
log "║ ✓ WireGuard настроен автоматически через RCI API ║"
|
||||
log "╚════════════════════════════════════════════════════════════╝"
|
||||
show_final_info
|
||||
else
|
||||
log ""
|
||||
log "╔════════════════════════════════════════════════════════════╗"
|
||||
log "║ ✓ Obfuscator установлен успешно ║"
|
||||
log "║ ⚠ WireGuard требует ручной настройки ║"
|
||||
log "╚════════════════════════════════════════════════════════════╝"
|
||||
show_manual_instructions
|
||||
show_final_info
|
||||
fi
|
||||
|
||||
elif [ "$ROUTER_PLATFORM" = "openwrt" ]; then
|
||||
install_wireguard_openwrt
|
||||
|
||||
if [ -d "/opt/etc" ]; then
|
||||
create_init_script
|
||||
else
|
||||
create_procd_init_script
|
||||
fi
|
||||
|
||||
start_obfuscator
|
||||
|
||||
if configure_wireguard_openwrt; then
|
||||
log ""
|
||||
log "╔════════════════════════════════════════════════════════════╗"
|
||||
log "║ ✓ Установка завершена успешно! ║"
|
||||
log "║ ✓ WireGuard настроен автоматически через UCI ║"
|
||||
log "╚════════════════════════════════════════════════════════════╝"
|
||||
show_final_info
|
||||
else
|
||||
log ""
|
||||
log "╔════════════════════════════════════════════════════════════╗"
|
||||
log "║ ✓ Obfuscator установлен успешно ║"
|
||||
log "║ ⚠ WireGuard требует ручной настройки ║"
|
||||
log "╚════════════════════════════════════════════════════════════╝"
|
||||
show_manual_instructions
|
||||
show_final_info
|
||||
fi
|
||||
|
||||
elif [ "$ROUTER_PLATFORM" = "linux" ]; then
|
||||
if [ "$IS_3XUI_MODE" = "true" ]; then
|
||||
log ""
|
||||
log "╔════════════════════════════════════════════════════════════╗"
|
||||
log "║ Режим установки: 3x-ui ║"
|
||||
log "╚════════════════════════════════════════════════════════════╝"
|
||||
log ""
|
||||
|
||||
create_systemd_obfuscator_service
|
||||
|
||||
if run_3xui_integration; then
|
||||
cleanup_3xui_script "true"
|
||||
log ""
|
||||
log "╔════════════════════════════════════════════════════════════╗"
|
||||
log "║ ✓ Установка в режиме 3x-ui завершена успешно! ║"
|
||||
log "║ ✓ Obfuscator настроен через systemd ║"
|
||||
log "║ ✓ Outbound 'Phobos' добавлен в конфигурацию 3x-ui ║"
|
||||
log "╚════════════════════════════════════════════════════════════╝"
|
||||
log ""
|
||||
log "Файлы установки:"
|
||||
log " Бинарник: /usr/local/bin/wg-obfuscator"
|
||||
log " Конфиг obfuscator: $PHOBOS_DIR/wg-obfuscator.conf"
|
||||
log " Конфиг WireGuard: $PHOBOS_DIR/${CLIENT_NAME}.conf"
|
||||
log " Скрипт 3xui.sh: $PHOBOS_DIR/3xui.sh"
|
||||
log " Systemd service: /etc/systemd/system/phobos-obfuscator.service"
|
||||
log ""
|
||||
log "Управление:"
|
||||
log " systemctl status phobos-obfuscator # Проверить что obfuscator запущен"
|
||||
log ""
|
||||
log "Примечание:"
|
||||
log " WireGuard управляется через 3x-ui панель"
|
||||
log " Скрипт 3xui.sh сохранен для повторной интеграции при необходимости"
|
||||
log ""
|
||||
else
|
||||
log ""
|
||||
log "╔════════════════════════════════════════════════════════════╗"
|
||||
log "║ ✗ Установка в режиме 3x-ui завершена с ошибками ║"
|
||||
log "╚════════════════════════════════════════════════════════════╝"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
install_wireguard_linux || {
|
||||
log "ОШИБКА: Не удалось установить WireGuard"
|
||||
exit 1
|
||||
}
|
||||
|
||||
create_systemd_obfuscator_service
|
||||
|
||||
if configure_wireguard_linux; then
|
||||
configure_ufw_linux
|
||||
cleanup_3xui_script "false"
|
||||
log ""
|
||||
log "╔════════════════════════════════════════════════════════════╗"
|
||||
log "║ ✓ Установка завершена успешно! ║"
|
||||
log "║ ✓ WireGuard и Obfuscator настроены через systemd ║"
|
||||
log "╚════════════════════════════════════════════════════════════╝"
|
||||
show_final_info
|
||||
else
|
||||
cleanup_3xui_script "false"
|
||||
log ""
|
||||
log "╔════════════════════════════════════════════════════════════╗"
|
||||
log "║ ⚠ Установка завершена с предупреждениями ║"
|
||||
log "║ ⚠ Проверьте статус служб вручную ║"
|
||||
log "╚════════════════════════════════════════════════════════════╝"
|
||||
show_final_info
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
log "==> Установка завершена."
|
||||
}
|
||||
|
||||
main "$@"
|
||||
399
overlay/phobos-client.sh
Executable file
399
overlay/phobos-client.sh
Executable file
@@ -0,0 +1,399 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/lib-core.sh"
|
||||
|
||||
check_root
|
||||
load_env
|
||||
ensure_dirs
|
||||
|
||||
CMD="${1:-help}"
|
||||
CLIENT_ARG="${2:-}"
|
||||
EXTRA_ARG="${3:-}"
|
||||
|
||||
resolve_client() {
|
||||
local name="$1"
|
||||
local id=$(echo "$name" | tr ' ' '-' | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
if [[ -d "$CLIENTS_DIR/$id" ]]; then
|
||||
echo "$id"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
action_add() {
|
||||
local name="$CLIENT_ARG"
|
||||
local manual_ip="$EXTRA_ARG"
|
||||
|
||||
if [[ -z "$name" ]]; then die "Использование: $0 add <client_name> [ip]"; fi
|
||||
|
||||
local id=$(echo "$name" | tr ' ' '-' | tr '[:upper:]' '[:lower:]')
|
||||
local dir="$CLIENTS_DIR/$id"
|
||||
|
||||
if [[ -d "$dir" ]]; then die "Клиент $id уже существует."; fi
|
||||
|
||||
if [[ -z "$SERVER_WG_PUBLIC_KEY" ]]; then
|
||||
die "Публичный ключ сервера не найден в server.env. Запустите установку."
|
||||
fi
|
||||
|
||||
local server_pub="$SERVER_WG_PUBLIC_KEY"
|
||||
local server_ip_v4="${SERVER_PUBLIC_IP_V4:-}"
|
||||
local server_ip_v6="${SERVER_PUBLIC_IP_V6:-}"
|
||||
|
||||
local client_ip_v4="$manual_ip"
|
||||
local ipv4_prefix_main=$(echo "${SERVER_WG_IPV4_NETWORK:-10.25.0.0/16}" | cut -d'/' -f1 | cut -d'.' -f1-2)
|
||||
local ipv6_prefix_main=$(echo "${SERVER_WG_IPV6_NETWORK:-fd00:10:25::/48}" | cut -d'/' -f1 | sed 's/::.*//')
|
||||
|
||||
if [[ -z "$client_ip_v4" ]]; then
|
||||
log_info "Поиск свободного IP..."
|
||||
declare -A used_ips
|
||||
|
||||
for d in "$CLIENTS_DIR"/*; do
|
||||
if [[ -d "$d" ]] && [[ -f "$d/metadata.json" ]]; then
|
||||
local ip=$(jq -r '.tunnel_ip_v4 // empty' "$d/metadata.json" 2>/dev/null)
|
||||
[[ -n "$ip" ]] && used_ips["$ip"]=1
|
||||
fi
|
||||
done
|
||||
|
||||
used_ips["${ipv4_prefix_main}.0.1"]=1
|
||||
|
||||
local found=false
|
||||
for oct3 in {0..255}; do
|
||||
local start_oct4=2
|
||||
for oct4 in $(seq $start_oct4 254); do
|
||||
local candidate="${ipv4_prefix_main}.${oct3}.${oct4}"
|
||||
if [[ -z "${used_ips[$candidate]:-}" ]]; then
|
||||
client_ip_v4="$candidate"
|
||||
found=true
|
||||
break 2
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
[[ "$found" == "false" ]] && die "Нет свободных IP в подсети."
|
||||
fi
|
||||
|
||||
local oct3=$(echo "$client_ip_v4" | cut -d. -f3)
|
||||
local oct4=$(echo "$client_ip_v4" | cut -d. -f4)
|
||||
local hex_part=$(printf "%x:%x" "$oct3" "$oct4")
|
||||
local client_ip_v6=""
|
||||
[[ -n "$server_ip_v6" ]] && client_ip_v6="${ipv6_prefix_main}::${hex_part}"
|
||||
|
||||
log_info "Назначен IP: $client_ip_v4 $([[ -n $client_ip_v6 ]] && echo "/ $client_ip_v6")"
|
||||
|
||||
mkdir -p "$dir"
|
||||
umask 077
|
||||
wg genkey > "$dir/client_private.key"
|
||||
wg pubkey < "$dir/client_private.key" > "$dir/client_public.key"
|
||||
local priv_key=$(cat "$dir/client_private.key")
|
||||
local pub_key=$(cat "$dir/client_public.key")
|
||||
|
||||
local allowed_ips="0.0.0.0/0"
|
||||
local addr_str="$client_ip_v4/32"
|
||||
if [[ -n "$client_ip_v6" ]]; then
|
||||
allowed_ips="0.0.0.0/0, ::/0"
|
||||
addr_str="$client_ip_v4/32, $client_ip_v6/128"
|
||||
fi
|
||||
|
||||
cat > "$dir/${id}.conf" <<EOF
|
||||
[Interface]
|
||||
PrivateKey = $priv_key
|
||||
Address = $addr_str
|
||||
DNS = 1.1.1.1, 8.8.8.8
|
||||
MTU = 1420
|
||||
|
||||
[Peer]
|
||||
PublicKey = $server_pub
|
||||
Endpoint = 127.0.0.1:${CLIENT_WG_PORT:-13255}
|
||||
AllowedIPs = $allowed_ips
|
||||
PersistentKeepalive = 25
|
||||
EOF
|
||||
chmod 600 "$dir/${id}.conf"
|
||||
|
||||
cat > "$dir/wg-obfuscator.conf" <<EOF
|
||||
[instance]
|
||||
source-if = 127.0.0.1
|
||||
source-lport = ${CLIENT_WG_PORT:-13255}
|
||||
target = $SERVER_PUBLIC_IP_V4:${OBFUSCATOR_PORT:-51821}
|
||||
key = ${OBFUSCATOR_KEY:-KEY}
|
||||
masking = ${OBFUSCATOR_MASKING:-AUTO}
|
||||
verbose = INFO
|
||||
idle-timeout = ${OBFUSCATOR_IDLE:-300}
|
||||
max-dummy = ${OBFUSCATOR_DUMMY:-4}
|
||||
EOF
|
||||
chmod 600 "$dir/wg-obfuscator.conf"
|
||||
|
||||
cat > "$dir/metadata.json" <<EOF
|
||||
{
|
||||
"client_id": "$id",
|
||||
"client_name": "$name",
|
||||
"tunnel_ip_v4": "$client_ip_v4",
|
||||
"tunnel_ip_v6": "$client_ip_v6",
|
||||
"public_key": "$pub_key",
|
||||
"created_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
||||
"obfuscator_key": "${OBFUSCATOR_KEY:-}",
|
||||
"obfuscator_dummy": "${OBFUSCATOR_DUMMY:-4}",
|
||||
"obfuscator_idle": "${OBFUSCATOR_IDLE:-300}",
|
||||
"server_ip_v4": "$SERVER_PUBLIC_IP_V4",
|
||||
"server_port": "${OBFUSCATOR_PORT:-}"
|
||||
}
|
||||
EOF
|
||||
chmod 600 "$dir/metadata.json"
|
||||
|
||||
local peer_ips="$client_ip_v4/32"
|
||||
[[ -n "$client_ip_v6" ]] && peer_ips="$peer_ips, $client_ip_v6/128"
|
||||
|
||||
cat >> "$WG_CONFIG" <<EOF
|
||||
|
||||
[Peer]
|
||||
PublicKey = $pub_key
|
||||
AllowedIPs = $peer_ips
|
||||
EOF
|
||||
|
||||
wg syncconf wg0 <(wg-quick strip wg0 2>/dev/null) 2>/dev/null
|
||||
log_success "Клиент $name создан."
|
||||
|
||||
CLIENT_ARG="$id"
|
||||
action_package
|
||||
action_link
|
||||
}
|
||||
|
||||
action_remove() {
|
||||
local id=$(resolve_client "$CLIENT_ARG")
|
||||
if [[ -z "$id" ]]; then die "Клиент не найден."; fi
|
||||
local dir="$CLIENTS_DIR/$id"
|
||||
|
||||
log_info "Удаление клиента $id..."
|
||||
|
||||
if [[ -f "$dir/client_public.key" ]]; then
|
||||
local pub=$(cat "$dir/client_public.key")
|
||||
if grep -qF "$pub" "$WG_CONFIG"; then
|
||||
awk -v key="$pub" '
|
||||
BEGIN {RS=""; ORS="\n\n"}
|
||||
index($0, key) == 0 {print $0}
|
||||
' "$WG_CONFIG" > "$WG_CONFIG.tmp" && mv "$WG_CONFIG.tmp" "$WG_CONFIG"
|
||||
|
||||
sed -i '/^$/N;/^\n$/D' "$WG_CONFIG"
|
||||
|
||||
wg syncconf wg0 <(wg-quick strip wg0 2>/dev/null) 2>/dev/null
|
||||
log_success "Peer удален из конфигурации."
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -rf "$dir"
|
||||
rm -f "$PACKAGES_DIR/phobos-$id.tar.gz"
|
||||
|
||||
if [[ -f "$TOKENS_FILE" ]] && command -v jq >/dev/null; then
|
||||
local tokens=$(jq -r ".[] | select(.client == \"$id\") | .token" "$TOKENS_FILE")
|
||||
for t in $tokens; do
|
||||
rm -f "$WWW_DIR/init/$t.sh"
|
||||
rm -rf "$WWW_DIR/packages/$t"
|
||||
done
|
||||
jq "map(select(.client != \"$id\"))" "$TOKENS_FILE" > "$TOKENS_FILE.tmp" && mv "$TOKENS_FILE.tmp" "$TOKENS_FILE"
|
||||
fi
|
||||
|
||||
log_success "Клиент $id полностью удален."
|
||||
}
|
||||
|
||||
action_package() {
|
||||
local id=$(resolve_client "$CLIENT_ARG")
|
||||
if [[ -z "$id" ]]; then die "Клиент не найден."; fi
|
||||
|
||||
log_info "Сборка пакета для $id..."
|
||||
local dir="$CLIENTS_DIR/$id"
|
||||
local tmp=$(mktemp -d)
|
||||
local pkg_root="$tmp/phobos-$id"
|
||||
|
||||
mkdir -p "$pkg_root/bin"
|
||||
|
||||
cp "$dir/${id}.conf" "$pkg_root/${id}.conf"
|
||||
cp "$dir/wg-obfuscator.conf" "$pkg_root/wg-obfuscator.conf"
|
||||
|
||||
for arch in mipsel mips aarch64 armv7 x86_64; do
|
||||
[[ -f "$PHOBOS_DIR/bin/wg-obfuscator-$arch" ]] && cp "$PHOBOS_DIR/bin/wg-obfuscator-$arch" "$pkg_root/bin/"
|
||||
done
|
||||
|
||||
local tpl_dir="$REPO_DIR/client/templates"
|
||||
|
||||
if [[ -d "$tpl_dir" ]]; then
|
||||
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"
|
||||
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
|
||||
else
|
||||
log_warn "Шаблоны не найдены в $tpl_dir"
|
||||
fi
|
||||
|
||||
echo "Phobos Client Package for $id" > "$pkg_root/README.txt"
|
||||
echo "Date: $(date)" >> "$pkg_root/README.txt"
|
||||
|
||||
# Health monitor + failover
|
||||
if [[ -f "$PHOBOS_DIR/server/phobos-health.sh" ]]; then
|
||||
cp "$PHOBOS_DIR/server/phobos-health.sh" "$pkg_root/phobos-health.sh"
|
||||
chmod +x "$pkg_root/phobos-health.sh"
|
||||
fi
|
||||
# Pull agent: panel -> router config sync (NAT-friendly). Token = panel
|
||||
# server_api_key (the /api/router-config endpoint accepts it as a fallback).
|
||||
for src in "$tpl_dir/phobos-pull.sh" "$PHOBOS_DIR/server/phobos-pull.sh"; do
|
||||
if [[ -f "$src" ]]; then cp "$src" "$pkg_root/phobos-pull.sh"; chmod +x "$pkg_root/phobos-pull.sh"; break; fi
|
||||
done
|
||||
local PULL_TOKEN=""
|
||||
command -v jq >/dev/null && PULL_TOKEN=$(jq -r '.server_api_key // empty' /opt/phobos-panel/settings.json 2>/dev/null)
|
||||
[[ -n "$PULL_TOKEN" ]] && echo "$PULL_TOKEN" > "$pkg_root/pull_token"
|
||||
# Generate failover.conf with current server data
|
||||
source "$PHOBOS_DIR/server/server.env"
|
||||
cat > "$pkg_root/failover.conf" <<FAILEOF
|
||||
# Phobos Failover Configuration
|
||||
SERVER_1=${SERVER_PUBLIC_IP_V4}:51821,51822,51823
|
||||
KEY_1=${OBFUSCATOR_KEY}
|
||||
FAILEOF
|
||||
|
||||
find "$pkg_root" -type f ! -path "*/bin/*" -exec sed -i 's/\r$//' {} \;
|
||||
|
||||
tar -C "$tmp" -czf "$PACKAGES_DIR/phobos-$id.tar.gz" "phobos-$id"
|
||||
rm -rf "$tmp"
|
||||
|
||||
log_success "Пакет создан: $PACKAGES_DIR/phobos-$id.tar.gz"
|
||||
}
|
||||
|
||||
action_check() {
|
||||
local id=$(resolve_client "$CLIENT_ARG")
|
||||
if [[ -z "$id" ]]; then die "Клиент не найден."; fi
|
||||
|
||||
local dir="$CLIENTS_DIR/$id"
|
||||
local changes=()
|
||||
|
||||
if [[ ! -f "$dir/metadata.json" ]]; then
|
||||
echo "metadata_missing"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local client_server_ip=$(jq -r '.server_ip_v4 // ""' "$dir/metadata.json")
|
||||
local client_obf_key=$(jq -r '.obfuscator_key // ""' "$dir/metadata.json")
|
||||
local client_obf_port=$(jq -r '.server_port // ""' "$dir/metadata.json")
|
||||
local client_obf_dummy=$(jq -r '.obfuscator_dummy // "4"' "$dir/metadata.json")
|
||||
local client_obf_idle=$(jq -r '.obfuscator_idle // "300"' "$dir/metadata.json")
|
||||
|
||||
local client_wg_pubkey=""
|
||||
if [[ -f "$dir/${id}.conf" ]]; then
|
||||
client_wg_pubkey=$(grep "^PublicKey" "$dir/${id}.conf" | cut -d'=' -f2- | tr -d ' ')
|
||||
fi
|
||||
|
||||
[[ "$client_server_ip" != "$SERVER_PUBLIC_IP_V4" ]] && changes+=("IP сервера: $client_server_ip -> $SERVER_PUBLIC_IP_V4")
|
||||
[[ "$client_obf_key" != "$OBFUSCATOR_KEY" ]] && changes+=("Ключ обфускатора: изменен")
|
||||
[[ "$client_obf_port" != "$OBFUSCATOR_PORT" ]] && changes+=("Порт обфускатора: $client_obf_port -> $OBFUSCATOR_PORT")
|
||||
[[ "$client_obf_dummy" != "$OBFUSCATOR_DUMMY" ]] && changes+=("Max dummy: изменен")
|
||||
[[ "$client_obf_idle" != "$OBFUSCATOR_IDLE" ]] && changes+=("Idle таймаут: изменен")
|
||||
[[ -n "$client_wg_pubkey" && "$client_wg_pubkey" != "$SERVER_WG_PUBLIC_KEY" ]] && changes+=("Публичный ключ WG: изменен")
|
||||
|
||||
if [[ ${#changes[@]} -gt 0 ]]; then
|
||||
echo "ИЗМЕНЕНИЯ КОНФИГУРАЦИИ:"
|
||||
for c in "${changes[@]}"; do
|
||||
echo " - $c"
|
||||
done
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
action_link() {
|
||||
local id=$(resolve_client "$CLIENT_ARG")
|
||||
if [[ -z "$id" ]]; then die "Клиент не найден."; fi
|
||||
local ttl="${EXTRA_ARG:-$TOKEN_TTL}"
|
||||
|
||||
if ! command -v jq >/dev/null; then die "jq не установлен. Установите: apt-get install jq"; fi
|
||||
|
||||
local token=$(head -c 16 /dev/urandom | md5sum | cut -d' ' -f1)
|
||||
local exp=$(($(date +%s) + ttl))
|
||||
|
||||
if [[ ! -f "$TOKENS_FILE" ]]; then
|
||||
echo "[]" > "$TOKENS_FILE"
|
||||
fi
|
||||
|
||||
local clean_json=$(jq "map(select(.client != \"$id\"))" "$TOKENS_FILE")
|
||||
echo "$clean_json" | jq ". + [{\"client\": \"$id\", \"token\": \"$token\", \"expires\": $exp}]" > "$TOKENS_FILE.tmp" && mv "$TOKENS_FILE.tmp" "$TOKENS_FILE"
|
||||
|
||||
local link_dir="$WWW_DIR/packages/$token"
|
||||
rm -rf "$link_dir"
|
||||
mkdir -p "$link_dir"
|
||||
ln -s "$PACKAGES_DIR/phobos-$id.tar.gz" "$link_dir/phobos-$id.tar.gz"
|
||||
|
||||
mkdir -p "$WWW_DIR/init"
|
||||
local script_url="http://${SERVER_PUBLIC_IP_V4}:${HTTP_PORT:-80}/packages/$token/phobos-$id.tar.gz"
|
||||
|
||||
cat > "$WWW_DIR/init/$token.sh" <<EOF
|
||||
#!/bin/sh
|
||||
url="$script_url"
|
||||
dir="/tmp/phobos_install_\$\$"
|
||||
mkdir -p "\$dir"
|
||||
echo "Downloading..."
|
||||
if command -v curl >/dev/null; then
|
||||
curl -L -s -o "\$dir/package.tar.gz" "\$url"
|
||||
else
|
||||
wget -q -O "\$dir/package.tar.gz" "\$url"
|
||||
fi
|
||||
if [ ! -f "\$dir/package.tar.gz" ]; then echo "Download failed"; exit 1; fi
|
||||
cd "\$dir"
|
||||
tar xzf package.tar.gz
|
||||
cd "phobos-$id"
|
||||
chmod +x install-router.sh
|
||||
./install-router.sh
|
||||
EOF
|
||||
|
||||
# nginx (www-data) must read these; the builder runs with a strict root umask
|
||||
# (files 600 / dirs 700) which makes the install URL return 403. Fix perms.
|
||||
chmod 755 "$WWW_DIR" "$WWW_DIR/init" "$WWW_DIR/packages" "$link_dir" 2>/dev/null
|
||||
chmod 644 "$WWW_DIR/init/$token.sh" 2>/dev/null
|
||||
chmod 755 "$PACKAGES_DIR" 2>/dev/null
|
||||
chmod 644 "$PACKAGES_DIR/phobos-$id.tar.gz" 2>/dev/null
|
||||
|
||||
local cmd="curl -s http://${SERVER_PUBLIC_IP_V4}:${HTTP_PORT:-80}/init/$token.sh | sh"
|
||||
|
||||
echo ""
|
||||
echo "=================================================="
|
||||
echo "КОМАНДА ДЛЯ УСТАНОВКИ (Действительна $(($ttl / 3600))ч)"
|
||||
echo "=================================================="
|
||||
echo "$cmd"
|
||||
echo "=================================================="
|
||||
echo ""
|
||||
}
|
||||
|
||||
action_list() {
|
||||
printf "% -20s % -20s % -20s\n" "CLIENT ID" "IPv4" "CREATED"
|
||||
echo "------------------------------------------------------------"
|
||||
for d in "$CLIENTS_DIR"/*; do
|
||||
if [[ -d "$d" ]]; then
|
||||
local id=$(basename "$d")
|
||||
local ip="N/A"
|
||||
local date="N/A"
|
||||
if [[ -f "$d/metadata.json" ]]; then
|
||||
ip=$(jq -r '.tunnel_ip_v4 // "N/A"' "$d/metadata.json")
|
||||
date=$(jq -r '.created_at // "N/A"' "$d/metadata.json" | cut -d'T' -f1)
|
||||
fi
|
||||
printf "% -20s % -20s % -20s\n" "$id" "$ip" "$date"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
case "$CMD" in
|
||||
add) action_add ;;
|
||||
remove) action_remove ;;
|
||||
package) action_package ;;
|
||||
link) action_link ;;
|
||||
check) action_check ;;
|
||||
list) action_list ;;
|
||||
rebuild)
|
||||
action_remove
|
||||
action_add
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {add|remove|list|package|link|check|rebuild}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
93
overlay/phobos-pull.sh
Executable file
93
overlay/phobos-pull.sh
Executable file
@@ -0,0 +1,93 @@
|
||||
#!/bin/sh
|
||||
# ============================================================
|
||||
# Phobos Config Pull — NAT-friendly management channel.
|
||||
# Runs ON the router via cron (every 2 min). OUTBOUND ONLY:
|
||||
# fetches this client's failover.conf from the panel over
|
||||
# HTTPS/HTTP and applies it. No inbound SSH needed, so it works
|
||||
# behind any NAT and regardless of which Phobos server is active.
|
||||
#
|
||||
# Why pull (not panel->router SSH):
|
||||
# - routers sit behind NAT with no public IP (KeenDNS is HTTP-
|
||||
# only, no port 22),
|
||||
# - WG3 must be security-level PUBLIC for LAN client routing,
|
||||
# which blocks inbound SSH on the tunnel,
|
||||
# - on failover the router's 10.25.0.2 moves to another server's
|
||||
# wg0, so a fixed panel host cannot reach it.
|
||||
# Pulling sidesteps all three.
|
||||
#
|
||||
# Files (written by installer / bootstrap):
|
||||
# /opt/etc/Phobos/client_id -> this router's client id (e.g. home)
|
||||
# /opt/etc/Phobos/pull_token -> shared secret for the panel endpoint
|
||||
# PANEL env or default below -> panel base URL
|
||||
# ============================================================
|
||||
PHOBOS_DIR="/opt/etc/Phobos"
|
||||
CONF="$PHOBOS_DIR/failover.conf"
|
||||
HEALTH="$PHOBOS_DIR/phobos-health.sh"
|
||||
LOG="$PHOBOS_DIR/health.log"
|
||||
# Config sources, tried in order. TUNNEL FIRST: 10.25.0.1 is the wg0 of whatever
|
||||
# server the tunnel currently terminates on, so management rides the same
|
||||
# obfuscated channel as data — survives a public-IP ban and follows failover.
|
||||
# - 10.25.0.1:8444 -> secondary server agent (phobos-api)
|
||||
# - 10.25.0.1:10514 -> primary server panel
|
||||
# - public panel IP -> bootstrap / if tunnel is down
|
||||
PANEL="${PANEL:-http://212.118.52.193:10514}"
|
||||
PANEL_URLS="${PANEL_URLS:-http://10.25.0.1:8444 http://10.25.0.1:10514 $PANEL}"
|
||||
CLIENT_ID=$(cat "$PHOBOS_DIR/client_id" 2>/dev/null || echo "home")
|
||||
TOKEN=$(cat "$PHOBOS_DIR/pull_token" 2>/dev/null)
|
||||
TMP="/tmp/failover.conf.pull"
|
||||
LOCK="/tmp/phobos-pull.lock"
|
||||
|
||||
log() { echo "$(date '+%H:%M:%S') $1" >> "$LOG"; }
|
||||
|
||||
# single instance
|
||||
if [ -f "$LOCK" ]; then
|
||||
pid=$(cat "$LOCK" 2>/dev/null)
|
||||
kill -0 "$pid" 2>/dev/null && exit 0
|
||||
fi
|
||||
echo $$ > "$LOCK"
|
||||
trap 'rm -f "$LOCK"' EXIT
|
||||
|
||||
[ -z "$TOKEN" ] && exit 0
|
||||
|
||||
# One fetch + compare + apply pass. Returns 0 always (best-effort).
|
||||
do_pull() {
|
||||
# Try each source (tunnel first); accept first that returns a valid conf.
|
||||
got=0
|
||||
for base in $PANEL_URLS; do
|
||||
curl -s -m 8 -o "$TMP" "${base}/api/router-config/${CLIENT_ID}?token=${TOKEN}" 2>/dev/null || continue
|
||||
if grep -q "^SERVER_1=" "$TMP" 2>/dev/null; then got=1; break; fi
|
||||
done
|
||||
[ "$got" = 1 ] || { rm -f "$TMP"; return 0; }
|
||||
|
||||
new=$(md5sum "$TMP" 2>/dev/null | cut -d' ' -f1)
|
||||
old=$(md5sum "$CONF" 2>/dev/null | cut -d' ' -f1)
|
||||
if [ "$new" = "$old" ]; then
|
||||
rm -f "$TMP"
|
||||
return 0
|
||||
fi
|
||||
|
||||
old_s1=$(grep "^SERVER_1=" "$CONF" 2>/dev/null | cut -d= -f2- | cut -d: -f1)
|
||||
new_s1=$(grep "^SERVER_1=" "$TMP" 2>/dev/null | cut -d= -f2- | cut -d: -f1)
|
||||
|
||||
cp "$CONF" "$CONF.prev" 2>/dev/null
|
||||
mv "$TMP" "$CONF"
|
||||
log "PULL: failover.conf updated (SERVER_1 ${old_s1:-?} -> ${new_s1:-?})"
|
||||
|
||||
# apply the new primary now (only if it actually changed)
|
||||
if [ "$old_s1" != "$new_s1" ] && [ -f "$HEALTH" ]; then
|
||||
sh "$HEALTH" apply-server 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Inner loop: cron fires this every 60s, but we poll ~4x per minute so a
|
||||
# panel "Set" applies within ~12-15s instead of up to a full minute. The
|
||||
# loop stays UNDER 60s and exits so the next cron tick takes over cleanly
|
||||
# (the lockfile blocks any overlap). POLL_INTERVAL/POLL_PASSES overridable.
|
||||
POLL_INTERVAL="${POLL_INTERVAL:-12}"
|
||||
POLL_PASSES="${POLL_PASSES:-4}"
|
||||
i=1
|
||||
while [ "$i" -le "$POLL_PASSES" ]; do
|
||||
do_pull
|
||||
[ "$i" -lt "$POLL_PASSES" ] && sleep "$POLL_INTERVAL"
|
||||
i=$((i + 1))
|
||||
done
|
||||
429
overlay/router-configure-wireguard.sh
Executable file
429
overlay/router-configure-wireguard.sh
Executable file
@@ -0,0 +1,429 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
RCI_URL="http://localhost:79/rci/"
|
||||
MAX_INTERFACE_NUM=9
|
||||
|
||||
check_dependencies() {
|
||||
local missing=""
|
||||
|
||||
for cmd in curl jq date; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
missing="$missing $cmd"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$missing" ]; then
|
||||
echo "ERROR: Missing required utilities:$missing" >&2
|
||||
echo "Please install them using: opkg update && opkg install$missing" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
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 Pegacomp \\
|
||||
--client-private-key "ABCD..." \\
|
||||
--client-ip 10.25.0.4 \\
|
||||
--client-ipv6 fd00:10:25::4 \\
|
||||
--server-public-key "EFGH..." \\
|
||||
--endpoint-port 13255 \\
|
||||
--fallback-config /opt/etc/Phobos/Pegacomp.conf
|
||||
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
find_phobos_interface() {
|
||||
local client_name="$1"
|
||||
local target_desc="Phobos-${client_name}"
|
||||
|
||||
log "Поиск существующего интерфейса Phobos для клиента: ${client_name}..." >&2
|
||||
|
||||
local i
|
||||
for i in 0 1 2 3 4 5 6 7 8 9; do
|
||||
local interface_json=$(curl -s "${RCI_URL}show/rc/interface/Wireguard${i}" 2>/dev/null)
|
||||
|
||||
if [ -n "${interface_json}" ] && echo "${interface_json}" | jq -e . >/dev/null 2>&1; then
|
||||
local desc=$(echo "${interface_json}" | jq -r '.description // empty' 2>/dev/null)
|
||||
if [ "${desc}" = "${target_desc}" ]; then
|
||||
log "Найден существующий интерфейс: Wireguard${i}" >&2
|
||||
echo "Wireguard${i}"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
log "Существующий интерфейс Phobos не найден" >&2
|
||||
echo ""
|
||||
return 0
|
||||
}
|
||||
|
||||
find_free_wireguard_interface() {
|
||||
log "Поиск свободного интерфейса WireGuard..." >&2
|
||||
|
||||
local i
|
||||
for i in 0 1 2 3 4 5 6 7 8 9; do
|
||||
local interface_json=$(curl -s "${RCI_URL}show/interface/Wireguard${i}" 2>/dev/null)
|
||||
|
||||
if [ -z "${interface_json}" ] || ! echo "${interface_json}" | jq -e '.id' >/dev/null 2>&1; then
|
||||
log "Найден свободный интерфейс: Wireguard${i}" >&2
|
||||
echo "Wireguard${i}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
error "Нет свободных интерфейсов WireGuard (0-${MAX_INTERFACE_NUM})"
|
||||
return 1
|
||||
}
|
||||
|
||||
remove_wireguard_interface() {
|
||||
local interface_name="$1"
|
||||
|
||||
log "Удаление существующего интерфейса: ${interface_name}..."
|
||||
|
||||
if command -v ndmc >/dev/null 2>&1; then
|
||||
if ndmc -c "no interface ${interface_name}" >/dev/null 2>&1; then
|
||||
log "Интерфейс ${interface_name} успешно удален ✓"
|
||||
return 0
|
||||
else
|
||||
log "Предупреждение: не удалось удалить интерфейс через ndmc"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log "Предупреждение: команда ndmc не найдена"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
configure_wireguard_interface() {
|
||||
local interface_name="$1"
|
||||
|
||||
local description="Phobos-${CLIENT_NAME}"
|
||||
local client_ip_addr=$(echo "${CLIENT_IP}" | cut -d'/' -f1)
|
||||
local client_ipv6_block="${CLIENT_IPV6}"
|
||||
|
||||
log "Настройка интерфейса ${interface_name}..."
|
||||
|
||||
local config_json=$(cat <<EOF
|
||||
{
|
||||
"interface": {
|
||||
"${interface_name}": {
|
||||
"description": "${description}",
|
||||
"security-level": {
|
||||
"public": true
|
||||
},
|
||||
"ip": {
|
||||
"address": {
|
||||
"address": "${client_ip_addr}",
|
||||
"mask": "255.255.255.255"
|
||||
},
|
||||
"mtu": ${MTU},
|
||||
"global": true,
|
||||
"defaultgw": false,
|
||||
"priority": 26622,
|
||||
"tcp": {
|
||||
"adjust-mss": {
|
||||
"pmtu": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ipv6": {
|
||||
"address": [
|
||||
{"auto": false},
|
||||
{"block": "${client_ipv6_block}"}
|
||||
],
|
||||
"prefix": [
|
||||
{"auto": false}
|
||||
]
|
||||
},
|
||||
"wireguard": {
|
||||
"private-key": "${CLIENT_PRIVATE_KEY}",
|
||||
"peer": [
|
||||
{
|
||||
"key": "${SERVER_PUBLIC_KEY}",
|
||||
"comment": "Phobos VPS Server",
|
||||
"endpoint": {
|
||||
"address": "127.0.0.1:${ENDPOINT_PORT}"
|
||||
},
|
||||
"keepalive-interval": {
|
||||
"interval": ${KEEPALIVE}
|
||||
},
|
||||
"allow-ips": [
|
||||
{
|
||||
"address": "0.0.0.0",
|
||||
"mask": "0.0.0.0"
|
||||
},
|
||||
{
|
||||
"address": "::",
|
||||
"mask": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"up": true
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
local result=$(echo "${config_json}" | curl -s -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @- \
|
||||
"${RCI_URL}" 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)
|
||||
error "RCI API отклонил конфигурацию: ${error_msg}"
|
||||
log "JSON запрос:"
|
||||
log "${config_json}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log "Интерфейс ${interface_name} создан ✓"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
save_configuration() {
|
||||
log "Сохранение конфигурации..."
|
||||
|
||||
local result=$(curl -s -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"system":{"configuration":{"save":{}}}}' \
|
||||
"${RCI_URL}" 2>/dev/null)
|
||||
|
||||
if echo "${result}" | grep -q '"status"[[:space:]]*:[[:space:]]*"message"'; then
|
||||
log "Конфигурация сохранена ✓"
|
||||
return 0
|
||||
else
|
||||
error "Ошибка сохранения конфигурации"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
verify_interface_created() {
|
||||
local client_name="$1"
|
||||
local interface_description="Phobos-${client_name}"
|
||||
|
||||
log "Проверка создания интерфейса WireGuard..."
|
||||
|
||||
local interfaces=$(curl -s "http://127.0.0.1:79/rci/show/interface" 2>/dev/null || echo "")
|
||||
|
||||
if [ -z "$interfaces" ]; then
|
||||
error "Не удалось получить список интерфейсов через RCI API"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! echo "$interfaces" | jq -e . >/dev/null 2>&1; then
|
||||
error "Некорректный JSON ответ от RCI API"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local found=$(echo "$interfaces" | jq -r "to_entries[] | select(.value.description == \"$interface_description\") | .key" 2>/dev/null)
|
||||
|
||||
if [ -n "$found" ]; then
|
||||
log "✓ Интерфейс $found (Phobos-${client_name}) успешно создан"
|
||||
return 0
|
||||
else
|
||||
error "Интерфейс с description '$interface_description' не найден"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
show_fallback_instructions() {
|
||||
cat <<EOF
|
||||
|
||||
╔════════════════════════════════════════════════════════════╗
|
||||
║ RCI API недоступен - требуется ручная настройка ║
|
||||
╚════════════════════════════════════════════════════════════╝
|
||||
|
||||
Конфигурация сохранена в: ${FALLBACK_CONFIG}
|
||||
|
||||
Инструкция по ручному импорту:
|
||||
1. Откройте веб-панель Keenetic (http://192.168.1.1 или http://my.keenetic.net)
|
||||
2. Перейдите: Интернет → WireGuard
|
||||
3. Нажмите: 'Добавить подключение'
|
||||
4. Выберите: 'Загрузить конфигурацию из файла'
|
||||
5. Укажите путь: ${FALLBACK_CONFIG}
|
||||
6. Активируйте подключение
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_args "$@"
|
||||
|
||||
if ! check_dependencies; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p /opt/etc/Phobos
|
||||
log "=== Phobos WireGuard RCI Configuration ==="
|
||||
log "Клиент: ${CLIENT_NAME}"
|
||||
|
||||
EXISTING_INTERFACE=$(find_phobos_interface "${CLIENT_NAME}") || true
|
||||
|
||||
if [ -n "${EXISTING_INTERFACE}" ]; then
|
||||
log "Обнаружен существующий интерфейс: ${EXISTING_INTERFACE}"
|
||||
if remove_wireguard_interface "${EXISTING_INTERFACE}"; then
|
||||
log "Интерфейс ${EXISTING_INTERFACE} удален, будет создан заново"
|
||||
else
|
||||
log "Не удалось удалить интерфейс ${EXISTING_INTERFACE}, попытка пересоздать"
|
||||
fi
|
||||
fi
|
||||
|
||||
INTERFACE_NAME=$(find_free_wireguard_interface) || true
|
||||
if [ -z "${INTERFACE_NAME}" ]; then
|
||||
show_fallback_instructions
|
||||
exit 1
|
||||
fi
|
||||
log "Создание нового интерфейса: ${INTERFACE_NAME}"
|
||||
|
||||
if ! configure_wireguard_interface "${INTERFACE_NAME}"; then
|
||||
error "Не удалось настроить WireGuard через RCI API"
|
||||
show_fallback_instructions
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! save_configuration; then
|
||||
error "Не удалось сохранить конфигурацию"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Настройка WireGuard завершена успешно! ✓"
|
||||
log "Интерфейс: ${INTERFACE_NAME}"
|
||||
log "Description: Phobos-${CLIENT_NAME}"
|
||||
|
||||
log ""
|
||||
log "Проверка статуса wg-obfuscator..."
|
||||
if [ -f /opt/etc/init.d/S49wg-obfuscator ]; then
|
||||
local obf_status=$(/opt/etc/init.d/S49wg-obfuscator status 2>&1)
|
||||
if echo "${obf_status}" | grep -q "dead"; then
|
||||
log "⚠ wg-obfuscator остановлен, перезапускаем..."
|
||||
/opt/etc/init.d/S49wg-obfuscator start
|
||||
sleep 2
|
||||
log "✓ wg-obfuscator перезапущен"
|
||||
else
|
||||
log "✓ wg-obfuscator работает"
|
||||
fi
|
||||
fi
|
||||
|
||||
log ""
|
||||
log "Ожидание применения конфигурации..."
|
||||
sleep 5
|
||||
|
||||
if verify_interface_created "${CLIENT_NAME}"; then
|
||||
log ""
|
||||
log "╔════════════════════════════════════════════════════════════╗"
|
||||
log "║ WireGuard успешно настроен! ║"
|
||||
log "╚════════════════════════════════════════════════════════════╝"
|
||||
log ""
|
||||
exit 0
|
||||
else
|
||||
log ""
|
||||
log "⚠ Не удалось подтвердить создание интерфейса WireGuard"
|
||||
log ""
|
||||
log "Проверьте вручную в веб-панели Keenetic:"
|
||||
log " Интернет → WireGuard"
|
||||
log ""
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user