From c46273e72af22fbccba3b35c8fcb7dbd71734473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=91=D0=BE=D0=B1?= =?UTF-8?q?=D1=8B=D1=80=D0=B5=D0=B2?= Date: Sun, 26 Apr 2026 11:12:19 +0300 Subject: [PATCH] =?UTF-8?q?tunnel:=20WireGuard=20VPS=E2=86=94=D1=80=D0=BE?= =?UTF-8?q?=D1=83=D1=82=D0=B5=D1=80=20=D0=B4=D0=BB=D1=8F=20=D1=81=D0=B5?= =?UTF-8?q?=D1=80=D1=8B=D1=85=20IP,=20=D0=B1=D0=B5=D0=B7=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=BA=D0=B8=20=D0=B4=D1=80=D1=83=D0=B3=D0=B8=D1=85?= =?UTF-8?q?=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Решает доступ к роутеру с серым IP через белый VPS — поднимаем WG-туннель (UDP 51820, подсеть 10.99.0.0/24), и любой сервис на VPS обращается к роутеру по 10.99.0.X. SSH, HTTP, что угодно — без проброса портов и без изменений в keenetic_ssh-web/keenetic-unified/Domain Hydra/etc. Что добавлено: - tunnel/server-install.sh — установка WG-сервера на Ubuntu VPS, генерация ключей, запуск wg-quick@wg0, открытие 51820/udp в ufw. - tunnel/kssh-tun — утилита управления peer-ами на VPS: add/list/show/remove. add генерит ключи и IP, печатает ENV-блок с одной командой для роутера. - tunnel/tunnel-install.sh — клиент для роутера (Entware), принимает 4 ENV (VPS_ENDPOINT/VPS_PUBKEY/CLIENT_IP/CLIENT_PRIVKEY), ставит wireguard-tools, поднимает wg0 и init-скрипт автозапуска. - tunnel/README.md — пошаговая инструкция массовой установки. - README.md: краткая ссылка из основного раздела. Параметры подсети/порта/имени репо — через ENV для масштабной установки. --- README.md | 21 +++++ tunnel/README.md | 168 ++++++++++++++++++++++++++++++++++++++ tunnel/kssh-tun | 169 +++++++++++++++++++++++++++++++++++++++ tunnel/server-install.sh | 86 ++++++++++++++++++++ tunnel/tunnel-install.sh | 118 +++++++++++++++++++++++++++ 5 files changed, 562 insertions(+) create mode 100644 tunnel/README.md create mode 100755 tunnel/kssh-tun create mode 100755 tunnel/server-install.sh create mode 100755 tunnel/tunnel-install.sh diff --git a/README.md b/README.md index 768fc98..79b2bde 100644 --- a/README.md +++ b/README.md @@ -198,6 +198,27 @@ KEEP_DATA=1 ./uninstall.sh --- +## Удалённый доступ через VPS (туннель WireGuard) + +Если у роутера **серый IP**, а нужно дотянуться до панели/SSH с интернета — поднимите WireGuard-туннель: см. [tunnel/README.md](tunnel/README.md). + +Кратко: + +```sh +# На VPS (Ubuntu) — один раз +curl -fsSL https://raw.githubusercontent.com/andrey271192/keenetic_ssh-web/main/tunnel/server-install.sh | sudo sh + +# На VPS — для каждого роутера +sudo kssh-tun add router-XYZ +# → выводит готовую команду с 4 ENV для роутера + +# На роутере — копируете эту команду и выполняете +``` + +После этого с VPS роутер доступен по `10.99.0.X:любой-порт`. Никаких изменений в `keenetic_ssh-web` и других сервисах не требуется. + +--- + ## Связанные проекты - [keenetic-unified](https://github.com/andrey271192/keenetic-unified) — мониторинг и управление с **VPS** по SSH (нужен «белый» IP на WAN для SSH). diff --git a/tunnel/README.md b/tunnel/README.md new file mode 100644 index 0000000..7eecd39 --- /dev/null +++ b/tunnel/README.md @@ -0,0 +1,168 @@ +# tunnel/ — WireGuard-туннель VPS ↔ роутер + +Решает задачу: у вас на роутере **серый IP** (нет публичного), но есть **VPS с белым IP**. Поднимаем WireGuard, и любой сервис на VPS обращается к роутеру по адресу из туннельной подсети (`10.99.0.X`), как если бы они были в одной LAN. SSH, HTTP, что угодно — без проброса портов и без переписывания других сервисов (`Domain Hydra`, `keenetic-unified`, `keenetic_ssh-web`, …). + +``` +[VPS, белый IP] [Роутер, серый IP] + :443 Caddy/Nginx → wg0 10.99.0.1 ⇄ wg0 10.99.0.X → 127.0.0.1:2001 (kssh-web) + 127.0.0.1:22 (SSH) + …и т. д. +``` + +UDP `51820`, подсеть `10.99.0.0/24`. Параметры можно поменять через ENV. + +--- + +## 1. На VPS (Ubuntu) — один раз + +```sh +curl -fsSL https://raw.githubusercontent.com/andrey271192/keenetic_ssh-web/main/tunnel/server-install.sh | sudo sh +``` + +Что делает: + +- `apt install wireguard wireguard-tools curl` +- генерит серверные ключи (`/etc/wireguard/server_{private,public}.key`) +- создаёт `/etc/wireguard/wg0.conf` (UDP 51820, IP `10.99.0.1/24`) +- ставит утилиту `kssh-tun` в `/usr/local/bin/` +- открывает `51820/udp` в `ufw`, если он активен +- запускает `wg-quick@wg0` и ставит на автозапуск (`systemctl enable`) + +В конце выводит: + +``` +Endpoint: :51820/udp +PublicKey: +``` + +--- + +## 2. На VPS — добавление каждого роутера + +```sh +sudo kssh-tun add router-andrey-1 +``` + +Утилита: + +- генерит пару ключей клиента +- выделяет следующий свободный IP в подсети (`10.99.0.2`, `10.99.0.3`, …) +- сохраняет peer-файл в `/etc/wireguard/peers/.peer` +- регенерирует `wg0.conf` и применяет без рестарта (`wg syncconf`) +- **печатает готовую команду для роутера** — четыре `ENV=…` + `curl …` одной портянкой + +Дополнительно: + +```sh +sudo kssh-tun list # список peer-ов +sudo kssh-tun show router-andrey-1 # повторно вывести ENV-блок +sudo kssh-tun remove router-andrey-1 # удалить peer +sudo kssh-tun status # wg show wg0 +``` + +--- + +## 3. На роутере (Keenetic + Entware) + +Скопируйте блок из `kssh-tun add` и выполните на роутере. Пример (значения подставит `kssh-tun add`): + +```sh +VPS_ENDPOINT=SERVER_IP:51820 \ +VPS_PUBKEY='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=' \ +CLIENT_IP=10.99.0.5 \ +CLIENT_PRIVKEY='yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy=' \ +sh -c "curl -fsSL https://raw.githubusercontent.com/andrey271192/keenetic_ssh-web/main/tunnel/tunnel-install.sh | sh" +``` + +Что делает: + +- `opkg install wireguard-tools` (если ещё нет) +- пишет `/opt/etc/wireguard/wg0.conf` +- поднимает интерфейс `wg0` с адресом `CLIENT_IP/24` +- маршрутизирует `10.99.0.0/24` через `wg0` +- ставит init-скрипт `/opt/etc/init.d/S50kssh-tunnel` ({start|stop|restart|status}) и rc.d-симлинк автозапуска +- в конце пингует `10.99.0.1` (VPS) для проверки + +Если ping не прошёл — почти всегда дело в фаерволе VPS: `sudo ufw allow 51820/udp` (или эквивалент для iptables/firewalld). + +--- + +## 4. Использование туннеля + +После запуска любые сервисы на VPS обращаются к роутеру по `10.99.0.X`: + +```sh +# С VPS — веб-панель keenetic_ssh-web +curl http://10.99.0.5:2001/ + +# SSH на роутер с VPS (если у роутера включён SSH) +ssh root@10.99.0.5 + +# Reverse-proxy на VPS: keenetic.example.com → http://10.99.0.5:2001 +# (Caddy/Nginx — прозрачно) +``` + +--- + +## Параметры (ENV) + +### `server-install.sh` + +| Переменная | По умолчанию | Описание | +|---|---|---| +| `WG_PORT` | `51820` | UDP-порт сервера. | +| `WG_SUBNET_PREFIX` | `10.99.0` | Первые три октета подсети `/24`. | +| `WG_SERVER_LAST_OCTET` | `1` | Последний октет адреса сервера в туннеле. | +| `KSSH_REPO` | `andrey271192/keenetic_ssh-web` | Репо, из которого ставить `kssh-tun` и `tunnel-install.sh`. | +| `KSSH_BRANCH` | `main` | Ветка. | + +### `tunnel-install.sh` (на роутере) + +| Переменная | Обязательная | Описание | +|---|---|---| +| `VPS_ENDPOINT` | да | `<публичный IP>:<порт>`, UDP. | +| `VPS_PUBKEY` | да | PublicKey сервера. | +| `CLIENT_IP` | да | IP роутера в туннельной подсети, например `10.99.0.5`. | +| `CLIENT_PRIVKEY` | да | PrivateKey роутера (выдаёт `kssh-tun add`). | +| `WG_IF` | `wg0` | Имя интерфейса. | +| `WG_SUBNET` | `10.99.0.0/24` | AllowedIPs клиента (что маршрутизировать в туннель). | +| `WG_KEEPALIVE` | `25` | `PersistentKeepalive` в секундах (нужен из-за NAT провайдера). | + +--- + +## Безопасность + +- WireGuard сам по себе шифрует трафик; но peer-файлы (`/etc/wireguard/peers/*.peer`) содержат **приватные ключи клиентов** в открытом виде — храните VPS чистым. +- Закройте `tunnel-install.sh`-команду от посторонних (там есть `CLIENT_PRIVKEY`). В Telegram/мессенджеры лучше не пересылать. +- `kssh-tun show ` всегда покажет ENV-блок снова — пароль не нужен, повторно сгенерировать ключ не получится. Если ключ утёк — `remove` и заново `add`. +- На стороне роутера `tunnel-install.sh` пишет конфиг в `/opt/etc/wireguard/wg0.conf` с правами `600`. + +--- + +## Удаление + +На VPS: + +```sh +sudo kssh-tun remove # для каждого клиента +sudo systemctl disable --now wg-quick@wg0 +sudo apt remove --purge wireguard wireguard-tools +sudo rm -rf /etc/wireguard /usr/local/bin/kssh-tun +``` + +На роутере: + +```sh +/opt/etc/init.d/S50kssh-tunnel stop +rm -f /opt/etc/rc.d/S50kssh-tunnel /opt/etc/init.d/S50kssh-tunnel +rm -rf /opt/etc/wireguard +``` + +--- + +## Что не входит в этот туннель + +- **NAT с роутера в интернет через VPS**. Здесь VPS лишь видит роутер; чтобы роутер выходил в интернет через VPS, на сервере нужен `MASQUERADE` и `ip_forward`. Не наша цель. +- **Доступ из VPS в LAN роутера** (`192.168.1.0/24`). Если нужно — расширьте `AllowedIPs` peer-а на стороне сервера и включите `ip_forward` на роутере. + +В обоих случаях скажите — добавлю в установщик ENV-флаги. diff --git a/tunnel/kssh-tun b/tunnel/kssh-tun new file mode 100755 index 0000000..fe6decc --- /dev/null +++ b/tunnel/kssh-tun @@ -0,0 +1,169 @@ +#!/bin/bash +# kssh-tun — управление peer-ами WireGuard-сервера для keenetic_ssh-web tunnel. +# Установлен через tunnel/server-install.sh в /usr/local/bin/kssh-tun. +set -e + +ENV_FILE="/etc/wireguard/.kssh-tun.env" +[ -f "$ENV_FILE" ] || { echo "Сервер не инициализирован. Запустите server-install.sh." >&2; exit 1; } +# shellcheck disable=SC1090 +. "$ENV_FILE" + +PEERS_DIR="$WG_DIR/peers" +CONF="$WG_DIR/$WG_IF.conf" + +require_root() { + if [ "$(id -u)" -ne 0 ]; then echo "Нужны права root (sudo)." >&2; exit 1; fi +} + +regen_conf() { + umask 077 + TMP="$(mktemp)" + cat > "$TMP" <> "$TMP" +# peer: $NAME ($CLIENT_IP) +[Peer] +PublicKey = $CLIENT_PUB +AllowedIPs = $CLIENT_IP/32 + +EOF + ) + done + fi + install -m 600 "$TMP" "$CONF" + rm -f "$TMP" + if ip link show "$WG_IF" >/dev/null 2>&1; then + wg syncconf "$WG_IF" <(wg-quick strip "$WG_IF") 2>/dev/null || true + fi +} + +next_ip() { + USED="$WG_SERVER_IP" + if [ -d "$PEERS_DIR" ]; then + for f in "$PEERS_DIR"/*.peer; do + [ -f "$f" ] || continue + ip=$(. "$f"; echo "$CLIENT_IP") + USED="$USED $ip" + done + fi + for i in $(seq 2 254); do + candidate="${WG_SUBNET_PREFIX}.$i" + case " $USED " in *" $candidate "*) ;; *) echo "$candidate"; return 0;; esac + done + echo "Нет свободного IP в подсети ${WG_SUBNET_PREFIX}.0/24" >&2 + return 1 +} + +print_env_block() { + PEER_FILE="$1" + # shellcheck disable=SC1090 + . "$PEER_FILE" + SERVER_PUB=$(cat "$WG_DIR/server_public.key") + PUB_IP="$(curl -s4 -m 3 ifconfig.me 2>/dev/null || hostname -I | awk '{print $1}')" + echo + echo "============================================================" + echo "Peer '$NAME' → $CLIENT_IP. Скопируйте на роутер ОДНОЙ командой:" + echo + cat <" >&2; exit 1; } + case "$NAME" in *[!a-zA-Z0-9_-]*) echo "Имя: только [a-zA-Z0-9_-]" >&2; exit 1;; esac + mkdir -p "$PEERS_DIR" + PEER_FILE="$PEERS_DIR/$NAME.peer" + if [ -f "$PEER_FILE" ]; then + echo "Peer '$NAME' уже существует — повторно вывожу ENV-блок:" + else + umask 077 + CLIENT_IP="$(next_ip)" + CLIENT_PRIV="$(wg genkey)" + CLIENT_PUB="$(echo "$CLIENT_PRIV" | wg pubkey)" + cat > "$PEER_FILE" <" >&2; exit 1; } + PEER_FILE="$PEERS_DIR/$NAME.peer" + [ -f "$PEER_FILE" ] || { echo "нет peer-а: $NAME" >&2; exit 1; } + print_env_block "$PEER_FILE" + ;; + remove|rm|del) + require_root + NAME="${1:-}" + [ -z "$NAME" ] && { echo "usage: kssh-tun remove " >&2; exit 1; } + PEER_FILE="$PEERS_DIR/$NAME.peer" + [ -f "$PEER_FILE" ] || { echo "нет peer-а: $NAME" >&2; exit 1; } + rm -f "$PEER_FILE" + regen_conf + echo "Удалён: $NAME" + ;; + _regen) + require_root + regen_conf + ;; + status) + if command -v wg >/dev/null 2>&1; then + wg show "$WG_IF" 2>/dev/null || echo "(wg-интерфейс $WG_IF не поднят)" + fi + ;; + ""|help|-h|--help) + cat < добавить peer, вывести ENV-блок для роутера + kssh-tun show повторно вывести ENV-блок + kssh-tun list список peer-ов + kssh-tun remove удалить peer + kssh-tun status wg show $WG_IF + +Конфиг: $WG_DIR/$WG_IF.conf (регенерируется автоматически) +Peer-файлы: $PEERS_DIR/*.peer +EOF + ;; + *) + echo "Неизвестная команда: $cmd. См. kssh-tun help" >&2 + exit 1 + ;; +esac diff --git a/tunnel/server-install.sh b/tunnel/server-install.sh new file mode 100755 index 0000000..82a0628 --- /dev/null +++ b/tunnel/server-install.sh @@ -0,0 +1,86 @@ +#!/bin/bash +# tunnel/server-install.sh — установка WireGuard-сервера на Ubuntu VPS. +# Поднимает wg0 (UDP 51820, подсеть 10.99.0.0/24) и ставит утилиту kssh-tun +# для добавления/удаления клиентов с генерацией ENV-блока для роутера. +set -e + +WG_PORT="${WG_PORT:-51820}" +WG_SUBNET_PREFIX="${WG_SUBNET_PREFIX:-10.99.0}" +WG_SERVER_LAST_OCTET="${WG_SERVER_LAST_OCTET:-1}" +WG_SERVER_IP="${WG_SUBNET_PREFIX}.${WG_SERVER_LAST_OCTET}" +WG_SERVER_CIDR="${WG_SERVER_IP}/24" +WG_DIR="/etc/wireguard" +WG_IF="wg0" +KSSH_REPO="${KSSH_REPO:-andrey271192/keenetic_ssh-web}" +KSSH_BRANCH="${KSSH_BRANCH:-main}" +RAW="https://raw.githubusercontent.com/${KSSH_REPO}/${KSSH_BRANCH}/tunnel" + +if [ "$(id -u)" -ne 0 ]; then + echo "Запустите через sudo." >&2 + exit 1 +fi + +echo "==> apt: wireguard wireguard-tools curl" +DEBIAN_FRONTEND=noninteractive apt-get update -qq +DEBIAN_FRONTEND=noninteractive apt-get install -y -qq wireguard wireguard-tools iproute2 curl + +mkdir -p "$WG_DIR" "$WG_DIR/peers" +chmod 700 "$WG_DIR" + +# Серверные ключи (один раз) +if [ ! -s "$WG_DIR/server_private.key" ] || [ ! -s "$WG_DIR/server_public.key" ]; then + echo "==> Генерация ключей сервера" + umask 077 + wg genkey | tee "$WG_DIR/server_private.key" | wg pubkey > "$WG_DIR/server_public.key" +fi + +# Сохраняем параметры запуска (используется kssh-tun для регенерации конфига) +cat > "$WG_DIR/.kssh-tun.env" < Установка /usr/local/bin/kssh-tun" +if [ -f "$(dirname "$0")/kssh-tun" ]; then + install -m 755 "$(dirname "$0")/kssh-tun" /usr/local/bin/kssh-tun +else + curl -fsSL "$RAW/kssh-tun" -o /usr/local/bin/kssh-tun + chmod 755 /usr/local/bin/kssh-tun +fi + +# Сборка wg0.conf из server-ключа + всех peer-файлов +/usr/local/bin/kssh-tun _regen + +# Включить и запустить +systemctl enable "wg-quick@${WG_IF}" >/dev/null 2>&1 || true +systemctl restart "wg-quick@${WG_IF}" + +# Открыть UDP-порт в UFW, если он активен +if command -v ufw >/dev/null 2>&1 && ufw status 2>/dev/null | grep -q "Status: active"; then + ufw allow "${WG_PORT}/udp" >/dev/null 2>&1 || true +fi + +PUB_IP="$(curl -s4 ifconfig.me 2>/dev/null || hostname -I | awk '{print $1}')" +SERVER_PUB="$(cat "$WG_DIR/server_public.key")" + +echo +echo "============================================================" +echo "WireGuard сервер готов." +echo " Endpoint: ${PUB_IP}:${WG_PORT}/udp" +echo " PublicKey: ${SERVER_PUB}" +echo " Server IP: ${WG_SERVER_IP}" +echo +echo "Добавление клиента (peer):" +echo " sudo kssh-tun add # выдаст ENV-блок для роутера" +echo " sudo kssh-tun list" +echo " sudo kssh-tun show " +echo " sudo kssh-tun remove " +echo "============================================================" diff --git a/tunnel/tunnel-install.sh b/tunnel/tunnel-install.sh new file mode 100755 index 0000000..2fe7715 --- /dev/null +++ b/tunnel/tunnel-install.sh @@ -0,0 +1,118 @@ +#!/bin/sh +# tunnel/tunnel-install.sh — установка WireGuard-клиента на роутере (Entware). +# Запускается с роутера, принимает 4 ENV-переменные (выдаёт kssh-tun add на VPS): +# VPS_ENDPOINT=: — публичный endpoint VPS, UDP +# VPS_PUBKEY= — публичный ключ сервера +# CLIENT_IP=10.99.0.X — IP роутера в туннельной подсети +# CLIENT_PRIVKEY= — приватный ключ роутера +# По желанию: +# WG_IF=wg0 — имя интерфейса (по умолчанию wg0) +# WG_SUBNET=10.99.0.0/24 — какие IP маршрутизируются через туннель +# WG_KEEPALIVE=25 — PersistentKeepalive в секундах +set -e + +WG_IF="${WG_IF:-wg0}" +WG_SUBNET="${WG_SUBNET:-10.99.0.0/24}" +WG_KEEPALIVE="${WG_KEEPALIVE:-25}" +WG_DIR="/opt/etc/wireguard" +INIT="/opt/etc/init.d/S50kssh-tunnel" + +[ -z "$VPS_ENDPOINT" ] && { echo "VPS_ENDPOINT обязателен" >&2; exit 1; } +[ -z "$VPS_PUBKEY" ] && { echo "VPS_PUBKEY обязателен" >&2; exit 1; } +[ -z "$CLIENT_IP" ] && { echo "CLIENT_IP обязателен" >&2; exit 1; } +[ -z "$CLIENT_PRIVKEY" ] && { echo "CLIENT_PRIVKEY обязателен" >&2; exit 1; } + +echo "==> WireGuard-клиент: $WG_IF $CLIENT_IP → $VPS_ENDPOINT" + +# wireguard-tools (на Keenetic ядре уже есть модуль wireguard, нужны только утилиты) +if ! command -v wg >/dev/null 2>&1; then + if command -v opkg >/dev/null 2>&1; then + opkg update || echo "(opkg update: частичные ошибки источников — продолжаем)" + opkg install wireguard-tools >/dev/null 2>&1 || true + fi +fi +if ! command -v wg >/dev/null 2>&1; then + echo "wg не найден. Установите вручную: opkg install wireguard-tools" >&2 + exit 1 +fi + +mkdir -p "$WG_DIR" +chmod 700 "$WG_DIR" + +CONF="$WG_DIR/$WG_IF.conf" +umask 077 +cat > "$CONF" < "$INIT" </dev/null 2>&1 && ip link del "\$WG_IF" 2>/dev/null + ip link add "\$WG_IF" type wireguard + wg setconf "\$WG_IF" "\$CONF" + ip addr add "\$ADDR" dev "\$WG_IF" + ip link set "\$WG_IF" up + ip route add "\$SUBNET" dev "\$WG_IF" 2>/dev/null || true + echo "started \$WG_IF \$ADDR" +} +down() { + ip link del "\$WG_IF" 2>/dev/null || true + echo "stopped \$WG_IF" +} + +case "\$1" in + start) up ;; + stop) down ;; + restart) down; sleep 1; up ;; + status) + ip -4 -o addr show "\$WG_IF" 2>/dev/null && wg show "\$WG_IF" 2>/dev/null || echo "(\$WG_IF не поднят)" + ;; + *) echo "usage: \$0 {start|stop|restart|status}"; exit 1 ;; +esac +INIEOF +chmod +x "$INIT" + +# Поднять +"$INIT" restart || true + +# Автозапуск через rc.d, если поддерживается +if [ -d /opt/etc/rc.d ] && [ ! -e /opt/etc/rc.d/S50kssh-tunnel ]; then + ln -sf "$INIT" /opt/etc/rc.d/S50kssh-tunnel 2>/dev/null && \ + echo "==> Автозапуск: /opt/etc/rc.d/S50kssh-tunnel → $INIT" +fi + +sleep 1 +echo +echo "============================================================" +echo "Туннель $WG_IF поднят: $CLIENT_IP → $VPS_ENDPOINT" +echo "Конфиг: $CONF" +echo "Init: $INIT {start|stop|restart|status}" +echo +ip -4 -o addr show "$WG_IF" 2>/dev/null | sed 's/^/ /' +wg show "$WG_IF" 2>/dev/null | sed 's/^/ /' +echo +VPS_IP="$(echo "$VPS_ENDPOINT" | cut -d: -f1)" +SERVER_TUN_IP="$(echo "$WG_SUBNET" | sed 's|0/24$|1|')" +echo "Проверка: ping -c 2 $SERVER_TUN_IP" +ping -c 2 -W 2 "$SERVER_TUN_IP" 2>/dev/null | sed 's/^/ /' || echo " (ping не прошёл — проверьте VPS firewall: ufw allow 51820/udp)" +echo +echo "Теперь с VPS ($VPS_IP) роутер доступен по адресу: $CLIENT_IP" +echo "Например, веб-панель: http://$CLIENT_IP:2001" +echo "============================================================"