Files
keenetic_ssh-web/tunnel/kssh-tun
Андрей Бобырев c6db82bd2e tunnel: per-peer DNAT ports via kssh-tun add --dnat
Made-with: Cursor
2026-04-26 16:19:59 +03:00

203 lines
6.2 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/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"
# Самый надёжный способ применить новый конфиг: рестарт wg-quick@WG_IF.
# `wg syncconf` через process substitution падал молча на части систем.
if command -v systemctl >/dev/null 2>&1 && \
systemctl is-enabled "wg-quick@$WG_IF" >/dev/null 2>&1; then
systemctl restart "wg-quick@$WG_IF" || \
echo "warning: systemctl restart wg-quick@$WG_IF не удался" >&2
elif ip link show "$WG_IF" >/dev/null 2>&1; then
TMP_STRIP="$(mktemp)"
if wg-quick strip "$WG_IF" > "$TMP_STRIP" 2>/dev/null; then
wg syncconf "$WG_IF" "$TMP_STRIP" || \
echo "warning: wg syncconf не удался — перезапустите wg-quick@$WG_IF" >&2
fi
rm -f "$TMP_STRIP"
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. Скопируйте 5 строк ниже на роутер"
echo "(можно блоком, можно по одной — работает в любом shell):"
echo
cat <<EOF
export VPS_ENDPOINT=$PUB_IP:$WG_PORT
export VPS_PUBKEY='$SERVER_PUB'
export CLIENT_IP=$CLIENT_IP
export CLIENT_PRIVKEY='$CLIENT_PRIV'
export DNAT_PORTS='${DNAT_PORTS:-81,79}'
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; }
shift || true
DNAT_PORTS_ARG=""
while [ $# -gt 0 ]; do
case "$1" in
--dnat)
DNAT_PORTS_ARG="${2:-}"
shift 2 || true
;;
--dnat=*)
DNAT_PORTS_ARG="${1#--dnat=}"
shift || true
;;
*)
echo "Неизвестный аргумент: $1" >&2
echo "usage: kssh-tun add <name> [--dnat 81,79]" >&2
exit 1
;;
esac
done
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"
DNAT_PORTS="$DNAT_PORTS_ARG"
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> [--dnat 81,79] добавить 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