Caddy previously ran as a host binary in /opt/caddy with a wgui-caddy systemd/OpenRC unit. Only Caddy is containerized; the app still runs on the host as root because it shells out to wg, ip and iptables. - Add caddy-container-commands.ts as the single source of truth for the run/exec/validate/inspect argument arrays. Every value stays a separate argv element, the image is pinned to caddy:2.11.4-alpine, and the entrypoint is set explicitly because some mirrors publish the image with a null Entrypoint (which made Docker exec "run" as a binary). - Rewrite install-caddy.sh: require_docker with distro-specific hints, pull + recreate the container, assert it is running and echo the daemon error instead of discarding it, normalize the admin socket path in existing Caddyfiles, and clean up a legacy native install only after the container is up so the ingress port is never unbound. Add --purge-legacy / --purge for hosts that want to drop the old install without installing the container. - Move the admin socket to /var/lib/wgui/caddy/admin.sock so it lives inside the mounted Caddy dir and reloads work over docker exec. - Validate candidate configs in a throwaway container, mounting the TLS dir read-only as well: provisioning the tls app loads every certificate referenced by a tls directive, so without that mount a route with a certificate failed validation and its Caddyfile was never committed. - Report docker-based installed/version/service_active, skip validate/reload with a warning when the image is absent, and update the system setup tool check to look for docker. - Issue certificates with a docker exec reload command and chown files to root:root, since the container reads them through a read-only mount. - Update the Setup UI copy and docs (README, AGENTS.md). Also drops the unused EmptyContent import and a stray debug log in the client route form, and stops passing the unsupported cancel_href prop to ClientRouteForm from the route add/edit pages.
327 lines
14 KiB
Markdown
327 lines
14 KiB
Markdown
# 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.<hostname>` TXT record. Add it at Cloudflare,
|
|
wait for propagation, then click **Validate**.
|
|
|
|
5. Clients use `https://<hostname>: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:<version>-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)
|