284 lines
8.5 KiB
Bash
Executable File
284 lines
8.5 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# This script installs wg-ui from the git repo
|
|
# and sets up the appropriate system process
|
|
# manager (systemd or whichever) for the
|
|
# appropriate OS. The preferred directory for
|
|
# assets is /var/lib/wgui/webapp
|
|
|
|
set -euo pipefail
|
|
|
|
log() { echo "==> $*"; }
|
|
fail() { echo "error: $*" >&2; exit 1; }
|
|
|
|
WGUI_LIB_DIR="/var/lib/wgui"
|
|
WG_QUICK_HELPER="$WGUI_LIB_DIR/scripts/wg-quick-manage.sh"
|
|
INSTALL_DIR="${INSTALL_DIR:-}"
|
|
SERVICE_NAME="${SERVICE_NAME:-wgui}"
|
|
REPO_URL="${REPO_URL:-https://git.tben.me/Moduletrace/wireguard-ui.git}"
|
|
BRANCH="${BRANCH:-main}"
|
|
BUN_INSTALL_DIR="${BUN_INSTALL_DIR:-/opt/bun}"
|
|
BUN_VERSION="${BUN_VERSION:-1.4.1}"
|
|
BUN_BIN="/usr/local/bin/bun"
|
|
UPDATE=false
|
|
DEV_MODE=false
|
|
if [ "${NODE_ENV:-}" = "development" ]; then
|
|
DEV_MODE=true
|
|
fi
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
|
|
REPO_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
|
|
|
require_root() {
|
|
if [ "$(id -u)" -ne 0 ]; then
|
|
if ! command -v sudo >/dev/null 2>&1; then
|
|
fail "this script must be run as root, and sudo is not installed"
|
|
fi
|
|
log "not running as root — re-executing with sudo ..."
|
|
sudo -v || fail "current user does not have sudo privileges — run this script as root or grant the current user sudo access"
|
|
exec sudo -E "$0" "$@"
|
|
fi
|
|
}
|
|
|
|
require_root
|
|
log "running the wg-ui service as root"
|
|
|
|
if [ "$DEV_MODE" = true ]; then
|
|
INSTALL_DIR="${INSTALL_DIR:-$REPO_ROOT}"
|
|
log "NODE_ENV=development — using local repo at $INSTALL_DIR, skipping clone and system service installation (daemon is assumed to already be running)"
|
|
else
|
|
INSTALL_DIR="${INSTALL_DIR:-$WGUI_LIB_DIR/webapp}"
|
|
fi
|
|
|
|
detect_distro() {
|
|
if [ -f /etc/os-release ]; then
|
|
# shellcheck disable=SC1091
|
|
. /etc/os-release
|
|
echo "$ID"
|
|
else
|
|
echo "unknown"
|
|
fi
|
|
}
|
|
|
|
detect_init_system() {
|
|
if command -v systemctl >/dev/null 2>&1; then
|
|
echo "systemd"
|
|
elif [ -x /sbin/openrc-run ] || [ -f /etc/alpine-release ]; then
|
|
echo "openrc"
|
|
else
|
|
echo "unknown"
|
|
fi
|
|
}
|
|
|
|
DISTRO="$(detect_distro)"
|
|
INIT_SYSTEM="$(detect_init_system)"
|
|
log "detected distro: $DISTRO, init system: $INIT_SYSTEM"
|
|
|
|
install_deps() {
|
|
case "$DISTRO" in
|
|
debian | ubuntu | linuxmint | raspbian)
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
apt-get update -y
|
|
apt-get install -y git curl ca-certificates openssl
|
|
;;
|
|
fedora | rhel | centos | rocky | almalinux | ol)
|
|
dnf install -y git curl ca-certificates openssl
|
|
;;
|
|
arch | manjaro | endeavouros)
|
|
pacman -Syu --noconfirm --needed git curl ca-certificates openssl
|
|
;;
|
|
alpine)
|
|
apk add --no-cache bash curl ca-certificates-bundle git openssl
|
|
;;
|
|
*)
|
|
fail "unsupported distro: $DISTRO"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
install_bun() {
|
|
local bun_binary="$BUN_INSTALL_DIR/bin/bun"
|
|
if [ ! -x "$BUN_BIN" ]; then
|
|
log "installing bun $BUN_VERSION to $BUN_INSTALL_DIR ..."
|
|
mkdir -p "$BUN_INSTALL_DIR"
|
|
curl -fsSL https://bun.sh/install | BUN_INSTALL="$BUN_INSTALL_DIR" bash -s "bun-v$BUN_VERSION"
|
|
if [ ! -x "$bun_binary" ]; then
|
|
fail "bun binary not found at $bun_binary after install"
|
|
fi
|
|
ln -sf "$bun_binary" "$BUN_BIN"
|
|
fi
|
|
log "bun: $($BUN_BIN --version)"
|
|
}
|
|
|
|
setup_lib_dirs() {
|
|
mkdir -p "$WGUI_LIB_DIR/iptables" "$WGUI_LIB_DIR/keys" "$WGUI_LIB_DIR/clients" "$WGUI_LIB_DIR/hosts" "$WGUI_LIB_DIR/scripts"
|
|
chmod 700 "$WGUI_LIB_DIR/iptables" "$WGUI_LIB_DIR/keys" "$WGUI_LIB_DIR/clients" "$WGUI_LIB_DIR/hosts"
|
|
chmod 755 "$WGUI_LIB_DIR/scripts"
|
|
}
|
|
|
|
clone_or_update() {
|
|
mkdir -p "$(dirname "$INSTALL_DIR")"
|
|
if [ -d "$INSTALL_DIR/.git" ]; then
|
|
UPDATE=true
|
|
log "updating existing install at $INSTALL_DIR ..."
|
|
git -C "$INSTALL_DIR" fetch --depth 1 origin "$BRANCH"
|
|
local db_backup="$INSTALL_DIR/db/.wgui-pre-update.db"
|
|
if [ -f "$INSTALL_DIR/db/wgui" ]; then
|
|
cp -a "$INSTALL_DIR/db/wgui" "$db_backup"
|
|
fi
|
|
git -C "$INSTALL_DIR" reset --hard "origin/$BRANCH"
|
|
if [ -f "$db_backup" ]; then
|
|
mv -f "$db_backup" "$INSTALL_DIR/db/wgui"
|
|
fi
|
|
else
|
|
log "cloning wg-ui ($REPO_URL, branch $BRANCH) ..."
|
|
git clone --depth 1 --branch "$BRANCH" "$REPO_URL" "$INSTALL_DIR"
|
|
fi
|
|
}
|
|
|
|
install_dependencies() {
|
|
log "installing app dependencies with bun ..."
|
|
cd "$INSTALL_DIR"
|
|
"$BUN_BIN" install
|
|
}
|
|
|
|
ensure_env_file() {
|
|
if [ ! -f "$INSTALL_DIR/.env" ]; then
|
|
log "generating $INSTALL_DIR/.env with fresh encryption secrets ..."
|
|
{
|
|
echo "NODE_ENV=${NODE_ENV:-production}"
|
|
echo "ENCRYPTION_KEY=$(openssl rand -base64 32 | tr -d '\n')"
|
|
echo "ENCRYPTION_SALT=$(openssl rand -base64 32 | tr -d '\n')"
|
|
echo "DATA_DIR=$INSTALL_DIR/.data"
|
|
} > "$INSTALL_DIR/.env"
|
|
chmod 600 "$INSTALL_DIR/.env"
|
|
fi
|
|
}
|
|
|
|
setup_wireguard() {
|
|
local setup_script="$INSTALL_DIR/src/scripts/setup-wireguard.sh"
|
|
if [ -x "$setup_script" ] && [ "${SKIP_WIREGUARD_SETUP:-0}" != "1" ]; then
|
|
log "running $(basename "$setup_script") to install wireguard on this host ..."
|
|
"$setup_script"
|
|
fi
|
|
}
|
|
|
|
install_wg_quick_helper() {
|
|
local helper_src="$INSTALL_DIR/src/scripts/wg-quick-manage.sh"
|
|
if [ ! -f "$helper_src" ]; then
|
|
fail "missing $helper_src — cannot install the wg-quick helper"
|
|
fi
|
|
log "installing wg-quick helper to $WG_QUICK_HELPER ..."
|
|
install -m 755 -o root -g root "$helper_src" "$WG_QUICK_HELPER"
|
|
}
|
|
|
|
install_systemd_unit() {
|
|
local unit="/etc/systemd/system/$SERVICE_NAME.service"
|
|
log "writing systemd unit $unit ..."
|
|
cat > "$unit" <<EOF
|
|
[Unit]
|
|
Description=Wireguard UI
|
|
After=network-online.target
|
|
Wants=network-online.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=root
|
|
Group=root
|
|
WorkingDirectory=$INSTALL_DIR
|
|
Environment=NODE_ENV=production
|
|
ExecStart=$BUN_BIN src/server.ts
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
TimeoutStartSec=300
|
|
PrivateTmp=true
|
|
UMask=0077
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
systemctl daemon-reload
|
|
log "enabling and starting $SERVICE_NAME ..."
|
|
systemctl enable --now "$SERVICE_NAME.service"
|
|
if [ "$UPDATE" = true ] && [ -z "${SKIP_SERVICE_RESTART:-}" ]; then
|
|
log "restarting $SERVICE_NAME to load updated code ..."
|
|
systemctl restart "$SERVICE_NAME.service"
|
|
fi
|
|
}
|
|
|
|
install_openrc_unit() {
|
|
local init_script="/etc/init.d/$SERVICE_NAME"
|
|
log "writing openrc init script $init_script ..."
|
|
cat > "$init_script" <<EOF
|
|
#!/sbin/openrc-run
|
|
name="$SERVICE_NAME"
|
|
description="Wireguard UI"
|
|
command="$BUN_BIN"
|
|
command_args="src/server.ts"
|
|
command_user="root"
|
|
directory="$INSTALL_DIR"
|
|
output_log="/var/log/$SERVICE_NAME.log"
|
|
error_log="/var/log/$SERVICE_NAME.log"
|
|
pidfile="/run/\${RC_SVCNAME}.pid"
|
|
export NODE_ENV="production"
|
|
|
|
start_pre() {
|
|
umask 077
|
|
}
|
|
|
|
depend() {
|
|
need net
|
|
}
|
|
EOF
|
|
chmod +x "$init_script"
|
|
log "adding $SERVICE_NAME to default runlevel and starting ..."
|
|
rc-update add "$SERVICE_NAME" default
|
|
if [ "$UPDATE" = true ] && [ -z "${SKIP_SERVICE_RESTART:-}" ]; then
|
|
rc-service "$SERVICE_NAME" restart
|
|
else
|
|
rc-service "$SERVICE_NAME" start
|
|
fi
|
|
}
|
|
|
|
grab_port() {
|
|
grep -oE 'ServerPort: [0-9]+' "$INSTALL_DIR/src/data/site-data.ts" 2>/dev/null | grep -oE '[0-9]+' | head -1 || echo "10752"
|
|
}
|
|
|
|
install_deps
|
|
install_bun
|
|
setup_lib_dirs
|
|
if [ "$DEV_MODE" = false ]; then
|
|
clone_or_update
|
|
fi
|
|
install_dependencies
|
|
ensure_env_file
|
|
setup_wireguard
|
|
install_wg_quick_helper
|
|
|
|
if [ "$DEV_MODE" = false ]; then
|
|
case "$INIT_SYSTEM" in
|
|
systemd)
|
|
install_systemd_unit
|
|
;;
|
|
openrc)
|
|
install_openrc_unit
|
|
;;
|
|
*)
|
|
log "no supported init system found — start manually with:
|
|
cd $INSTALL_DIR && NODE_ENV=production $BUN_BIN src/server.ts
|
|
(add the line above to your boot scripts)"
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
PORT="$(grab_port)"
|
|
log "wg-ui install complete."
|
|
log "webapp: $INSTALL_DIR"
|
|
log "runtime: $WGUI_LIB_DIR (host configs, keys, iptables, client configs)"
|
|
log "tunnels: managed by the web server via wg-quick up/down (configs in $WGUI_LIB_DIR/hosts — /etc/wireguard is never touched)"
|
|
if [ "$DEV_MODE" = true ]; then
|
|
log "process: development mode — no system service installed; run the dev server as root (e.g. after sudo -i)"
|
|
else
|
|
log "process: managed by $INIT_SYSTEM as $SERVICE_NAME"
|
|
fi
|
|
log "open http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo localhost):$PORT in your browser" |