diff --git a/src/scripts/install-wg-ui.sh b/src/scripts/install-wg-ui.sh new file mode 100755 index 0000000..d015c5a --- /dev/null +++ b/src/scripts/install-wg-ui.sh @@ -0,0 +1,247 @@ +#!/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 + +WGUI_LIB_DIR="/var/lib/wgui" +INSTALL_DIR="${INSTALL_DIR:-$WGUI_LIB_DIR/webapp}" +SERVICE_USER="${SERVICE_USER:-wgui}" +SERVICE_NAME="${SERVICE_NAME:-wgui}" +REPO_URL="${REPO_URL:-}" +BRANCH="${BRANCH:-main}" +BUN_INSTALL_DIR="${BUN_INSTALL_DIR:-/opt/bun}" +BUN_BIN="/usr/local/bin/bun" + +log() { echo "==> $*"; } +fail() { echo "error: $*" >&2; exit 1; } + +if [ "$(id -u)" -ne 0 ]; then + fail "this script must be run as root" +fi + +if [ -z "$REPO_URL" ]; then + fail "REPO_URL is not set — pass the git URL of the wg-ui repo, e.g. + REPO_URL=https://git.example.com/org/wireguard-ui.git $0" +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) + 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() { + if [ ! -x "$BUN_BIN" ]; then + log "installing bun to $BUN_INSTALL_DIR ..." + mkdir -p "$BUN_INSTALL_DIR" + curl -fsSL https://bun.sh/install | BUN_INSTALL="$BUN_INSTALL_DIR" bash + if [ ! -x "$BUN_INSTALL_DIR/bun" ]; then + fail "bun binary not found at $BUN_INSTALL_DIR/bun after install" + fi + ln -sf "$BUN_INSTALL_DIR/bun" "$BUN_BIN" + fi + log "bun: $($BUN_BIN --version)" +} + +setup_service_user() { + if ! id -u "$SERVICE_USER" >/dev/null 2>&1; then + useradd --system --home-dir "$WGUI_LIB_DIR" --shell /usr/sbin/nologin "$SERVICE_USER" + fi + mkdir -p "$WGUI_LIB_DIR/iptables" "$WGUI_LIB_DIR/keys" "$WGUI_LIB_DIR/clients" + chown -R "$SERVICE_USER:$SERVICE_USER" "$WGUI_LIB_DIR" +} + +clone_or_update() { + mkdir -p "$(dirname "$INSTALL_DIR")" + if [ -d "$INSTALL_DIR/.git" ]; then + log "updating existing install at $INSTALL_DIR ..." + git -C "$INSTALL_DIR" fetch --depth 1 origin "$BRANCH" + git -C "$INSTALL_DIR" reset --hard "origin/$BRANCH" + else + log "cloning wg-ui ($REPO_URL, branch $BRANCH) ..." + git clone --depth 1 --branch "$BRANCH" "$REPO_URL" "$INSTALL_DIR" + fi + chown -R "$SERVICE_USER:$SERVICE_USER" "$INSTALL_DIR" +} + +run_as_service_user() { + local cmd + cmd="cd $(printf '%q' "$INSTALL_DIR") && export PATH=/usr/local/bin:/usr/bin:/bin && $(printf '%q ' "$@")" + if command -v runuser >/dev/null 2>&1; then + runuser -u "$SERVICE_USER" -- bash -c "$cmd" + else + su -s /bin/bash -c "$cmd" "$SERVICE_USER" + fi +} + +install_dependencies() { + log "installing app dependencies with bun ..." + run_as_service_user "$BUN_BIN" install +} + +ensure_env_file() { + if [ ! -f "$INSTALL_DIR/.env" ]; then + log "generating $INSTALL_DIR/.env with fresh encryption secrets ..." + { + echo "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" + chown "$SERVICE_USER:$SERVICE_USER" "$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 +} + +grant_runtime_access() { + log "granting $SERVICE_USER access to /etc/wireguard ..." + mkdir -p /etc/wireguard + chown "root:$SERVICE_USER" /etc/wireguard + chmod 770 /etc/wireguard +} + +install_systemd_unit() { + local unit="/etc/systemd/system/$SERVICE_NAME.service" + log "writing systemd unit $unit ..." + cat > "$unit" < "$init_script" </dev/null | grep -oE '[0-9]+' | head -1 || echo "10752" +} + +install_deps +install_bun +setup_service_user +clone_or_update +install_dependencies +ensure_env_file +setup_wireguard +grant_runtime_access + +case "$INIT_SYSTEM" in + systemd) + install_systemd_unit + ;; + openrc) + install_openrc_unit + ;; + *) + log "no supported init system found — start manually with: + su -s /bin/bash $SERVICE_USER -c 'cd $INSTALL_DIR && NODE_ENV=production $BUN_BIN src/server.ts' +(add the line above to your boot scripts)" + ;; +esac + +PORT="$(grab_port)" +log "wg-ui install complete." +log "webapp: $INSTALL_DIR" +log "runtime: $WGUI_LIB_DIR (keys, iptables, client configs)" +log "process: managed by $INIT_SYSTEM as $SERVICE_NAME" +log "open http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo localhost):$PORT in your browser" \ No newline at end of file diff --git a/src/scripts/setup-wireguard.sh b/src/scripts/setup-wireguard.sh index dacd7a4..18118ed 100755 --- a/src/scripts/setup-wireguard.sh +++ b/src/scripts/setup-wireguard.sh @@ -59,6 +59,10 @@ case "$DISTRO" in echo "detected Arch-family distro: $DISTRO" pacman -Syu --noconfirm --needed wireguard-tools ;; + alpine) + echo "detected Alpine distro: $DISTRO" + apk add --no-cache wireguard-tools + ;; *) echo "error: unsupported distro: $DISTRO" >&2 echo "install wireguard manually (kernel module + wg/wg-quick tools)" >&2