WireGuard: add clean reinstall flow and bootstrap wg-install
This commit is contained in:
33
README.md
33
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
|
||||
```
|
||||
|
||||
24
bootstrap/install_wg_install.sh
Executable file
24
bootstrap/install_wg_install.sh
Executable file
@@ -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}" <<EOF
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
tmp="\$(mktemp -d)"
|
||||
trap 'rm -rf "\$tmp"' EXIT
|
||||
curl -fL "${ARCHIVE_URL}" -o "\$tmp/repo.tar.gz"
|
||||
tar -xzf "\$tmp/repo.tar.gz" -C "\$tmp"
|
||||
exec bash "\$tmp/wireguard_server/server/install_server.sh" "\$@"
|
||||
EOF
|
||||
|
||||
chmod 755 "${INSTALL_PATH}"
|
||||
echo "Установлено: ${INSTALL_PATH}"
|
||||
echo "Использование: sudo wg-install [опции]"
|
||||
@@ -92,6 +92,7 @@ parse_args() {
|
||||
validate_inputs() {
|
||||
is_valid_port "$SSH_PORT" || die "Некорректный SSH-порт: $SSH_PORT"
|
||||
[[ "$SSH_AUTH_METHOD" == "key" || "$SSH_AUTH_METHOD" == "password" ]] || die "--ssh-auth должен быть key или password"
|
||||
[[ "$TUNNEL_MODE" == "full" || "$TUNNEL_MODE" == "split" ]] || die "--mode должен быть full или split"
|
||||
|
||||
if [[ -z "$SERVER_HOST" ]]; then
|
||||
die "Не указан --server-host"
|
||||
|
||||
@@ -29,6 +29,7 @@ GUI_RESET_DB="no"
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Установка WireGuard-сервера и GUI (Debian/Ubuntu).
|
||||
Каждый запуск выполняет полный reset прошлой инсталляции и поднимает все с нуля.
|
||||
|
||||
Использование:
|
||||
install_server.sh [опции]
|
||||
@@ -48,7 +49,7 @@ usage() {
|
||||
--gui-port <port> Порт GUI (по умолчанию: 5000)
|
||||
--gui-user <user> Логин GUI (по умолчанию: admin)
|
||||
--gui-password <pass> Пароль GUI (если не указан, будет запрос)
|
||||
--gui-reset-db <yes|no> Сбросить БД GUI, чтобы применить новые дефолты (по умолчанию: no)
|
||||
--gui-reset-db <yes|no> Устарело: теперь 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 <<EOF_COMPOSE
|
||||
services:
|
||||
wireguard-ui:
|
||||
@@ -398,7 +407,6 @@ GUI адрес: http://${GUI_HOST}:${GUI_PORT}
|
||||
GUI логин: ${GUI_USER}
|
||||
GUI статус: ${gui_status}
|
||||
$(if [[ "$GUI_ENABLE" == "yes" && "$GUI_PASSWORD_GENERATED" -eq 1 ]]; then echo "GUI пароль: ${GUI_PASSWORD} (сгенерирован, рекомендуется заменить)"; fi)
|
||||
GUI reset db: ${GUI_RESET_DB}
|
||||
|
||||
Helper для peer: /usr/local/sbin/wg-peerctl
|
||||
Лог установки: ${LOG_FILE}
|
||||
@@ -421,7 +429,8 @@ main() {
|
||||
|
||||
collect_inputs
|
||||
|
||||
log_info "Начинаю установку WireGuard-сервера"
|
||||
log_info "Начинаю установку WireGuard-сервера (чистый запуск)"
|
||||
reset_existing_install
|
||||
install_packages
|
||||
setup_sysctl
|
||||
setup_keys
|
||||
|
||||
@@ -39,25 +39,70 @@ load_meta() {
|
||||
|
||||
WG_INTERFACE="${WG_INTERFACE:-wg0}"
|
||||
WG_NETWORK="${WG_NETWORK:-10.66.66.0/24}"
|
||||
WG_ADDRESS="${WG_ADDRESS:-10.66.66.1/24}"
|
||||
WG_PORT="${WG_PORT:-51820}"
|
||||
SERVER_PUBLIC_IP="${SERVER_PUBLIC_IP:-}"
|
||||
SERVER_DNS="${SERVER_DNS:-1.1.1.1}"
|
||||
WG_CONF="/etc/wireguard/${WG_INTERFACE}.conf"
|
||||
}
|
||||
|
||||
ip_to_int() {
|
||||
local ip="$1"
|
||||
local o1 o2 o3 o4
|
||||
IFS='.' read -r o1 o2 o3 o4 <<< "$ip"
|
||||
echo $(( (o1 << 24) + (o2 << 16) + (o3 << 8) + o4 ))
|
||||
}
|
||||
|
||||
int_to_ip() {
|
||||
local n="$1"
|
||||
printf '%d.%d.%d.%d' \
|
||||
$(( (n >> 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
|
||||
|
||||
Reference in New Issue
Block a user