Some checks failed
Build control-plane image / build-image (push) Has been cancelled
Restructure the deployment model: instead of bundling and owning its own Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy the operator already runs (as sibling containers, or otherwise reachable). This avoids duplicating infra self-hosters commonly already have, and sidesteps having to run a second ACME/DNS setup entirely — the operator's own Traefik already has a working cert resolver. - New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live against a real running container (auth model, route shapes, and the new per-client PresharedKey all differ materially from v14's API). Threaded the preshared key through to the box side (furtka/wireguard.py in the furtka repo) so v15's handshake actually completes. - GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE- derived hardcoded "le", so it can mirror whatever resolver name (if any) the operator's own Traefik already has configured. - Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh asks how the operator's Traefik reaches it (same docker network, joined via `docker network connect`, or a separate host) instead of prompting for DNS-01 providers and port remaps single-parallel mode needed. - single-parallel mode is retired outright — nothing needs it once furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for. - The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to examples/starter-stack/ as the documented zero-infra on-ramp. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
73 lines
3.7 KiB
Markdown
73 lines
3.7 KiB
Markdown
# furtka-gateway starter stack
|
|
|
|
Zero-infra on-ramp: bundles its own Traefik + wg-easy alongside the
|
|
control-plane, for an operator who doesn't already run either. This is a
|
|
secondary path — if you already have Traefik and/or wg-easy running
|
|
(a common self-hosting setup), use the main repo's root `docker-compose.yaml`
|
|
+ `ops/deploy.sh` instead, which plugs into what you already have rather
|
|
than running a second copy. See the root `README.md`'s Prerequisites
|
|
checklist.
|
|
|
|
This stack always runs in `GATEWAY_MODE=single`, owns ports `80/tcp`,
|
|
`443/tcp`, and `51820/udp` on the host directly, and uses HTTP-01 certs
|
|
(no DNS provider needed) — the tradeoffs that make `single-parallel` mode
|
|
necessary elsewhere don't apply here, because this stack assumes nothing
|
|
else is already using those ports.
|
|
|
|
## Deploying on a VPS
|
|
|
|
1. Get a VPS with a static public IP, and point two A/AAAA records at it:
|
|
one for `GATEWAY_PUBLIC_HOST` (the WireGuard endpoint) and one for
|
|
`GATEWAY_CONTROL_PLANE_HOST` (the registration API — needs its own
|
|
hostname since the response carries a WireGuard private key in
|
|
plaintext JSON, so that call must go over HTTPS).
|
|
2. Run `./deploy.sh` from this directory and answer its prompts. It
|
|
generates the wg-easy admin password hash, `GATEWAY_BOX_TOKEN`, and
|
|
`GATEWAY_ADMIN_TOKEN`, writes `.env`, renders
|
|
`traefik/static/traefik.active.yml` and
|
|
`traefik/dynamic/control-plane.yml` from their tracked templates, runs
|
|
`sudo ops/host-sysctls.sh` (wg-easy needs `net.ipv4.ip_forward=1` and
|
|
`net.ipv4.conf.all.src_valid_mark` — see that script's comments for why
|
|
Docker's own `sysctls:` key can't set these under `network_mode: host`),
|
|
brings the stack up, runs `sudo ops/firewall.sh` (locks wg-easy's admin
|
|
API off the public interface — `network_mode: host` means Docker itself
|
|
can no longer do that automatically), and health-checks it.
|
|
3. Open `80/tcp`, `443/tcp`, `51820/udp` in whatever firewall/security
|
|
group sits in front of the VPS.
|
|
|
|
Run `./deploy.sh --render-only` to just (re)write `.env` and the rendered
|
|
Traefik files without touching sysctls/compose/firewall — useful to inspect
|
|
what would be generated, or after hand-editing `.env`.
|
|
|
|
## Local dev
|
|
|
|
```bash
|
|
./deploy.sh --render-only
|
|
# GATEWAY_PUBLIC_HOST can be a LAN-reachable IP for a first smoke test.
|
|
docker compose up -d
|
|
curl http://127.0.0.1:8090/healthz
|
|
```
|
|
|
|
`ops/firewall.sh` and the `control-plane.yml` Traefik route are about
|
|
reaching this gateway from the real internet — skip both for local dev.
|
|
|
|
## Docker Swarm caveats
|
|
|
|
If deploying via `docker stack deploy` instead of `docker compose up`:
|
|
- `docker-compose.yaml`'s `build: ../../control_plane` doesn't work under
|
|
Swarm at all — it only ever schedules pre-built, tagged images. Swarm
|
|
falls back to the `image:` key on the same service instead, which CI
|
|
keeps published to the registry on every push to `master` (tagged
|
|
`latest` plus the short commit sha). Set `GATEWAY_CONTROL_PLANE_IMAGE` in
|
|
`.env` first if you want a specific commit-sha tag rather than whatever
|
|
`latest` currently points to.
|
|
- `docker stack deploy` does not read a `.env` file next to the compose
|
|
file the way `docker compose up` does — `set -a; source .env; set +a`
|
|
first, or the `${VAR}` substitutions come out empty. **Do not `source`
|
|
`.env` directly if `WG_EASY_PASSWORD_HASH` is set** — bash expands `$$`
|
|
(its own PID) while sourcing, silently corrupting the hash's doubled-`$`
|
|
escaping. Export that one variable separately, unescaped, or set it
|
|
directly on the `docker stack deploy` command line.
|
|
- `depends_on` is silently ignored by Swarm — harmless here, since
|
|
`control-plane` only talks to wg-easy/Traefik lazily on the first real
|
|
request, not at startup.
|