Go backend that drives kernel WireGuard over netlink (wireguard-go as the fallback), nftables NAT with MSS clamping, forwarding and buffer sysctls, SQLite for peers, users, sessions, traffic history and the audit log. React console: dashboard with live rates and usage history, peer management with QR codes and .conf downloads, disconnect, session reset, key rotation, expiry, client-supplied keys, settings, users with admin and viewer roles, two-factor authentication with recovery codes, audit log. Docker image on Alpine with compose files for bridged and host networking, CI and GHCR publish workflows, performance notes.
141 lines
6.6 KiB
Markdown
141 lines
6.6 KiB
Markdown
# WGX
|
|
|
|
A WireGuard server with a secure web console, in one container.
|
|
|
|
Start it, open the console, create a peer, scan the QR code. WGX runs the
|
|
tunnel on the kernel's WireGuard module, keeps the NAT rules and forwarding
|
|
sysctls in order, and gives you a dashboard that shows who is connected, how
|
|
much they are moving, and a button to cut them off.
|
|
|
|
## What it does
|
|
|
|
- **Peers.** Create, edit, disable, delete. The server generates the key
|
|
pair (and a preshared key) and shows a QR code and a `.conf` download; or
|
|
the client brings its own public key and the private key never leaves the
|
|
device. Pin a tunnel address or let WGX allocate one. Set an expiry and the
|
|
peer is disconnected on time. Rotate keys in one click.
|
|
- **Who is connected.** Live status from the interface counters every two
|
|
seconds: endpoint, last handshake, session length, current rate, total
|
|
transfer. Usage history in five-minute buckets, per peer and overall, kept
|
|
for 90 days.
|
|
- **Disconnect them.** *Disconnect* removes the peer from the interface and
|
|
keeps it off until you enable it again. *Reset session* drops the current
|
|
session and lets the client handshake afresh.
|
|
- **Fast.** Kernel data plane over netlink, no user-space hop. Tuned
|
|
sysctls, TCP MSS clamping, optional host networking. Falls back to
|
|
`wireguard-go` on hosts without the module and tells you so. See
|
|
[docs/performance.md](docs/performance.md).
|
|
- **Locked down.** argon2id passwords, two-factor authentication with
|
|
recovery codes, viewer and administrator roles, rate-limited login,
|
|
same-origin enforcement, strict CSP, built-in TLS if you want it, and an
|
|
audit log of every change (including every time a peer's configuration is
|
|
viewed). See [SECURITY.md](SECURITY.md).
|
|
- **Observable.** `/api/health` for a liveness probe and `/metrics` in
|
|
Prometheus format, guarded by a bearer token.
|
|
- **Self-contained.** One static Go binary, one SQLite file under `/data`,
|
|
no other services. Multi-arch image for amd64 and arm64.
|
|
|
|
## Quick start
|
|
|
|
```sh
|
|
curl -O https://raw.githubusercontent.com/Coffey-Labs/WGX/main/docker-compose.yml
|
|
# edit WGX_ENDPOINT (your public hostname or IP), then:
|
|
docker compose up -d
|
|
```
|
|
|
|
Open <http://localhost:51821>, create the first administrator, and add a
|
|
peer. Point the WireGuard app on your phone at the QR code.
|
|
|
|
The console is bound to localhost in the compose file on purpose. To reach
|
|
it from elsewhere, either set `WGX_TLS_SELF_SIGNED: "true"` and bind to the
|
|
address you need, or put a TLS-terminating reverse proxy in front of it and
|
|
list the proxy in `WGX_TRUSTED_PROXIES`.
|
|
|
|
For the fastest configuration, `docker-compose.host.yml` runs on the host
|
|
network; [docs/performance.md](docs/performance.md) says when that is worth
|
|
it and which host sysctls to set.
|
|
|
|
### Requirements
|
|
|
|
- Docker (or Podman) on a Linux host with a kernel from 5.6 on. Older kernels
|
|
work with `wireguard-dkms` installed on the host, or fall back to the
|
|
slower user-space data plane automatically.
|
|
- The container needs `NET_ADMIN` and the forwarding sysctls in the compose
|
|
file. `SYS_MODULE` is not needed unless the host has never loaded the
|
|
module and cannot autoload it.
|
|
- UDP port 51820 (or whatever you choose) reachable from the internet.
|
|
|
|
## Configuration
|
|
|
|
Infrastructure is configured through the environment; everything an
|
|
administrator might change while the server runs lives in the database and
|
|
is edited in the console under **Settings** (endpoint, DNS, default client
|
|
routes, MTU, keepalive, peer isolation, MSS clamping, preshared keys).
|
|
|
|
| Variable | Default | Meaning |
|
|
| --- | --- | --- |
|
|
| `WGX_ENDPOINT` | | Public hostname or IP for client configs. Also asked for at first-run setup. |
|
|
| `WGX_PORT` | `51820` | UDP listen port. |
|
|
| `WGX_SUBNET` | `10.8.0.0/24` | IPv4 tunnel network; the server takes the first address. |
|
|
| `WGX_SUBNET6` | | IPv6 tunnel network, e.g. `fd42:42:42::/64`. Off when empty. |
|
|
| `WGX_DNS` | `1.1.1.1, 1.0.0.1` | Resolvers handed to clients on first run. |
|
|
| `WGX_INTERFACE` | `wg0` | Interface name. |
|
|
| `WGX_EGRESS_INTERFACE` | auto | Interface to masquerade on. Auto uses the default route. |
|
|
| `WGX_HTTP_LISTEN` | `:51821` | Console listen address. |
|
|
| `WGX_TLS_SELF_SIGNED` | `false` | Serve HTTPS with a certificate generated into `/data`. |
|
|
| `WGX_TLS_CERT`, `WGX_TLS_KEY` | | Serve HTTPS with your own certificate. |
|
|
| `WGX_SECURE_COOKIES` | `false` | Mark cookies `Secure` when TLS terminates at a proxy. |
|
|
| `WGX_TRUSTED_PROXIES` | | CIDRs whose `X-Forwarded-For` is believed. |
|
|
| `WGX_METRICS_TOKEN` | | Bearer token for `/metrics`. A signed-in session works too. |
|
|
| `WGX_SESSION_IDLE` | `12h` | Sign out after this much inactivity. |
|
|
| `WGX_SESSION_MAX` | `168h` | Sign out after this long regardless. |
|
|
| `WGX_TRAFFIC_RETENTION` | `2160h` | How long usage history is kept (90 days). |
|
|
| `WGX_POLL_INTERVAL` | `2s` | How often the interface counters are read. |
|
|
| `WGX_BACKEND` | `auto` | `kernel`, `userspace` or `mock`. Auto prefers the kernel. |
|
|
| `WGX_MANAGE_FIREWALL` | `true` | Set to `false` if the host owns the NAT rules. |
|
|
| `WGX_MANAGE_SYSCTL` | `true` | Set to `false` if the host has tuned itself. |
|
|
| `WGX_DATA_DIR` | `/data` | Where the database and TLS files live. |
|
|
| `WGX_LOG_LEVEL`, `WGX_LOG_JSON` | `info`, `false` | Logging. |
|
|
|
|
## Locked out?
|
|
|
|
```sh
|
|
docker exec -it wgx wgx reset-password admin
|
|
```
|
|
|
|
sets a new password for that user, clears their second factor and ends
|
|
their sessions. It runs against the same database, so no restart is needed.
|
|
|
|
## Client setup
|
|
|
|
Any WireGuard client works: the official apps on iOS, Android, macOS and
|
|
Windows, `wg-quick` on Linux, and routers that speak WireGuard. Scan the QR
|
|
code from the peer's **Configuration** tab, or download the `.conf`. The
|
|
default configuration routes everything through the tunnel; change **Client
|
|
routes** on the peer (or the default under Settings) to the tunnel subnet
|
|
alone for split tunnelling.
|
|
|
|
## API
|
|
|
|
Everything the console does goes through `/api/…` with the session cookie.
|
|
`GET /api/peers`, `POST /api/peers`, `GET /api/peers/{id}/config`,
|
|
`POST /api/peers/{id}/disable` and friends are stable enough to script
|
|
against; the shapes are in `internal/server/api.go`. A cross-site request
|
|
without a same-origin `Sec-Fetch-Site` or `Origin` header is refused, so
|
|
call it from the same origin or from a non-browser client.
|
|
|
|
## Building from source
|
|
|
|
```sh
|
|
cd web && npm ci && npm run build && cd ..
|
|
go build ./cmd/wgx
|
|
```
|
|
|
|
The UI is embedded in the binary. `docker build -t wgx .` does both steps.
|
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for the development loop against the
|
|
mock data plane, which needs no privileges.
|
|
|
|
## Licence
|
|
|
|
AGPL-3.0-or-later. See [LICENSE](LICENSE).
|