Files
wireguard-scripts/add-user.sh
T
2026-10-11 22:46:09 +03:00

201 lines
5.2 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
WG_INTERFACE="wg0"
WG_CONF="/etc/wireguard/${WG_INTERFACE}.conf"
WG_DIR="/etc/wireguard"
CLIENT_DIR="${WG_DIR}/clients"
SERVER_PORT="51820"
DEFAULT_ENDPOINT="37.26.136.232"
SERVER_VPN_IP="10.9.0.1"
VPN_SUBNET_PREFIX="10.9.0"
START_HOST="2"
END_HOST="254"
USE_IPV6="0"
IPV6_PREFIX="fd05:13:53::"
umask 077
die() {
echo "Ошибка: $*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || die "Не найдена команда: $1"
}
usage() {
cat <<EOF
Использование:
sudo $0 CLIENT_NAME [ENDPOINT]
Примеры:
sudo $0 android-phone
sudo $0 work-laptop vpn.example.com
sudo $0 user5 37.26.136.232
Что делает:
- создаёт ключи клиента
- подбирает следующий свободный IP
- добавляет peer в ${WG_CONF}
- создаёт клиентский конфиг в ${CLIENT_DIR}
- применяет конфиг WireGuard
EOF
}
[[ $# -ge 1 ]] || { usage; exit 1; }
CLIENT_NAME="$1"
ENDPOINT="${2:-$DEFAULT_ENDPOINT}"
[[ "$EUID" -eq 0 ]] || die "Скрипт нужно запускать через sudo/root"
[[ -f "$WG_CONF" ]] || die "Не найден конфиг ${WG_CONF}"
need_cmd wg
need_cmd awk
need_cmd grep
need_cmd sed
need_cmd sort
need_cmd tee
need_cmd mkdir
need_cmd chmod
mkdir -p "$CLIENT_DIR"
if [[ ! "$CLIENT_NAME" =~ ^[a-zA-Z0-9._-]+$ ]]; then
die "Имя клиента может содержать только буквы, цифры, точку, подчёркивание и дефис"
fi
CLIENT_CONF="${CLIENT_DIR}/${WG_INTERFACE}-client-${CLIENT_NAME}.conf"
CLIENT_PRIVKEY_FILE="${CLIENT_DIR}/${CLIENT_NAME}.key"
CLIENT_PUBKEY_FILE="${CLIENT_DIR}/${CLIENT_NAME}.pub"
CLIENT_PSK_FILE="${CLIENT_DIR}/${CLIENT_NAME}.psk"
if grep -qE "^### Client ${CLIENT_NAME}$" "$WG_CONF"; then
die "Клиент с именем '${CLIENT_NAME}' уже есть в ${WG_CONF}"
fi
if [[ -e "$CLIENT_CONF" || -e "$CLIENT_PRIVKEY_FILE" || -e "$CLIENT_PUBKEY_FILE" || -e "$CLIENT_PSK_FILE" ]]; then
die "Файлы клиента '${CLIENT_NAME}' уже существуют в ${CLIENT_DIR}"
fi
find_next_ipv4() {
local used host
used="$(
grep -oE "${VPN_SUBNET_PREFIX//./\\.}\.[0-9]+/32" "$WG_CONF" \
| sed 's#/32##' \
| awk -F. '{print $4}' \
| sort -n \
| uniq
)"
for host in $(seq "$START_HOST" "$END_HOST"); do
if ! grep -qx "$host" <<<"$used"; then
echo "${VPN_SUBNET_PREFIX}.${host}"
return 0
fi
done
return 1
}
CLIENT_IPV4="$(find_next_ipv4)" || die "Не удалось найти свободный IPv4 адрес"
CLIENT_HOST_OCTET="${CLIENT_IPV4##*.}"
if [[ "$USE_IPV6" == "1" ]]; then
CLIENT_IPV6="${IPV6_PREFIX}${CLIENT_HOST_OCTET}"
fi
echo "Создаю клиента: ${CLIENT_NAME}"
echo "IPv4 клиента: ${CLIENT_IPV4}"
if [[ "$USE_IPV6" == "1" ]]; then
echo "IPv6 клиента: ${CLIENT_IPV6}"
fi
wg genkey | tee "$CLIENT_PRIVKEY_FILE" | wg pubkey > "$CLIENT_PUBKEY_FILE"
wg genpsk > "$CLIENT_PSK_FILE"
chmod 600 "$CLIENT_PRIVKEY_FILE" "$CLIENT_PUBKEY_FILE" "$CLIENT_PSK_FILE"
CLIENT_PRIVATE_KEY="$(cat "$CLIENT_PRIVKEY_FILE")"
CLIENT_PUBLIC_KEY="$(cat "$CLIENT_PUBKEY_FILE")"
CLIENT_PRESHARED_KEY="$(cat "$CLIENT_PSK_FILE")"
if command -v wg >/dev/null 2>&1 && wg show "$WG_INTERFACE" >/dev/null 2>&1; then
SERVER_PUBLIC_KEY="$(wg show "$WG_INTERFACE" public-key)"
else
SERVER_PUBLIC_KEY="$(wg pubkey < <(grep '^PrivateKey *= *' "$WG_CONF" | head -n1 | awk -F'=' '{gsub(/ /,"",$2); print $2}'))"
fi
TMP_PEER_FILE="$(mktemp)"
trap 'rm -f "$TMP_PEER_FILE"' EXIT
{
echo
echo "### Client ${CLIENT_NAME}"
echo "[Peer]"
echo "PublicKey = ${CLIENT_PUBLIC_KEY}"
echo "PresharedKey = ${CLIENT_PRESHARED_KEY}"
if [[ "$USE_IPV6" == "1" ]]; then
echo "AllowedIPs = ${CLIENT_IPV4}/32,${CLIENT_IPV6}/128"
else
echo "AllowedIPs = ${CLIENT_IPV4}/32"
fi
} > "$TMP_PEER_FILE"
cat "$TMP_PEER_FILE" >> "$WG_CONF"
if [[ "$USE_IPV6" == "1" ]]; then
CLIENT_ADDRESS="${CLIENT_IPV4}/32,${CLIENT_IPV6}/128"
CLIENT_ALLOWED_IPS="0.0.0.0/0,::/0"
else
CLIENT_ADDRESS="${CLIENT_IPV4}/32"
CLIENT_ALLOWED_IPS="0.0.0.0/0"
fi
cat > "$CLIENT_CONF" <<EOF
[Interface]
PrivateKey = ${CLIENT_PRIVATE_KEY}
Address = ${CLIENT_ADDRESS}
DNS = ${SERVER_VPN_IP},192.168.0.200
[Peer]
PublicKey = ${SERVER_PUBLIC_KEY}
PresharedKey = ${CLIENT_PRESHARED_KEY}
Endpoint = ${ENDPOINT}:${SERVER_PORT}
AllowedIPs = ${CLIENT_ALLOWED_IPS}
PersistentKeepalive = 25
EOF
chmod 600 "$CLIENT_CONF"
echo "Применяю конфиг WireGuard..."
wg syncconf "$WG_INTERFACE" <(wg-quick strip "$WG_INTERFACE")
echo
echo "Готово."
echo "Клиент: ${CLIENT_NAME}"
echo "IP: ${CLIENT_IPV4}"
echo "Конфиг: ${CLIENT_CONF}"
echo
if command -v qrencode >/dev/null 2>&1; then
echo "QR-код для клиента:"
qrencode -t ansiutf8 < "$CLIENT_CONF"
echo
else
echo "Пакет qrencode не установлен, QR-код не напечатан."
echo "Установить можно так:"
echo " sudo apt update && sudo apt install -y qrencode"
echo
fi
echo "Проверка:"
echo " sudo wg show ${WG_INTERFACE}"
echo
echo "Передать клиенту файл:"
echo " ${CLIENT_CONF}"