Two gaps found while walking through a real single-tenant deployment scenario, both fixed and verified against the actual running stack (not just unit tests): - wg-easy moves to network_mode: host so Traefik can actually route to WireGuard peer addresses (wg-quick's route only existed inside wg-easy's own isolated network namespace before). This forced host-level sysctls (ops/host-sysctls.sh — Docker/runc rejects namespaced sysctls under host networking) and a host firewall rule (ops/firewall.sh) to keep wg-easy's now-unisolated admin API off the public interface. - control-plane gets a real Traefik route (traefik/dynamic/control-plane.yml.example) instead of being reachable only at 127.0.0.1:8090 — box registration carries a WireGuard private key and needs TLS, not bare loopback HTTP. Along the way, running the actual pinned wg-easy image (ghcr.io/wg-easy/wg-easy:14) surfaced that it's a different, older, Express-based codebase than what wg-easy's current docs/master branch describe: auth is a plain Authorization header per request (no session cookie), routes live under /api/wireguard/client, and its default WG_ALLOWED_IPS is full-tunnel (0.0.0.0/0) rather than the split-tunnel this design assumed. wgeasy.py and docker-compose.yaml are corrected accordingly, verified end-to-end against the real container. Also fixed: Docker Compose silently truncates a bcrypt hash's `$` characters when read from .env — documented the required $$ escaping.
49 lines
2.4 KiB
Text
49 lines
2.4 KiB
Text
# Which cert/tenancy strategy to run: "single" or "shared".
|
|
# See traefik/static/traefik.single.yml vs traefik.shared.yml.
|
|
GATEWAY_MODE=single
|
|
|
|
# The hostname (or IP, for a first local smoke test) wg-easy advertises to
|
|
# peers as the WireGuard endpoint. Must be reachable on udp/51820.
|
|
GATEWAY_PUBLIC_HOST=vpn.example.com
|
|
|
|
# bcrypt hash of the wg-easy admin password. Generate with:
|
|
# docker run --rm ghcr.io/wg-easy/wg-easy:14 node -e \
|
|
# "console.log(require('bcryptjs').hashSync(process.argv[1], 10))" 'your-password'
|
|
#
|
|
# IMPORTANT: bcrypt hashes contain literal `$` characters (e.g.
|
|
# "$2a$10$..."), and Docker Compose's own .env-file parser treats `$word`
|
|
# as a variable reference to substitute — confirmed by actually hitting
|
|
# this: an unescaped hash silently got truncated to "$2a$10" with
|
|
# everything after dropped. Every `$` in the value below MUST be doubled
|
|
# as `$$`, e.g. WG_EASY_PASSWORD_HASH=$$2a$$10$$abc123...
|
|
WG_EASY_PASSWORD_HASH=
|
|
|
|
# PLAINTEXT password matching the hash above. wg-easy's admin API (this
|
|
# specific pinned image, ghcr.io/wg-easy/wg-easy:14 — confirmed by reading
|
|
# its actual source, which turned out to differ from what wg-easy's current
|
|
# docs describe) has no login call or session cookie at all: every request
|
|
# just carries this password as a plain `Authorization` header, checked
|
|
# with bcrypt against WG_EASY_PASSWORD_HASH. No username concept exists.
|
|
WG_EASY_ADMIN_PASSWORD=
|
|
|
|
# GATEWAY_MODE=single only: the one box token a single-tenant deployment
|
|
# accepts at /v1/boxes/register, skipping full account/registration-token
|
|
# issuance. Generate with: openssl rand -hex 32
|
|
GATEWAY_BOX_TOKEN=
|
|
|
|
# Hostname a Furtka box actually reaches the control-plane API on — needs
|
|
# its own A/AAAA record pointing at this VPS. Not read by docker-compose
|
|
# itself; it's a reminder of what to substitute into
|
|
# traefik/dynamic/control-plane.yml (copied from the .example file — see
|
|
# that file for why this route has to exist at all).
|
|
GATEWAY_CONTROL_PLANE_HOST=gw.example.com
|
|
|
|
# Bearer token required to create accounts via POST /v1/accounts.
|
|
# Only meaningful once account endpoints exist (Phase 2+); harmless to set
|
|
# now. Generate with: openssl rand -hex 32
|
|
GATEWAY_ADMIN_TOKEN=
|
|
|
|
# GATEWAY_MODE=shared only: base domain subdomains are issued under, and the
|
|
# DNS provider Traefik's DNS-01 challenge should use for the wildcard cert.
|
|
# See traefik/static/traefik.shared.yml.
|
|
GATEWAY_BASE_DOMAIN=boxes.example.com
|