tunnel: WireGuard VPS↔роутер для серых IP, без правки других сервисов

Решает доступ к роутеру с серым 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 для масштабной установки.
This commit is contained in:
Андрей Бобырев
2026-04-26 11:12:19 +03:00
parent 7c2adf95b9
commit c46273e72a
5 changed files with 562 additions and 0 deletions

169
tunnel/kssh-tun Executable file
View File

@@ -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" <<EOF
[Interface]
Address = $WG_SERVER_CIDR
ListenPort = $WG_PORT
PrivateKey = $(cat "$WG_DIR/server_private.key")
SaveConfig = false
EOF
if [ -d "$PEERS_DIR" ]; then
for f in "$PEERS_DIR"/*.peer; do
[ -f "$f" ] || continue
( . "$f"
cat <<EOF >> "$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 <<EOF
VPS_ENDPOINT=$PUB_IP:$WG_PORT \\
VPS_PUBKEY='$SERVER_PUB' \\
CLIENT_IP=$CLIENT_IP \\
CLIENT_PRIVKEY='$CLIENT_PRIV' \\
sh -c "curl -fsSL https://raw.githubusercontent.com/${KSSH_REPO}/${KSSH_BRANCH}/tunnel/tunnel-install.sh | sh"
EOF
echo "============================================================"
}
cmd="${1:-}"
shift || true
case "$cmd" in
add)
require_root
NAME="${1:-}"
[ -z "$NAME" ] && { echo "usage: kssh-tun add <name>" >&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" <<EOF
NAME="$NAME"
CLIENT_IP="$CLIENT_IP"
CLIENT_PRIV="$CLIENT_PRIV"
CLIENT_PUB="$CLIENT_PUB"
EOF
chmod 600 "$PEER_FILE"
regen_conf
echo "Добавлен: $NAME → $CLIENT_IP"
fi
print_env_block "$PEER_FILE"
;;
list)
if [ ! -d "$PEERS_DIR" ]; then echo "(нет peer-ов)"; exit 0; fi
printf "%-30s %s\n" "NAME" "IP"
for f in "$PEERS_DIR"/*.peer; do
[ -f "$f" ] || continue
( . "$f"; printf "%-30s %s\n" "$NAME" "$CLIENT_IP" )
done
;;
show)
NAME="${1:-}"
[ -z "$NAME" ] && { echo "usage: kssh-tun show <name>" >&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 <name>" >&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 <<EOF
kssh-tun — управление WireGuard-клиентами для keenetic_ssh-web tunnel.
kssh-tun add <name> добавить peer, вывести ENV-блок для роутера
kssh-tun show <name> повторно вывести ENV-блок
kssh-tun list список peer-ов
kssh-tun remove <name> удалить 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