201 lines
5.2 KiB
Bash
Executable File
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}"
|