Server: replace wireguard-ui with built-in wg-admin-gui + PostgreSQL

This commit is contained in:
Ruslan
2026-04-14 11:43:07 +03:00
parent e2d3993fb3
commit ae3da04d4a
11 changed files with 550 additions and 121 deletions

View File

@@ -23,12 +23,12 @@ GUI_PORT="5000"
GUI_USER="admin"
GUI_PASSWORD=""
GUI_PASSWORD_GENERATED=0
GUI_SESSION_SECRET=""
GUI_RESET_DB="no"
GUI_DB_PASSWORD=""
usage() {
cat <<'USAGE'
Установка WireGuard-сервера и GUI (Debian/Ubuntu).
Установка WireGuard-сервера и встроенного WG Admin GUI (Debian/Ubuntu).
Каждый запуск выполняет полный reset прошлой инсталляции и поднимает все с нуля.
Использование:
@@ -44,12 +44,12 @@ usage() {
--server-dns <ip> DNS для клиентов (по умолчанию: 1.1.1.1)
--default-iface <iface> Внешний интерфейс для NAT
--gui-enable <yes|no> Включить GUI wireguard-ui (по умолчанию: yes)
--gui-enable <yes|no> Включить WG Admin GUI (по умолчанию: yes)
--gui-host <host> Домен/IP для открытия GUI
--gui-port <port> Порт GUI (по умолчанию: 5000)
--gui-user <user> Логин GUI (по умолчанию: admin)
--gui-password <pass> Пароль GUI (если не указан, будет запрос)
--gui-reset-db <yes|no> Устарело: теперь reset GUI выполняется автоматически
--gui-password <pass> Пароль GUI (если не указан, будет сгенерирован)
--gui-reset-db <yes|no> Устарело: reset БД теперь выполняется автоматически
-h, --help Показать помощь
USAGE
@@ -116,6 +116,16 @@ validate_inputs() {
fi
}
detect_compose_cmd() {
if docker compose version >/dev/null 2>&1; then
COMPOSE_CMD=(docker compose)
elif command -v docker-compose >/dev/null 2>&1; then
COMPOSE_CMD=(docker-compose)
else
die "Не найден docker compose. Установите docker-compose-plugin или docker-compose."
fi
}
reset_existing_install() {
log_warn "Выполняю полный reset предыдущей инсталляции WireGuard/GUI"
@@ -135,16 +145,25 @@ reset_existing_install() {
log_info "Очищены конфиги/ключи WireGuard в /etc/wireguard"
fi
systemctl disable --now wg-admin-gui.service >/dev/null 2>&1 || true
rm -f /etc/systemd/system/wg-admin-gui.service
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
if [[ -f /opt/wg-admin-gui/docker-compose.yml ]]; then
detect_compose_cmd
(cd /opt/wg-admin-gui && "${COMPOSE_CMD[@]}" down --remove-orphans >/dev/null 2>&1) || true
fi
docker rm -f wireguard-ui >/dev/null 2>&1 || true
if [[ -f /opt/wireguard-ui/docker-compose.yml ]]; then
detect_compose_cmd
(cd /opt/wireguard-ui && "${COMPOSE_CMD[@]}" down --remove-orphans >/dev/null 2>&1) || true
fi
docker rm -f wireguard-ui wg-admin-postgres >/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"
rm -rf /opt/wireguard-ui /opt/wg-admin-gui
log_info "Очищено состояние GUI"
}
collect_inputs() {
@@ -174,9 +193,9 @@ collect_inputs() {
if [[ "$GUI_ENABLE" == "yes" ]]; then
if [[ -z "$GUI_PASSWORD" ]]; then
GUI_PASSWORD="$(random_alnum 8)"
GUI_PASSWORD="$(random_alnum 10)"
GUI_PASSWORD_GENERATED=1
log_warn "Пароль GUI не задан. Сгенерирован пароль (8 символов): ${GUI_PASSWORD}"
log_warn "Пароль GUI не задан. Сгенерирован пароль: ${GUI_PASSWORD}"
if (( ! NON_INTERACTIVE )); then
local replace_or_password=""
@@ -187,8 +206,6 @@ collect_inputs() {
if [[ -n "$custom_gui_password" ]]; then
GUI_PASSWORD="$custom_gui_password"
GUI_PASSWORD_GENERATED=0
else
log_warn "Пустой пароль не принят. Остается сгенерированный пароль."
fi
elif [[ -n "$replace_or_password" && ! "$replace_or_password" =~ ^([nN][oO]?|[nN])$ ]]; then
GUI_PASSWORD="$replace_or_password"
@@ -196,11 +213,9 @@ collect_inputs() {
fi
fi
fi
GUI_SESSION_SECRET="$(random_alnum 32)"
GUI_DB_PASSWORD="$(random_alnum 24)"
[[ "$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
@@ -209,7 +224,7 @@ collect_inputs() {
install_packages() {
apt_install_if_missing \
wireguard wireguard-tools iproute2 iptables curl ca-certificates openssl \
qrencode docker.io
docker.io python3 python3-venv python3-pip
if apt-cache show docker-compose-plugin >/dev/null 2>&1; then
apt_install_if_missing docker-compose-plugin
@@ -237,19 +252,12 @@ setup_keys() {
local priv="/etc/wireguard/server_private.key"
local pub="/etc/wireguard/server_public.key"
if [[ ! -f "$priv" ]]; then
umask 077
wg genkey | tee "$priv" | wg pubkey > "$pub"
log_success "Сгенерированы ключи сервера"
else
if [[ ! -f "$pub" ]]; then
wg pubkey < "$priv" > "$pub"
fi
log_info "Ключи сервера уже существуют, переиспользую"
fi
umask 077
wg genkey | tee "$priv" | wg pubkey > "$pub"
safe_chmod_600 "$priv"
safe_chmod_600 "$pub"
log_success "Сгенерированы ключи сервера"
}
setup_wg_config() {
@@ -363,75 +371,69 @@ EOF_SYNC_PATH
setup_gui() {
[[ "$GUI_ENABLE" == "yes" ]] || { log_warn "GUI отключен (GUI_ENABLE=no)"; return; }
mkdir -p /opt/wireguard-ui/{db,data}
safe_chmod_700 /opt/wireguard-ui
mkdir -p /opt/wg-admin-gui/{app,pgdata}
safe_chmod_700 /opt/wg-admin-gui
cat > /opt/wireguard-ui/docker-compose.yml <<EOF_COMPOSE
cp -a "${PROJECT_ROOT}/gui/." /opt/wg-admin-gui/app/
cat > /opt/wg-admin-gui/docker-compose.yml <<EOF_COMPOSE
services:
wireguard-ui:
image: ngoduykhanh/wireguard-ui:latest
container_name: wireguard-ui
postgres:
image: postgres:16-alpine
container_name: wg-admin-postgres
restart: unless-stopped
ports:
- "${GUI_PORT}:5000"
environment:
- WGUI_USERNAME=${GUI_USER}
- WGUI_PASSWORD=${GUI_PASSWORD}
- SESSION_SECRET=${GUI_SESSION_SECRET}
- WGUI_ENDPOINT_ADDRESS=${SERVER_PUBLIC_IP}:${WG_PORT}
- WGUI_DNS=${SERVER_DNS}
- WGUI_CONFIG_FILE_PATH=/etc/wireguard/${WG_INTERFACE}.conf
- WGUI_SERVER_INTERFACE_ADDRESSES=${WG_ADDRESS}
- WGUI_SERVER_LISTEN_PORT=${WG_PORT}
- WGUI_DEFAULT_CLIENT_ALLOWED_IPS=0.0.0.0/0
- WGUI_MANAGE_START=true
- WGUI_MANAGE_RESTART=true
- POSTGRES_DB=wgadmin
- POSTGRES_USER=wgadmin
- POSTGRES_PASSWORD=${GUI_DB_PASSWORD}
ports:
- "127.0.0.1:5432:5432"
volumes:
- /etc/wireguard:/etc/wireguard
- /opt/wireguard-ui/db:/app/db
- /opt/wireguard-ui/data:/app/data
cap_add:
- NET_ADMIN
- /opt/wg-admin-gui/pgdata:/var/lib/postgresql/data
EOF_COMPOSE
systemd_enable_now docker.service
detect_compose_cmd
(cd /opt/wg-admin-gui && "${COMPOSE_CMD[@]}" up -d --remove-orphans)
local compose_cmd=()
local compose_mode=""
if docker compose version >/dev/null 2>&1; then
compose_cmd=(docker compose)
compose_mode="plugin"
elif command -v docker-compose >/dev/null 2>&1; then
compose_cmd=(docker-compose)
compose_mode="legacy"
else
die "Не найден docker compose. Установите docker-compose-plugin или docker-compose."
fi
python3 -m venv /opt/wg-admin-gui/venv
/opt/wg-admin-gui/venv/bin/pip install --upgrade pip >/dev/null
/opt/wg-admin-gui/venv/bin/pip install -r /opt/wg-admin-gui/app/requirements.txt >/dev/null
# На некоторых системах с legacy docker-compose (v1) при recreate может возникать
# KeyError: 'ContainerConfig'. Предварительно удаляем старый контейнер по имени.
if [[ "$compose_mode" == "legacy" ]]; then
docker rm -f wireguard-ui >/dev/null 2>&1 || true
cat > /opt/wg-admin-gui/wg-admin-gui.env <<EOF_ENV
DB_DSN=postgresql://wgadmin:${GUI_DB_PASSWORD}@127.0.0.1:5432/wgadmin
WG_INTERFACE=${WG_INTERFACE}
WG_META_FILE=/etc/wireguard/wg-meta.env
ADMIN_USER=${GUI_USER}
ADMIN_PASSWORD=${GUI_PASSWORD}
APP_SECRET=$(random_alnum 32)
APP_PORT=${GUI_PORT}
EOF_ENV
chmod 600 /opt/wg-admin-gui/wg-admin-gui.env
# Удаляем возможные старые контейнеры вида <project>_wireguard-ui
# и контейнеры сервиса wireguard-ui по compose-label.
local legacy_ids legacy_names
legacy_ids="$(docker ps -aq --filter 'label=com.docker.compose.service=wireguard-ui' || true)"
if [[ -n "$legacy_ids" ]]; then
docker rm -f $legacy_ids >/dev/null 2>&1 || true
fi
cat > /etc/systemd/system/wg-admin-gui.service <<EOF_SERVICE
[Unit]
Description=WG Admin GUI
After=network-online.target docker.service
Wants=network-online.target
legacy_names="$(docker ps -a --format '{{.Names}}' | grep -E '(^|[_-])wireguard-ui($|[_-])' || true)"
if [[ -n "$legacy_names" ]]; then
while IFS= read -r cname; do
[[ -n "$cname" ]] || continue
docker rm -f "$cname" >/dev/null 2>&1 || true
done <<< "$legacy_names"
fi
fi
[Service]
Type=simple
WorkingDirectory=/opt/wg-admin-gui/app
EnvironmentFile=/opt/wg-admin-gui/wg-admin-gui.env
ExecStart=/opt/wg-admin-gui/venv/bin/gunicorn -w 2 -b 0.0.0.0:${GUI_PORT} app:app
Restart=always
RestartSec=2
User=root
(cd /opt/wireguard-ui && "${compose_cmd[@]}" up -d --remove-orphans)
log_success "GUI wireguard-ui запущен"
[Install]
WantedBy=multi-user.target
EOF_SERVICE
systemctl daemon-reload
systemctl enable --now wg-admin-gui.service
log_success "WG Admin GUI запущен"
}
print_summary() {
@@ -440,8 +442,7 @@ print_summary() {
gui_status="disabled"
if [[ "$GUI_ENABLE" == "yes" ]]; then
gui_status="$(docker ps --filter name=wireguard-ui --format '{{.Status}}' || true)"
[[ -n "$gui_status" ]] || gui_status="not running"
gui_status="$(systemctl is-active wg-admin-gui.service 2>/dev/null || true)"
fi
cat <<EOF_SUMMARY
@@ -466,10 +467,6 @@ Auto-apply GUI->WG: enabled (wg-syncconf@${WG_INTERFACE}.path)
Лог установки: ${LOG_FILE}
=================================================
EOF_SUMMARY
if [[ "$GUI_ENABLE" == "yes" ]]; then
echo "Ссылка для входа в GUI: http://${GUI_HOST}:${GUI_PORT}"
fi
}
main() {
@@ -480,6 +477,7 @@ main() {
require_cmd ip
require_cmd awk
require_cmd sed
require_cmd python3
collect_inputs