Files
PCA_Phobos/overlay/router-configure-wireguard.sh
Andrei Leibzon 1f3eced228 release 1.5.0: one panel in all channels; Apple apps (iOS, new macOS tab) only in beta/dev
- stable gets the beta panel and server features (new design, presets, firewall,
  monthly traffic, router failover over all ports/addresses + TCP, altip, HR tab,
  Windows tab, update banner); ios/ macos/ windows/ sources stay out of stable
- menu item "Update to N" under Settings when a new version is out
- mobile: top menu no longer stretches to 200px
- urllib User-Agent: Cloudflare in front of git.bobyrev.com returns 403 to Python-urllib
- docs: GitHub adleibzon-rgb as source, password/uninstall/devices sections,
  new screenshots, PORTS.md TCP fallback and second IP

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 18:12:13 +03:00

466 lines
15 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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.26.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}"
local route_target=$(echo "$client_ip_addr" | cut -d'.' -f1-2).0.0/16
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
# Настраиваем статический маршрут в созданный интерфейс отдельным вызовом RCI
local route_target_net=$(echo "$client_ip_addr" | cut -d'.' -f1-2).0.0
local route_mask="255.255.0.0"
# 02.11.2026: доп. учётка (на роутере уже стоит другая): та же /16 через второй
# интерфейс конфликтует с главной, а /32 до панели увёл бы pull главной в чужой
# туннель — маршрут не ставим, pull доп. учётки ходит по panel_urls.
local main_cid=$(cat /opt/etc/Phobos/client_id 2>/dev/null)
if [ -n "$main_cid" ] && [ "$main_cid" != "${CLIENT_NAME}" ]; then
log "Доп. учётка (главная — ${main_cid}): маршрут подсети туннеля не нужен"
log "Интерфейс ${interface_name} создан ✓"
return 0
fi
log "Настройка маршрута для туннеля (${route_target_net}/${route_mask}) -> ${interface_name}..."
local route_json=$(cat <<EOF
{
"network": "${route_target_net}",
"mask": "${route_mask}",
"interface": "${interface_name}",
"auto": true
}
EOF
)
local route_result=$(echo "${route_json}" | curl -s -X POST \
-H "Content-Type: application/json" \
-d @- \
"http://localhost:79/rci/ip/route/" 2>/dev/null)
if echo "${route_result}" | jq -e '.status[] | select(.status == "error")' >/dev/null 2>&1; then
local error_msg=$(echo "${route_result}" | jq -r '.status[] | select(.status == "error") | .message // "Unknown error"' 2>/dev/null)
error "RCI API отклонил настройку маршрута: ${error_msg}"
log "JSON запрос:"
log "${route_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 "$@"