furtka-gateway/docker-compose.yaml
Robert Syrnicki 3a9d18fcd5 Scaffold gateway stack and single-tenant control plane
wg-easy + Traefik docker-compose stack (Phase 1) plus a stdlib-only
control-plane API for box registration, WireGuard peer provisioning via
wg-easy, and per-box route publish/unpublish backed by Traefik's file
provider (Phase 2, single-tenant mode). SQLite holds accounts/boxes/routes
so a later multi-tenant shared instance is the same schema with more rows,
not a reshape.

wg-easy's actual REST API was verified against its source rather than
assumed: it has no bearer-token auth (session-cookie login via
POST /api/auth/password) and no way to accept an externally-generated
public key (it always mints the keypair itself, private key included) —
both corrected from the original plan during implementation.
2026-08-24 11:50:25 +02:00

76 lines
2 KiB
YAML

services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy:14
container_name: furtka-gateway-wg-easy
environment:
- WG_HOST=${GATEWAY_PUBLIC_HOST}
- PASSWORD_HASH=${WG_EASY_PASSWORD_HASH}
- PORT=51821
- WG_PORT=51820
volumes:
- wg_easy_data:/etc/wireguard
ports:
- "51820:51820/udp"
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
restart: unless-stopped
networks:
- internal
# wg-easy's own admin UI/API (port 51821) is intentionally NOT published
# to the host. Only control-plane, on the internal network, talks to it.
traefik:
image: traefik:v3.1
container_name: furtka-gateway-traefik
command:
- --configFile=/etc/traefik/traefik.yml
volumes:
- ./traefik/static/traefik.${GATEWAY_MODE:-single}.yml:/etc/traefik/traefik.yml:ro
- ./traefik/dynamic:/etc/traefik/dynamic:ro
- traefik_acme:/acme
ports:
- "80:80"
- "443:443"
restart: unless-stopped
networks:
- internal
# Traefik's own dashboard/API is disabled in both static configs
# (api.dashboard: false, api.insecure: false) — never reachable at all,
# let alone publicly.
control-plane:
build: ./control_plane
container_name: furtka-gateway-control-plane
environment:
- GATEWAY_MODE=${GATEWAY_MODE:-single}
- GATEWAY_STATE_DIR=/data
- GATEWAY_DYNAMIC_DIR=/dynamic
- GATEWAY_ADMIN_TOKEN=${GATEWAY_ADMIN_TOKEN}
- GATEWAY_BOX_TOKEN=${GATEWAY_BOX_TOKEN}
- WG_EASY_URL=http://wg-easy:51821
- WG_EASY_ADMIN_USERNAME=${WG_EASY_ADMIN_USERNAME:-admin}
- WG_EASY_ADMIN_PASSWORD=${WG_EASY_ADMIN_PASSWORD}
volumes:
- control_plane_data:/data
- ./traefik/dynamic:/dynamic
ports:
- "127.0.0.1:8090:8090"
restart: unless-stopped
networks:
- internal
depends_on:
- wg-easy
- traefik
networks:
internal:
driver: bridge
volumes:
wg_easy_data:
traefik_acme:
control_plane_data: