mirror of
https://github.com/andrey271192/domen_hydra.git
synced 2026-09-20 14:42:00 +00:00
fix: WireGuard deploy через Keenetic RCI API вместо ndmc
ndmc без PTY принимает stdin но не применяет конфигурационные команды (system failed 0xcffd00cc) — это не баг синтаксиса, а ограничение TTY. RCI HTTP API — то же что использует веб-интерфейс: - Auth: MD5(admin:realm:pass) + MD5(hash+challenge) через /auth - Конфиги Wireguard0-5 собираются в Python (json.dumps, без escaping) и встраиваются в скрипт как heredoc-файлы - Автоподбор свободного индекса Wireguard0-5 через /rci/show/interface/ - Fallback: raw wg+ip (wg0, работает но не виден в Keenetic UI) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
122
server/main.py
122
server/main.py
@@ -1,5 +1,6 @@
|
|||||||
"""HydraRoute Domain Manager — standalone web server."""
|
"""HydraRoute Domain Manager — standalone web server."""
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import json as _json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@@ -709,11 +710,41 @@ async def wg_deploy(name: str, x_admin_password: str = Header("")):
|
|||||||
port = srv.get("port", 51820)
|
port = srv.get("port", 51820)
|
||||||
client_ip = peer["ip"]
|
client_ip = peer["ip"]
|
||||||
|
|
||||||
|
# Build RCI payloads in Python (safe JSON, no shell escaping issues)
|
||||||
|
def _rci_iface(idx: int) -> str:
|
||||||
|
return _json.dumps({
|
||||||
|
"interface": {
|
||||||
|
f"Wireguard{idx}": {
|
||||||
|
"description": "HydraVPN",
|
||||||
|
"up": True,
|
||||||
|
"address": [{"ip": client_ip, "mask": "255.255.255.255"}],
|
||||||
|
"wireguard": {
|
||||||
|
"private-key": priv_key,
|
||||||
|
"peer": [{
|
||||||
|
"public-key": pub_key,
|
||||||
|
"endpoint": {"address": vps_host, "port": port},
|
||||||
|
"allowed-ips": [{"ip": "0.0.0.0", "mask": "0.0.0.0"}],
|
||||||
|
"persistent-keepalive": 25,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
rci_jsons = {i: _rci_iface(i) for i in range(6)}
|
||||||
|
rci_save = '{"system":{"configuration":{"save":true}}}'
|
||||||
|
router_pass = (rcfg.get("password") or config.SSH_PASS).replace("'", r"'\''")
|
||||||
|
|
||||||
|
# Embed JSON payloads into the script as heredoc files
|
||||||
|
rci_files = ""
|
||||||
|
for i, payload in rci_jsons.items():
|
||||||
|
rci_files += f"cat > /tmp/wg_rci_{i}.json << 'RCI{i}EOF'\n{payload}\nRCI{i}EOF\n"
|
||||||
|
|
||||||
script = f"""\
|
script = f"""\
|
||||||
echo '[1/3] Установка wireguard-tools...'
|
echo '[1/3] Установка wireguard-tools...'
|
||||||
opkg update 2>/dev/null || true
|
opkg update 2>/dev/null || true
|
||||||
opkg install wireguard-tools 2>/dev/null || true
|
opkg install wireguard-tools 2>/dev/null || true
|
||||||
command -v wg >/dev/null 2>&1 || {{ echo 'ОШИБКА: wg не установлен (opkg install wireguard-tools)'; exit 1; }}
|
command -v wg >/dev/null 2>&1 || {{ echo 'ОШИБКА: wg не установлен'; exit 1; }}
|
||||||
|
|
||||||
echo '[2/3] Запись конфига...'
|
echo '[2/3] Запись конфига...'
|
||||||
mkdir -p /opt/etc/wireguard
|
mkdir -p /opt/etc/wireguard
|
||||||
@@ -729,17 +760,12 @@ case "$1" in
|
|||||||
start)
|
start)
|
||||||
ip link show wg0 >/dev/null 2>&1 || ip link add wg0 type wireguard 2>/dev/null || exit 0
|
ip link show wg0 >/dev/null 2>&1 || ip link add wg0 type wireguard 2>/dev/null || exit 0
|
||||||
wg set wg0 private-key /opt/etc/wireguard/wg0.key \\
|
wg set wg0 private-key /opt/etc/wireguard/wg0.key \\
|
||||||
peer '{pub_key}' \\
|
peer '{pub_key}' allowed-ips 0.0.0.0/0 \\
|
||||||
allowed-ips 0.0.0.0/0 \\
|
endpoint '{vps_host}:{port}' persistent-keepalive 25
|
||||||
endpoint '{vps_host}:{port}' \\
|
|
||||||
persistent-keepalive 25
|
|
||||||
ip addr add '{client_ip}/32' dev wg0 2>/dev/null || true
|
ip addr add '{client_ip}/32' dev wg0 2>/dev/null || true
|
||||||
ip link set up dev wg0
|
ip link set up dev wg0
|
||||||
;;
|
;;
|
||||||
stop)
|
stop) ip link set down dev wg0 2>/dev/null; ip link delete wg0 2>/dev/null ;;
|
||||||
ip link set down dev wg0 2>/dev/null
|
|
||||||
ip link delete wg0 2>/dev/null
|
|
||||||
;;
|
|
||||||
restart) $0 stop; sleep 1; $0 start ;;
|
restart) $0 stop; sleep 1; $0 start ;;
|
||||||
esac
|
esac
|
||||||
INITEOF
|
INITEOF
|
||||||
@@ -748,47 +774,63 @@ chmod +x /opt/etc/init.d/S50wg0
|
|||||||
echo '[3/3] Настройка WireGuard...'
|
echo '[3/3] Настройка WireGuard...'
|
||||||
DONE=0
|
DONE=0
|
||||||
|
|
||||||
# Вариант 1: Keenetic CLI (ndmc) — нативная интеграция (появится в Подключениях)
|
# === Вариант 1: Keenetic RCI HTTP API (нативно → появится в Другие подключения) ===
|
||||||
if command -v ndmc >/dev/null 2>&1; then
|
{rci_files}
|
||||||
echo 'Keenetic CLI (ndmc)...'
|
AUTH_HDR=$(curl -si http://localhost/auth 2>/dev/null)
|
||||||
printf '%s\\n' \\
|
REALM=$(printf '%s' "$AUTH_HDR" | grep -i 'x-ndm-realm' | tail -1 | tr -d '\\r\\n' | awk -F': ' '{{print $2}}')
|
||||||
'interface Wireguard0' \\
|
CHAL=$(printf '%s' "$AUTH_HDR" | grep -i 'x-ndm-challenge' | tail -1 | tr -d '\\r\\n' | awk -F': ' '{{print $2}}')
|
||||||
'ip address {client_ip}/32' \\
|
|
||||||
'wireguard private-key {priv_key}' \\
|
|
||||||
'wireguard peer {pub_key}' \\
|
|
||||||
'wireguard allowed-ips 0.0.0.0 0.0.0.0' \\
|
|
||||||
'wireguard endpoint {vps_host} {port}' \\
|
|
||||||
'wireguard persistent-keepalive 25' \\
|
|
||||||
'exit' \\
|
|
||||||
'exit' \\
|
|
||||||
'system configuration save' | ndmc 2>&1 && DONE=1 && \\
|
|
||||||
echo 'OK: Keenetic WireGuard нативно (проверь в Подключениях)' || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Вариант 2: raw wg + ip (Entware, без wg-quick)
|
if [ -n "$REALM" ] && [ -n "$CHAL" ]; then
|
||||||
|
HASH=$(printf 'admin:%s:{router_pass}' "$REALM" | md5sum | cut -c1-32)
|
||||||
|
RESP=$(printf '%s%s' "$HASH" "$CHAL" | md5sum | cut -c1-32)
|
||||||
|
LOGIN=$(curl -s -c /tmp/rci.jar http://localhost/auth \\
|
||||||
|
-H 'Content-Type: application/json' \\
|
||||||
|
-d '{{"login":"admin","password":"'"$RESP"'"}}')
|
||||||
|
echo "RCI auth: $LOGIN"
|
||||||
|
|
||||||
|
if printf '%s' "$LOGIN" | grep -qi 'success\\|true'; then
|
||||||
|
for WG_IDX in 2 3 4 5 0 1; do
|
||||||
|
IFACE=$(curl -s -b /tmp/rci.jar \\
|
||||||
|
"http://localhost/rci/show/interface/Wireguard${{WG_IDX}}/" 2>/dev/null)
|
||||||
|
if printf '%s' "$IFACE" | grep -q '"description"\\|"type"'; then
|
||||||
|
echo "Wireguard${{WG_IDX}} занят, пробуем следующий..."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
CFG=$(curl -s -b /tmp/rci.jar -X POST http://localhost/rci/ \\
|
||||||
|
-H 'Content-Type: application/json' \\
|
||||||
|
--data-binary @/tmp/wg_rci_${{WG_IDX}}.json)
|
||||||
|
echo "RCI config Wireguard${{WG_IDX}}: $CFG"
|
||||||
|
curl -s -b /tmp/rci.jar -X POST http://localhost/rci/ \\
|
||||||
|
-H 'Content-Type: application/json' \\
|
||||||
|
-d '{rci_save}' >/dev/null
|
||||||
|
DONE=1
|
||||||
|
echo "OK: Wireguard${{WG_IDX}} создан в Keenetic (проверь Другие подключения)"
|
||||||
|
break
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
rm -f /tmp/rci.jar /tmp/wg_rci_*.json 2>/dev/null
|
||||||
|
|
||||||
|
# === Вариант 2: raw wg + ip (Entware wg0, без интеграции в UI) ===
|
||||||
if [ "$DONE" = "0" ]; then
|
if [ "$DONE" = "0" ]; then
|
||||||
echo 'Используем wg + ip...'
|
echo 'RCI не сработал, используем wg + ip...'
|
||||||
ip link show wg0 >/dev/null 2>&1 || ip link add wg0 type wireguard 2>/dev/null || true
|
ip link show wg0 >/dev/null 2>&1 || ip link add wg0 type wireguard 2>/dev/null || true
|
||||||
wg set wg0 private-key /opt/etc/wireguard/wg0.key \\
|
wg set wg0 private-key /opt/etc/wireguard/wg0.key \\
|
||||||
peer '{pub_key}' \\
|
peer '{pub_key}' allowed-ips 0.0.0.0/0 \\
|
||||||
allowed-ips 0.0.0.0/0 \\
|
endpoint '{vps_host}:{port}' persistent-keepalive 25 && DONE=1 || true
|
||||||
endpoint '{vps_host}:{port}' \\
|
|
||||||
persistent-keepalive 25 && DONE=1 || true
|
|
||||||
ip addr add '{client_ip}/32' dev wg0 2>/dev/null || true
|
ip addr add '{client_ip}/32' dev wg0 2>/dev/null || true
|
||||||
ip link set up dev wg0 2>/dev/null || true
|
ip link set up dev wg0 2>/dev/null || true
|
||||||
[ "$DONE" = "1" ] && echo 'OK: wg0 поднят'
|
if [ "$DONE" = "1" ]; then
|
||||||
fi
|
echo 'OK: wg0 поднят (VPN работает, но не виден в Keenetic UI)'
|
||||||
|
echo 'Чтобы добавить в UI вручную: Другие подключения → Загрузить из файла'
|
||||||
if [ "$DONE" = "0" ]; then
|
echo 'Файл конфига: /opt/etc/wireguard/wg0.conf'
|
||||||
echo 'ОШИБКА: не удалось настроить WireGuard'
|
fi
|
||||||
echo 'Конфиг сохранён: /opt/etc/wireguard/wg0.conf'
|
|
||||||
echo 'После перезагрузки S50wg0 попробует снова'
|
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
[ "$DONE" = "0" ] && {{ echo 'ОШИБКА: VPN не удалось настроить'; exit 1; }}
|
||||||
echo '=== OK ==='
|
echo '=== OK ==='
|
||||||
echo 'VPN IP: {client_ip}'
|
echo 'VPN IP: {client_ip}'
|
||||||
wg show wg0 2>/dev/null | head -8 || ip addr show wg0 2>/dev/null | grep inet || true
|
wg show 2>/dev/null | head -10 || ip addr show wg0 2>/dev/null | grep inet || true
|
||||||
"""
|
"""
|
||||||
|
|
||||||
rc, out, err = await _ssh_on_router(rcfg, script, timeout=120)
|
rc, out, err = await _ssh_on_router(rcfg, script, timeout=120)
|
||||||
|
|||||||
Reference in New Issue
Block a user