diff --git a/README.md b/README.md index 51b4eb5..33d2cc3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Проект автоматизирует установку и настройку WireGuard-сервера и WireGuard-клиента на Debian/Ubuntu. -Основная идея: один репозиторий с понятными Bash-скриптами, которые можно повторно запускать без бессмысленной поломки уже существующей конфигурации. +Основная идея: один репозиторий с понятными Bash-скриптами для быстрого разворачивания и переустановки WireGuard-стека. ## Назначение проекта @@ -26,6 +26,7 @@ - `README.md` - `.gitignore` - `lib/common.sh` — общие функции +- `bootstrap/install_wg_install.sh` — установка короткой команды `wg-install` - `templates/wg0.conf.template` — шаблон базового `wg0.conf` - `server/install_server.sh` — установка сервера + GUI - `server/wg-peerctl.sh` — helper для регистрации peer на сервере @@ -76,15 +77,34 @@ sudo bash server/install_server.sh ``` +Важно: серверный установщик теперь всегда выполняет полный reset прошлого состояния (`/etc/wireguard` + `wireguard-ui` data/db) и поднимает всё заново. + ### Запуск сервера одной командой (без `git clone`) ```bash tmp="$(mktemp -d)" && curl -fL "https://git.ruslan.xyz/ruslan/Wireguard_server/archive/main.tar.gz" -o "$tmp/repo.tar.gz" && tar -xzf "$tmp/repo.tar.gz" -C "$tmp" && bash "$tmp/wireguard_server/server/install_server.sh" ``` -Если GUI уже ранее запускался и нужно переинициализировать его дефолты (подсеть, endpoint, пользователь/пароль), добавьте: +### Короткая команда `wg-install` (установить один раз) + +Из локального репозитория: ```bash ---gui-reset-db yes +sudo bash bootstrap/install_wg_install.sh +``` + +После этого запуск сервера: +```bash +sudo wg-install +``` + +С аргументами тоже работает: +```bash +sudo wg-install --non-interactive --server-public-ip 203.0.113.10 --default-iface eth0 +``` + +Установка `wg-install` без `git clone`: +```bash +tmp="$(mktemp -d)" && curl -fL "https://git.ruslan.xyz/ruslan/Wireguard_server/archive/main.tar.gz" -o "$tmp/repo.tar.gz" && tar -xzf "$tmp/repo.tar.gz" -C "$tmp" && sudo bash "$tmp/wireguard_server/bootstrap/install_wg_install.sh" ``` Скрипт интерактивно спросит недостающие данные: @@ -162,7 +182,7 @@ sudo bash client/install_client.sh \ - Сервер: `/etc/wireguard/server_private.key` и `/etc/wireguard/server_public.key`. - Клиент: `/etc/wireguard/wg0_client_private.key`, `..._public.key`, `..._psk.key`. -- Повторный запуск не перегенерирует ключи без необходимости. +- При каждом запуске серверного установщика серверные ключи создаются заново (так как выполняется полный reset). ## Как задаются маршруты @@ -292,9 +312,7 @@ docker ps -aq --filter 'label=com.docker.compose.service=wireguard-ui' | xargs - docker ps -a --format '{{.Names}}' | grep -E '(^|[_-])wireguard-ui($|[_-])' | xargs -r docker rm -f ``` -Если клиенты из GUI создаются в неправильной подсети (например `10.252.1.x` вместо вашей `10.66.66.x`), запустите установщик с `--gui-reset-db yes`, затем создайте клиента заново и пересканируйте QR. - -Почему так бывает: `wireguard-ui` хранит глобальные настройки в своей БД. Если БД была создана ранее со старой подсетью, новые переменные окружения не всегда перезаписывают эти значения автоматически. +Если клиенты из GUI создаются в неправильной подсети, просто перезапустите серверный установщик: теперь он автоматически очищает БД GUI и поднимает всё с нуля. ## Важные пути @@ -310,4 +328,5 @@ docker ps -a --format '{{.Names}}' | grep -E '(^|[_-])wireguard-ui($|[_-])' | xa ```bash bash server/install_server.sh --help bash client/install_client.sh --help +bash bootstrap/install_wg_install.sh ``` diff --git a/bootstrap/install_wg_install.sh b/bootstrap/install_wg_install.sh new file mode 100755 index 0000000..50c77ba --- /dev/null +++ b/bootstrap/install_wg_install.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ "${EUID}" -ne 0 ]]; then + echo "Этот скрипт нужно запускать от root (через sudo)." >&2 + exit 1 +fi + +ARCHIVE_URL="${ARCHIVE_URL:-https://git.ruslan.xyz/ruslan/Wireguard_server/archive/main.tar.gz}" +INSTALL_PATH="${INSTALL_PATH:-/usr/local/bin/wg-install}" + +cat > "${INSTALL_PATH}" < Порт GUI (по умолчанию: 5000) --gui-user Логин GUI (по умолчанию: admin) --gui-password Пароль GUI (если не указан, будет запрос) - --gui-reset-db Сбросить БД GUI, чтобы применить новые дефолты (по умолчанию: no) + --gui-reset-db Устарело: теперь reset GUI выполняется автоматически -h, --help Показать помощь USAGE @@ -104,6 +105,7 @@ parse_args() { validate_inputs() { is_valid_port "$WG_PORT" || die "Некорректный порт WireGuard: $WG_PORT" is_valid_port "$GUI_PORT" || die "Некорректный порт GUI: $GUI_PORT" + [[ "$GUI_ENABLE" == "yes" || "$GUI_ENABLE" == "no" ]] || die "--gui-enable должен быть yes или no" if [[ ! "$WG_NETWORK" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[12][0-9]|3[0-2])$ ]]; then die "Некорректная сеть WG: $WG_NETWORK" @@ -114,6 +116,35 @@ validate_inputs() { fi } +reset_existing_install() { + log_warn "Выполняю полный reset предыдущей инсталляции WireGuard/GUI" + + if [[ -d /etc/wireguard ]]; then + local conf iface + shopt -s nullglob + for conf in /etc/wireguard/*.conf; do + iface="$(basename "$conf" .conf)" + systemctl disable --now "wg-quick@${iface}.service" >/dev/null 2>&1 || true + wg-quick down "$iface" >/dev/null 2>&1 || true + done + shopt -u nullglob + + rm -f /etc/wireguard/*.conf /etc/wireguard/*.key /etc/wireguard/wg-meta.env + log_info "Очищены конфиги/ключи WireGuard в /etc/wireguard" + fi + + if command -v docker >/dev/null 2>&1; then + if [[ -f /opt/wireguard-ui/docker-compose.yml ]]; then + (cd /opt/wireguard-ui && docker compose down --remove-orphans >/dev/null 2>&1) || true + (cd /opt/wireguard-ui && docker-compose down --remove-orphans >/dev/null 2>&1) || true + fi + docker rm -f wireguard-ui >/dev/null 2>&1 || true + fi + + rm -rf /opt/wireguard-ui/db/* /opt/wireguard-ui/data/* /opt/wireguard-ui/docker-compose.yml + log_info "Очищено состояние GUI в /opt/wireguard-ui" +} + collect_inputs() { if [[ -z "$DEFAULT_IFACE" ]]; then DEFAULT_IFACE="$(detect_default_iface || true)" @@ -160,6 +191,9 @@ collect_inputs() { fi GUI_SESSION_SECRET="$(random_alnum 32)" [[ "$GUI_RESET_DB" == "yes" || "$GUI_RESET_DB" == "no" ]] || die "--gui-reset-db должен быть yes или no" + if [[ "$GUI_RESET_DB" == "yes" ]]; then + log_warn "--gui-reset-db устарел: очистка GUI теперь выполняется автоматически на каждом запуске." + fi fi validate_inputs @@ -213,11 +247,6 @@ setup_keys() { setup_wg_config() { local conf="/etc/wireguard/${WG_INTERFACE}.conf" - if [[ -f "$conf" ]]; then - log_warn "Конфиг ${conf} уже существует. Автосоздание пропущено." - return - fi - local server_priv server_priv="$(cat /etc/wireguard/server_private.key)" @@ -284,26 +313,6 @@ setup_gui() { mkdir -p /opt/wireguard-ui/{db,data} safe_chmod_700 /opt/wireguard-ui - if [[ "$GUI_RESET_DB" == "yes" ]]; then - rm -rf /opt/wireguard-ui/db/* - log_warn "БД GUI очищена. Дефолтные настройки GUI будут инициализированы заново." - elif [[ -n "$(find /opt/wireguard-ui/db -mindepth 1 -maxdepth 1 2>/dev/null)" ]]; then - log_warn "Обнаружена существующая БД GUI. Дефолты (подсеть/endpoint/пароль) могли сохраниться старыми." - log_warn "Если нужен чистый старт GUI, перезапустите с --gui-reset-db yes" - - # Частый кейс миграции: старая БД wireguard-ui хранит дефолт 10.252.1.0/24. - # В интерактивном режиме даем быстрый вариант синхронизации с текущей подсетью WG. - if (( ! NON_INTERACTIVE )); then - if grep -aEq '10\\.252\\.1\\.' /opt/wireguard-ui/db/* 2>/dev/null; then - log_warn "В БД GUI обнаружены старые значения подсети (10.252.1.x)." - if confirm "Сбросить БД GUI сейчас, чтобы синхронизировать подсеть с ${WG_NETWORK}?"; then - rm -rf /opt/wireguard-ui/db/* - log_warn "БД GUI очищена по подтверждению пользователя." - fi - fi - fi - fi - cat > /opt/wireguard-ui/docker-compose.yml <> 24) & 255 )) \ + $(( (n >> 16) & 255 )) \ + $(( (n >> 8) & 255 )) \ + $(( n & 255 )) +} + +cidr_bounds() { + local cidr="$1" + local ip prefix + IFS='/' read -r ip prefix <<< "$cidr" + [[ -n "$ip" && -n "$prefix" ]] || return 1 + + local ip_int mask net broadcast + ip_int="$(ip_to_int "$ip")" + if ((prefix == 0)); then + mask=0 + else + mask=$(( (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF )) + fi + net=$(( ip_int & mask )) + broadcast=$(( net | ((~mask) & 0xFFFFFFFF) )) + echo "${net} ${broadcast}" +} + next_client_ip() { local network="$1" - local base - base="${network%.*}" + local net_start net_end + read -r net_start net_end < <(cidr_bounds "$network") || return 1 + ((net_end - net_start >= 3)) || return 1 + + local server_ip server_ip_int + server_ip="${WG_ADDRESS%%/*}" + server_ip_int="$(ip_to_int "$server_ip")" + + local first_host last_host + first_host=$((net_start + 1)) + last_host=$((net_end - 1)) local used - used="$(grep -E '^AllowedIPs\s*=\s*' "$WG_CONF" | awk -F'=' '{print $2}' | tr ',' '\n' | sed 's/ //g' | grep -E '^10\.[0-9]+\.[0-9]+\.[0-9]+/32$' | sed 's#/32##' || true)" + used="$(grep -E '^AllowedIPs\s*=\s*' "$WG_CONF" | awk -F'=' '{print $2}' | tr ',' '\n' | sed 's/ //g' | grep -E '^([0-9]{1,3}\.){3}[0-9]{1,3}/32$' | sed 's#/32##' || true)" - local i candidate - for i in $(seq 2 254); do - candidate="${base}.${i}" - if ! grep -qx "$candidate" <<< "$used"; then - echo "${candidate}/32" + local candidate_int candidate_ip + for ((candidate_int = first_host; candidate_int <= last_host; candidate_int++)); do + ((candidate_int == server_ip_int)) && continue + candidate_ip="$(int_to_ip "$candidate_int")" + if ! grep -qx "$candidate_ip" <<< "$used"; then + echo "${candidate_ip}/32" return 0 fi done