# Wireguard UI An admin dashboard for Wireguard — manage hosts, clients, tunnels, and IP rules through a web interface. ## Features - **Host Management** — Create and configure WireGuard hosts with public keys, IP addresses, and listen ports - **Client Management** — Add clients per host with WireGuard configs, allowed IPs, and notes - **Client Rules** — Define IP table rules per client (rule type, destination, ports, protocol) - **Media Management** — Upload and manage media assets (images, documents, etc.) with tagging, thumbnails, and RAG ingestion - **User Management** — Multi-user support with role-based access (user types) - **Persistent Storage** — SQLite database with automatic schema generation - **WebSocket Support** — Real-time updates via WebSocket connections - **Responsive UI** — Tailwind CSS v4 with twui design system components ## Prerequisites - **Bun** 1.3.1 or later ([install](https://bun.sh/install)) - **SQLite** (via `@moduletrace/bun-sqlite`) - **git** - **zip** and **unzip** - **Linux** (Debian, Ubuntu, Fedora, Arch, Alpine, or similar) - Root access (for installation script and system service setup) ## Installation ### Automatic (Recommended) Run the install script on a fresh machine: ```bash curl -fsSL https://url.tben.me/wg-ui | sudo bash ``` Or clone the repo and run the script manually: ```bash git clone https://git.tben.me/Moduletrace/wireguard-ui.git cd wireguard-ui bash src/scripts/install-wg-ui.sh ``` The install script will: 1. Install system dependencies (git, curl, ca-certificates, openssl) 2. Install Bun 1.3.14 to `/opt/bun` 3. Set up library directories under `/var/lib/wgui` (keys, clients, hosts, iptables, scripts) 4. Clone or update the repo to `/var/lib/wgui/webapp` 5. Install app dependencies (`bun install`) 6. Generate `.env` with encryption secrets 7. Run WireGuard setup (if available) 8. Install Docker (if missing) and run Caddy as the `wgui-caddy` container 9. Install the `wg-quick` helper script 10. Set up systemd/OpenRC services for `wgui` and certificate renewal The installer **never modifies your firewall** — it will print the commands to temporarily allow the dashboard port so you can reach the first login, and then point you to **Setup → Firewall** for reviewing and removing rules. ### Firewall Firewall rules are managed per host, automatically: 1. **Bootstrap (manual, temporary)** — allow the dashboard port so you can reach the web UI once: ```bash ufw allow 10752/tcp # ufw (Debian/Ubuntu) firewall-cmd --permanent --add-port=10752/tcp && firewall-cmd --reload # firewalld (RHEL/Fedora) ``` 2. **Host creation** — every host setup automatically allows its entire wireguard subnet (e.g. `10.5.0.1` → `10.5.0.0/24` from anywhere) and the host's wireguard listen port (e.g. `51821/udp`) in ufw or firewalld. Detailed per-client traffic rules are enforced by the existing iptables chain scripts, not the host firewall. 3. **Host deletion** — the host's subnet and listen-port rules are removed automatically. 4. **Cleanup** — once everything is reachable via the tunnel, remove the temporary bootstrap rule and review all rules under **Setup → Firewall**: ```bash ufw delete allow 10752/tcp firewall-cmd --permanent --remove-port=10752/tcp && firewall-cmd --reload ``` Rules that no longer match any host (`temporary`/`orphaned`) are flagged with copyable removal commands on the setup page. ### TLS for Tunnel Services Client routes expose internal services to WireGuard clients over HTTPS. The data plane is a dedicated Caddy container (`wgui-caddy`, host networking) on `8443`; wg-ui owns the route and certificate records and regenerates the Caddyfile. Caddy never binds ports 80 or 443, and the host's existing proxy is left alone. Docker is required — the installer starts the daemon if it is not running. 1. If Caddy isn't installed yet, open **Setup → TLS ingress** and click **Install Caddy** (or use the Caddy action on **Setup → Maintenance**). The installer pulls `caddy:2.11.4-alpine`, (re)creates the `wgui-caddy` container, and sets up certificate support. 2. Open a route form from **Routes** (or a host's **Routes** panel) and set the ACME email and staging toggle. No DNS provider token is needed: certificates use manual DNS-01. Let's Encrypt requires a public domain you control; internal-only names such as `home.arpa` or `.local` need a private CA instead. 3. Add an A/AAAA record for each route hostname pointing at the host's WireGuard IP. Caddy also restricts each route to the subnet of the host that owns it. 4. Open the route page and click **Issue** (or **Renew**). The dashboard shows the exact `_acme-challenge.` TXT record. Add it at Cloudflare, wait for propagation, then click **Validate**. 5. Clients use `https://:8443/`. Portless URLs require the host's existing 443 proxy to forward to `127.0.0.1:8443`; wg-ui does not take over 443. Caddy allows localhost so that proxy can reach it, which means the proxy itself must enforce who may use those portless URLs. Manual DNS-01 certificates are renewed from the route page because validation needs your registrar. The route list shows expiry warnings. A successful issuance reloads Caddy without restarting wg-ui. Check `docker logs wgui-caddy` for reload failures. #### Upgrading from the old native Caddy install Earlier versions ran Caddy as a host binary in `/opt/caddy` with a `wgui-caddy` systemd/OpenRC service. Re-running the installer migrates the host in place: it pulls the image, starts the container, and only then removes the old service and binary. The Caddyfile is rewritten to use the new admin socket path (`/var/lib/wgui/caddy/admin.sock`); certificates in `/var/lib/wgui/tls` are untouched. To remove a leftover native install without installing the container: ```bash sudo bash src/scripts/install-caddy.sh --purge-legacy ``` Add `--purge` to also remove the container, the image and the certificate renewal job (the Caddyfile and certificates are always kept): ```bash sudo bash src/scripts/install-caddy.sh --purge-legacy --purge ``` ### Manual (Development) ```bash git clone https://git.tben.me/Moduletrace/wireguard-ui.git cd wireguard-ui bun install NODE_ENV=development bun run dev ``` The dev server runs on port **10752** by default. ## Configuration ### Environment Variables The `.env` file (gitignored) controls runtime behavior. It is auto-generated during installation if missing: | Variable | Description | Default | | ----------------- | ---------------------------------------- | -------------------- | | `NODE_ENV` | Environment (`development`/`production`) | `production` | | `ENCRYPTION_KEY` | Encryption key for sensitive data | Auto-generated | | `ENCRYPTION_SALT` | Encryption salt for sensitive data | Auto-generated | | `DATA_DIR` | Application data directory | `$INSTALL_DIR/.data` | | `CADDY_VERSION` | Caddy image tag (`caddy:-alpine`)| `2.11.4` | | `CADDY_HTTPS_PORT`| Caddy ingress HTTPS port (never 80/443) | `8443` | | `CADDY_HTTP_PORT` | Caddy plain HTTP port (never 80) | `8081` | | `CADDY_INGRESS_ENABLED` | Set to `0` to skip Caddy config sync | `1` | | `ACME_STAGING` | Set to `1` to use the Let's Encrypt staging CA | `0` | ### Database Configuration Edit `bun-sqlite.config.ts` to change database settings: ```typescript const config: BunSQLiteConfig = { db_name: "wgui", // Database file name db_dir: "./db", // Database directory typedef_file_path: "./db/types/db.ts", // Generated types db_schema_file_name: "schema.ts", // Schema definition }; ``` ### Server Port The server port is defined in `src/data/site-data.ts`: ```typescript export const SiteData = { ServerPort: 10752, // ... } as const; ``` ## Available Scripts | Command | Description | | ------------------- | ----------------------------------------- | | `bun run dev` | Start development server (requires root) | | `bun run start` | Start production server | | `bun run db:schema` | Generate/update database schema and types | | `bun run db:admin` | Open database admin interface | ## Project Structure ``` wireguard-ui/ ├── src/ │ ├── pages/ # File-system based routes (__root.tsx, index.tsx, etc.) │ │ ├── (sections)/ # Page sections │ │ ├── (partials)/ # Page-local components │ │ ├── (data)/ # Page-local data │ │ ├── (functions)/ # Page-local functions │ │ ├── (utils)/ # Page-local utilities │ │ └── (hooks)/ # Page-local hooks │ ├── components/ │ │ ├── twui/ # Local design system (publishable package) │ │ ├── ui/ # shadcn/ui components │ │ ├── shadcnblocks/ # shadcnblocks registry components │ │ └── general/ # Shared page components (chat, date-picker, logo, etc.) │ ├── hooks/ # Shared React hooks │ ├── functions/ │ │ ├── backend/ # Server-only: DB queries, email, media upload, RAG │ │ ├── frontend/ # Client-safe: CRUD handlers, fetch handlers │ │ └── general/ # Platform-agnostic: join builders │ ├── utils/ # Utility modules (auth, crypto, CSRF, hashing, etc.) │ ├── dict/ # Constants and dictionaries (user types, gates, etc.) │ ├── data/ # Static data (app constants, location data, etc.) │ ├── types/ # Type definitions │ ├── scripts/ # Install and setup scripts │ └── server.ts # Entry point — starts Bun.serve with WebSocket support ├── db/ │ ├── schema.ts # Database schema definition │ └── types/db.ts # Generated database types ├── public/ # Static assets served by the server ├── assets/ # Project assets ├── bun-sqlite.config.ts # Database configuration ├── bunext.config.ts # Bunext configuration ├── package.json # Dependencies and scripts ├── tsconfig.json # TypeScript configuration └── .env # Environment variables (gitignored) ``` ## Database Schema The database consists of the following tables: | Table | Description | | ----------------- | ----------------------------------------------- | | `users` | User accounts (name, email, username, password) | | `user_types` | User type assignments per user | | `media` | Media assets (images, documents, etc.) | | `media_paradigms` | Media paradigm classifications per media item | | `clients` | WireGuard clients per host | | `hosts` | WireGuard host configurations | | `client_rules` | IP table rules for each client | | `client_routes` | HTTPS routes exposed to WireGuard clients | | `tls_certificates`| Certificates used by the Caddy ingress | | `variables` | Application-wide key-value variables | Generate types from the schema: ```bash bun run db:schema ``` ## Admin Route Hierarchy | Route Pattern | Access | Features | | ----------------------------------------------- | ------------ | ------------------------------------------------------------ | | `/admin` | Admin | Dashboard overview | | `/admin/hosts` | Admin | Host management (add, edit, per-host detail) | | `/admin/routes` | Admin | Global HTTPS route list with host and certificate status | | `/admin/hosts/[host_id]/routes` | Admin | HTTPS routes and upstreams exposed to tunnel clients | | `/admin/hosts/[host_id]/routes/[route_id]` | Admin | Route details, TLS settings and certificate issue/renew | | `/admin/clients` | Admin | Client management (add, edit, client rules per host) | | `/admin/settings` | Admin | Account settings | | `/admin/setup` | Super admin | System setup (hosts, WireGuard tools, update, restart) | | `/admin/setup/tls` | Super admin | Caddy ingress installation and status | ## Development ### Development Mode Set `NODE_ENV=development` and run: ```bash NODE_ENV=development bun run dev ``` In development mode, the install script skips cloning and system service setup — the server is assumed to already be running. ### Path Alias `@/*` resolves from the repository root. Examples: ```typescript import { Button } from "@/src/components/ui/button"; import type { BUN_SQLITE_WGUI_USERS } from "@/db/types/db"; ``` ### Type-Checking Run type checks with: ```bash bunx tsc --noEmit ``` ### Testing Run individual test files: ```bash bun test path/to/test.file.test.ts ``` ## Repository - **URL**: [https://git.tben.me/Moduletrace/wireguard-ui](https://git.tben.me/Moduletrace/wireguard-ui) - **Install Script**: [src/scripts/install-wg-ui.sh](https://git.tben.me/Moduletrace/wireguard-ui/raw/branch/main/src/scripts/install-wg-ui.sh)