WireGuard: add clean reinstall flow and bootstrap wg-install

This commit is contained in:
Ruslan
2026-04-14 10:04:25 +03:00
parent cbc2f5bf45
commit 278b403e09
5 changed files with 141 additions and 43 deletions

View File

@@ -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

View File

@@ -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