Files
Wireguard_server/server/install_server.sh

383 lines
12 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
# shellcheck source=../lib/common.sh
source "${PROJECT_ROOT}/lib/common.sh"
LOG_FILE="/var/log/wireguard-server-install.log"
NON_INTERACTIVE=0
WG_INTERFACE="wg0"
WG_PORT="51820"
WG_NETWORK="10.66.66.0/24"
WG_ADDRESS="10.66.66.1/24"
SERVER_PUBLIC_IP=""
SERVER_DNS="1.1.1.1"
DEFAULT_IFACE=""
GUI_ENABLE="yes"
GUI_HOST=""
GUI_PORT="5000"
GUI_USER="admin"
GUI_PASSWORD=""
GUI_PASSWORD_GENERATED=0
GUI_SESSION_SECRET=""
usage() {
cat <<'USAGE'
Установка WireGuard-сервера и GUI (Debian/Ubuntu).
Использование:
install_server.sh [опции]
Опции:
--non-interactive Режим без вопросов (используются аргументы/дефолты)
--wg-interface <name> Имя интерфейса WireGuard (по умолчанию: wg0)
--wg-port <port> Порт WireGuard (по умолчанию: 51820)
--wg-network <cidr> Подсеть VPN (по умолчанию: 10.66.66.0/24)
--wg-address <cidr> Адрес сервера в VPN (по умолчанию: 10.66.66.1/24)
--server-public-ip <ip> Публичный IP сервера
--server-dns <ip> DNS для клиентов (по умолчанию: 1.1.1.1)
--default-iface <iface> Внешний интерфейс для NAT
--gui-enable <yes|no> Включить GUI wireguard-ui (по умолчанию: yes)
--gui-host <host> Домен/IP для открытия GUI
--gui-port <port> Порт GUI (по умолчанию: 5000)
--gui-user <user> Логин GUI (по умолчанию: admin)
--gui-password <pass> Пароль GUI (если не указан, будет запрос)
-h, --help Показать помощь
USAGE
}
on_error() {
local code=$?
log_error "Установка прервана с ошибкой (код: ${code}). Подробности в ${LOG_FILE}"
exit "$code"
}
trap on_error ERR
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--non-interactive)
NON_INTERACTIVE=1; shift ;;
--wg-interface)
WG_INTERFACE="$2"; shift 2 ;;
--wg-port)
WG_PORT="$2"; shift 2 ;;
--wg-network)
WG_NETWORK="$2"; shift 2 ;;
--wg-address)
WG_ADDRESS="$2"; shift 2 ;;
--server-public-ip)
SERVER_PUBLIC_IP="$2"; shift 2 ;;
--server-dns)
SERVER_DNS="$2"; shift 2 ;;
--default-iface)
DEFAULT_IFACE="$2"; shift 2 ;;
--gui-enable)
GUI_ENABLE="$2"; shift 2 ;;
--gui-host)
GUI_HOST="$2"; shift 2 ;;
--gui-port)
GUI_PORT="$2"; shift 2 ;;
--gui-user)
GUI_USER="$2"; shift 2 ;;
--gui-password)
GUI_PASSWORD="$2"; shift 2 ;;
-h|--help)
usage; exit 0 ;;
*)
die "Неизвестный аргумент: $1"
;;
esac
done
}
validate_inputs() {
is_valid_port "$WG_PORT" || die "Некорректный порт WireGuard: $WG_PORT"
is_valid_port "$GUI_PORT" || die "Некорректный порт GUI: $GUI_PORT"
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"
fi
if [[ ! "$WG_ADDRESS" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[12][0-9]|3[0-2])$ ]]; then
die "Некорректный адрес WG сервера: $WG_ADDRESS"
fi
}
collect_inputs() {
if [[ -z "$DEFAULT_IFACE" ]]; then
DEFAULT_IFACE="$(detect_default_iface || true)"
fi
if [[ -z "$DEFAULT_IFACE" ]]; then
if ((NON_INTERACTIVE)); then
die "Не удалось определить внешний интерфейс. Передайте --default-iface"
fi
read -r -p "Введите внешний интерфейс (например eth0): " DEFAULT_IFACE
fi
if [[ -z "$SERVER_PUBLIC_IP" ]]; then
SERVER_PUBLIC_IP="$(detect_public_ip || true)"
fi
if [[ -z "$SERVER_PUBLIC_IP" ]]; then
if ((NON_INTERACTIVE)); then
die "Не удалось определить публичный IP. Передайте --server-public-ip"
fi
read -r -p "Введите публичный IP сервера: " SERVER_PUBLIC_IP
fi
if [[ -z "$GUI_HOST" ]]; then
GUI_HOST="$SERVER_PUBLIC_IP"
fi
if [[ "$GUI_ENABLE" == "yes" ]]; then
if [[ -z "$GUI_PASSWORD" ]]; then
GUI_PASSWORD="$(random_alnum 8)"
GUI_PASSWORD_GENERATED=1
log_warn "Пароль GUI не задан. Сгенерирован пароль (8 символов): ${GUI_PASSWORD}"
if (( ! NON_INTERACTIVE )); then
if confirm "Хотите заменить сгенерированный пароль GUI?"; then
local custom_gui_password=""
ask_secret "Введите новый пароль GUI (${GUI_USER})" custom_gui_password
if [[ -n "$custom_gui_password" ]]; then
GUI_PASSWORD="$custom_gui_password"
GUI_PASSWORD_GENERATED=0
else
log_warn "Пустой пароль не принят. Остается сгенерированный пароль."
fi
fi
fi
fi
GUI_SESSION_SECRET="$(random_alnum 32)"
fi
validate_inputs
}
install_packages() {
apt_install_if_missing \
wireguard wireguard-tools iproute2 iptables curl ca-certificates openssl \
qrencode docker.io
if apt-cache show docker-compose-plugin >/dev/null 2>&1; then
apt_install_if_missing docker-compose-plugin
elif apt-cache show docker-compose >/dev/null 2>&1; then
apt_install_if_missing docker-compose
else
log_warn "Не найден пакет docker-compose-plugin/docker-compose. Проверьте репозитории APT."
fi
}
setup_sysctl() {
local f="/etc/sysctl.d/99-wireguard-forwarding.conf"
cat > "$f" <<EOF_SYSCTL
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
EOF_SYSCTL
sysctl --system >/dev/null
log_success "IP forwarding включен"
}
setup_keys() {
mkdir -p /etc/wireguard
chmod 700 /etc/wireguard
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
safe_chmod_600 "$priv"
safe_chmod_600 "$pub"
}
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)"
sed -e "s|__WG_ADDRESS__|${WG_ADDRESS}|g" \
-e "s|__WG_PORT__|${WG_PORT}|g" \
-e "s|__SERVER_PRIVATE_KEY__|${server_priv}|g" \
-e "s|__WG_INTERFACE__|${WG_INTERFACE}|g" \
-e "s|__WG_NETWORK__|${WG_NETWORK}|g" \
-e "s|__DEFAULT_IFACE__|${DEFAULT_IFACE}|g" \
"${PROJECT_ROOT}/templates/wg0.conf.template" > "$conf"
safe_chmod_600 "$conf"
log_success "Создан базовый конфиг: $conf"
}
setup_meta() {
local meta="/etc/wireguard/wg-meta.env"
touch "$meta"
safe_chmod_600 "$meta"
set_kv_in_file "WG_INTERFACE" "$WG_INTERFACE" "$meta"
set_kv_in_file "WG_PORT" "$WG_PORT" "$meta"
set_kv_in_file "WG_NETWORK" "$WG_NETWORK" "$meta"
set_kv_in_file "WG_ADDRESS" "$WG_ADDRESS" "$meta"
set_kv_in_file "SERVER_PUBLIC_IP" "$SERVER_PUBLIC_IP" "$meta"
set_kv_in_file "SERVER_DNS" "$SERVER_DNS" "$meta"
set_kv_in_file "DEFAULT_IFACE" "$DEFAULT_IFACE" "$meta"
}
setup_wg_service() {
systemd_enable_now "wg-quick@${WG_INTERFACE}.service"
log_success "WireGuard сервис запущен и включен в автозапуск"
}
setup_ufw_if_active() {
if command -v ufw >/dev/null 2>&1; then
if ufw status 2>/dev/null | grep -q "Status: active"; then
ufw allow "${WG_PORT}/udp" || true
if [[ "$GUI_ENABLE" == "yes" ]]; then
ufw allow "${GUI_PORT}/tcp" || true
fi
log_info "UFW активен: правила для WireGuard/GUI добавлены"
fi
fi
}
install_peer_helper() {
mkdir -p /usr/local/lib/wireguard-automation/{lib,server}
install -m 640 "${PROJECT_ROOT}/lib/common.sh" /usr/local/lib/wireguard-automation/lib/common.sh
install -m 750 "${PROJECT_ROOT}/server/wg-peerctl.sh" /usr/local/lib/wireguard-automation/server/wg-peerctl.sh
cat > /usr/local/sbin/wg-peerctl <<'EOF_HELPER'
#!/usr/bin/env bash
exec /usr/local/lib/wireguard-automation/server/wg-peerctl.sh "$@"
EOF_HELPER
chmod 750 /usr/local/sbin/wg-peerctl
log_success "Установлен helper: /usr/local/sbin/wg-peerctl"
}
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
cat > /opt/wireguard-ui/docker-compose.yml <<EOF_COMPOSE
services:
wireguard-ui:
image: ngoduykhanh/wireguard-ui:latest
container_name: wireguard-ui
restart: unless-stopped
ports:
- "${GUI_PORT}:5000"
environment:
- WGUI_USERNAME=${GUI_USER}
- WGUI_PASSWORD=${GUI_PASSWORD}
- SESSION_SECRET=${GUI_SESSION_SECRET}
- WGUI_MANAGE_START=true
- WGUI_MANAGE_RESTART=true
volumes:
- /etc/wireguard:/etc/wireguard
- /opt/wireguard-ui/db:/app/db
- /opt/wireguard-ui/data:/app/data
cap_add:
- NET_ADMIN
EOF_COMPOSE
systemd_enable_now docker.service
local 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
(cd /opt/wireguard-ui && "${compose_cmd[@]}" up -d)
log_success "GUI wireguard-ui запущен"
}
print_summary() {
local service_status gui_status
service_status="$(systemctl is-active "wg-quick@${WG_INTERFACE}" 2>/dev/null || true)"
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"
fi
cat <<EOF_SUMMARY
================ ИТОГОВАЯ СВОДКА ================
WireGuard сервис: ${service_status}
Интерфейс: ${WG_INTERFACE}
Порт WireGuard: ${WG_PORT}/udp
Конфиг сервера: /etc/wireguard/${WG_INTERFACE}.conf
Ключи сервера: /etc/wireguard/server_private.key, /etc/wireguard/server_public.key
Подсеть VPN: ${WG_NETWORK}
Маршрутизация NAT: через интерфейс ${DEFAULT_IFACE}
GUI: ${GUI_ENABLE}
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)
Helper для peer: /usr/local/sbin/wg-peerctl
Лог установки: ${LOG_FILE}
=================================================
EOF_SUMMARY
if [[ "$GUI_ENABLE" == "yes" ]]; then
echo "Ссылка для входа в GUI: http://${GUI_HOST}:${GUI_PORT}"
fi
}
main() {
parse_args "$@"
require_root
check_os_supported
require_cmd ip
require_cmd awk
require_cmd sed
collect_inputs
log_info "Начинаю установку WireGuard-сервера"
install_packages
setup_sysctl
setup_keys
setup_wg_config
setup_meta
setup_wg_service
setup_ufw_if_active
install_peer_helper
setup_gui
print_summary
log_success "Установка завершена"
}
main "$@"