Skip to content

Releases: host-yt/caddy-proxy-manager

v1.4.0

Choose a tag to compare

@marcoome marcoome released this 15 Jul 15:06

Reverse-proxy control panel for Caddy: multi-tenant hosts, ACME + on-demand TLS, WAF, GeoIP, mTLS, L4 streams, multi-node fleet, and a dark/light web UI.

Highlights

  • SQLite database engine - run the panel with no separate database container. Pick db_driver=sqlite3 in the install wizard for a homelab / single-node install; migrations, the full MySQL-dialect query set, and engine-native backup + restore-drill all work on SQLite. MariaDB/MySQL remains the recommended backend for multi-writer / multi-node / scale deployments (SQLite runs single-writer).
  • Serve your own TLS certificates - import a cert + key, link it to a route, and the edge serves it for that domain with no ACME. Ideal for private-CA / internal domains or pre-issued certs. See docs/MANUAL_CERTS.md.
  • Multi-node cluster route fan-out (#3) - a route on a node group is now pushed to every node in the group, not just the anchor. Peers no longer answer an empty NOP.

Fixed

  • Enabling WAF or GeoIP could reject a node's whole config and freeze it (missing WAF audit-log dir / missing GeoIP DB); both are now guarded.
  • The Manual Certificates admin page rendered blank (missing from the layout dispatch).
  • Auth tokens (password reset / node join / registration) could be born already expired on non-UTC servers; expiry is now computed DB-side.
  • Compose no longer forwards only some module flags to the app, and a network race on geoip-init is fixed.

Full detail: CHANGELOG.md.

Images

Multi-arch (amd64 + arm64), pushed to GHCR on this tag:

  • ghcr.io/host-yt/caddy-proxy-manager:1.4.0
  • ghcr.io/host-yt/caddy-proxy-manager-edge:1.4.0
  • ghcr.io/host-yt/caddy-proxy-manager-node-agent:1.4.0
  • ghcr.io/host-yt/caddy-proxy-manager-wg:1.4.0

Deploy: docs/INSTALL.md.

v1.3.4

Choose a tag to compare

@marcoome marcoome released this 12 Jul 14:53

Fixed

  • First-run On-Demand TLS 403 (#1): panel's own APP_URL host is now always approved by /internal/ask, so Caddy can provision the panel cert before any route row exists.
  • First-run login loop over HTTP IP (#1): session/auth cookie Secure flag is now derived per-request (kept on real HTTPS, dropped on plain HTTP), so first-run access via http://<IP>:8080 no longer drops the cookie into an endless redirect.
  • WireGuard node-to-master 0 bit/s stall (#2): master wg0.conf installs FORWARD accept rules for wg0, so node-to-master traffic survives Docker's default FORWARD DROP policy.

v1.3.2 - client portal fixes, backend-server registry, port automation, WAF throughput

Choose a tag to compare

@marcoome marcoome released this 03 Jul 06:45

Fixed

  • Admin resellers 500: /admin/resellers no longer 500s for super-admins (a page-local Features field shadowed the nav-gating one).
  • Blank client route pages: /app/routes/{id}/edit and /app/routes/{id}/logs now render (missing layout dispatch branch).
  • WAF ingest throughput: 500-event batches now resolve routes with one indexed query and commit in one transaction per batch, ending the 30s-timeout 503 loop seen in production.

Added

  • Backend-server registry at /admin/servers (name + IP + external ref, reseller-scoped) with a dropdown in the service form.
  • Plan-driven port range: end port auto-fills from the plan's port count.
  • First-free-port pre-fill on the client new-route form.

Changed

  • Port-collision guards: reject overlapping service ranges on the same backend IP, and a route port already used in its service.
  • Client portal: removed the duplicate sidebar theme toggle; Cmd/Ctrl+K palette now works.

Deploy: rebuild the app image. Migration 00128 runs on start.

Full Changelog: v1.3.1...v1.3.2

v1.3.1 - WAF ingest loop fix + authoritative DNS ownership proof

Choose a tag to compare

@marcoome marcoome released this 02 Jul 21:57

Fixed

  • DNS domain-ownership proof: the panel now reads the _hpg-verify TXT record straight from the domain's authoritative nameservers instead of the container's default resolver. Fixes verification failing when the record is published and visible to external tools but the container's Docker DNS returns a stale/split-horizon answer, without weakening the proof. The A/CNAME serving check uses public resolvers for the same reason.
  • WAF event ingest loop: a backlog could get stuck re-shipping forever. Per-route pruning moved off the per-event hot path, the handler no longer aborts on client disconnect mid-batch, unattributed events are globally capped, and an incomplete batch returns a retryable error so no events are silently dropped. A concurrency cap sheds load under retry storms.
  • Node control-plane rate limiting: authenticated node endpoints (/api/node/*, /internal/*) are exempt from the unauthenticated-POST limit, so a busy node's WAF/access-log batches can no longer 429 themselves into a stall.

Removed

  • Client support/contact form: removed the in-app contact page and its mail path.

Full Changelog: v1.3.0...v1.3.1

v1.3.0 - reseller multi-tenancy, Terraform provider, lite stack

Choose a tag to compare

@marcoome marcoome released this 02 Jul 17:25

Reseller multi-tenancy

  • Resellers own a scoped set of clients (and optionally their own plans + branding); a reseller-admin sees only that reseller's clients, never platform-global infra or other resellers.
  • Explicit admin tiers: super_admin > unrestricted platform admin > reseller-admin (users.reseller_id) > client-scoped admin (users.is_restricted + admin_client_scope). Restriction is now an explicit opt-in flag, closing a scope-escalation footgun.
  • Reseller-scoped plans, self-service client/host/service/route/tunnel/stream provisioning, and per-reseller client-portal + status-page branding.
  • Suspend/resume is fail-closed: a suspended reseller-admin resolves to an empty scope (never platform-wide) and its live sessions are revoked immediately.
  • Super-admin management UI for reseller CRUD, client assignment, reseller-admin provisioning.

API & Terraform

  • Multi-tenant API v1 key scope: a reseller-admin key is transparently scoped to its own clients and cannot touch global infrastructure (nodes, pools, plans, client provisioning gated to platform admins).
  • Terraform provider (terraform-provider-hpg, in-repo nested module): hpg_node_pool, hpg_node, hpg_plan, hpg_client, hpg_service, hpg_route. Ships GoReleaser + release workflow for Terraform Registry publishing.

Deployment

  • Lite stack (deploy/docker-compose.lite.yml): stock Caddy with every edge module disabled, for installs that do not need WAF/GeoIP/L4/cache/rate-limit.

Security

  • Closed multiple reseller-boundary and IDOR gaps from adversarial review (scoped single-service, client status-slug, bulk admin handlers; plan-mutation restricted to unrestricted admins; L4 streams scoped to owner).
  • Fixed Terraform hpg_service delete (was an unregistered endpoint whose 404 was swallowed, leaving services live) and scoped ClientDelete so reseller-admins can destroy their own clients.

Full changelog: see CHANGELOG.md.

v1.2.1 - WAF events stop reappearing after clear

Choose a tag to compare

@marcoome marcoome released this 30 Jun 20:34

Fix: cleared WAF events no longer reappear

Symptom: after clearing events on /admin/waf, old events kept flooding back in.

Root cause: the node-agent mounts the Caddy log dir read-only, so its read-offset sidecar (.hpgpos) never persisted. Every node-agent restart re-read the entire Coraza audit log from the start and re-POSTed all history; the panel's blind INSERT re-added everything, resurrecting cleared events.

Panel - idempotent ingest

  • New waf_seen_events dedup ledger (migration 00119). Each event gets a per-node content hash; replays are dropped, not re-inserted.
  • "Clear events" never touches the ledger -> cleared events stay cleared, while genuinely new attacks still show.
  • Ledger capped to its newest 100k rows (above the 10k/route visible cap), pruned hourly.

Node-agent - durable offset (kills replay at the source)

  • New HPG_AGENT_STATE_DIR env: the offset sidecar now lives on a writable volume (the log volume stays read-only).
  • Offset write failures are now logged instead of failing silently.

Upgrade notes

  • Bundled stacks: pull the new app + node-agent images; the composes already add the node_agent_state volume + HPG_AGENT_STATE_DIR.
  • Custom stacks (e.g. Portainer): add to the hpg-node-agent service:
    • env HPG_AGENT_STATE_DIR=/var/lib/hpg-node-agent
    • a writable volume mounted at that path (e.g. node_agent_state:/var/lib/hpg-node-agent)
  • After upgrading: clear events once; they stay clear.

Full changelog: v1.2.0...v1.2.1

v1.2.0 - geo-block pages, log analytics fixes, production-ready L4 streams

Choose a tag to compare

@marcoome marcoome released this 29 Jun 20:51

Feature + reliability release on top of v1.1.0: customizable geo-block pages, host-log analytics fixes, and the L4 stream advanced options promoted to production-ready.

Features

  • Customizable geo-block page. When a host blocks a visitor by country or IP you can now control what they see: a branded page (title, message, logo, background colour) or a 302 redirect to a URL. Each client configures their own under /app -> Account -> Geo-block page; super-admins set a panel-wide default under Settings -> Branding -> Geo-block page (default). Saving re-pushes the affected nodes so the change is live immediately.
  • Geo-block response codes now behave correctly. 444 No Response aborts the connection (Nginx-style) instead of returning a body, and 403/503 show a reason matching the code (previously every code rendered "Forbidden").
  • Host access-log table: sorting + pagination. Sort by newest, errors-first, or status ascending, and page through the full history (50/page). The page links keep every active filter, including the date range.

Fixes

  • Empty per-host traffic-trend / error-rate charts. Time bucketing now uses TIMESTAMPDIFF(... ) DIV step instead of UNIX_TIMESTAMP/FLOOR, making it independent of the DB session timezone and immune to a decimal-scan edge case that left every bucket at zero.

L4 streams now production-ready

The advanced stream options (SNI / HTTP-host matching, load balancing, PROXY protocol, CIDR access control) are no longer marked experimental. The generated caddy-l4 config was verified against the module's actual schema and corrected:

  • PROXY-protocol in is emitted as a handler (not a listener wrapper); out is a plain "v1"/"v2" string.
  • CIDR access control uses the remote_ip matcher with terminal close routes (allow-list via not) - not a non-existent handler.
  • PROXY protocol is decoded before the CIDR ACL and SNI matchers (via a subroute) so the ACL evaluates the real client IP, not the upstream load balancer's address. This closes a potential access-control bypass when PROXY-in and a CIDR allow/deny list were combined.
  • The caddy-l4 module is pinned to v0.1.1 in the Caddy image for reproducible builds, and a /load fixture test (integration build tag) loads every generated shape into a real Caddy+caddy-l4.

UI

  • The host-edit page no longer jumps to the top after adding or removing a Basic-Auth user; scroll position is preserved across the reload.

Upgrade notes

  • Rebuild + restart the panel (migration 00118 adds the per-client geo-block columns) and rebuild/redeploy the Caddy image so the pinned caddy-l4@v0.1.1 and corrected stream config take effect.
  • If you use stream CIDR ACLs together with PROXY-protocol-in, re-save those streams after upgrading so the corrected ordering is pushed.

v1.1.0 - SQLite backend, UI improvements, WAF/geo/access-log fixes

Choose a tag to compare

@marcoome marcoome released this 29 Jun 18:44

Maintenance + feature release on top of v1.0.0: a new SQLite backend, several admin-UI improvements, and a batch of reliability/security fixes (WAF event handling, geo blocking, access-log analytics, and a public-repo domain cleanup).

Features

  • SQLite backend as an install-time option (DB_DRIVER=sqlite3, pure-Go modernc.org/sqlite, no separate service). Chosen in the install wizard or via env; intended for homelab/single-node. MySQL/MariaDB remains the default.
  • Alerts: super-admin "Clear log" button and full pagination over the alert log.
  • Host edit: tab navigation moved from a horizontal strip to a vertical sticky sidebar (two-column layout) that uses the wide space.
  • Geo tab: plain country/continent text inputs replaced with a searchable country checklist (flags + names) and a continent multi-select.
  • Hosts list: dense 11-column table collapsed into 6 grouped, stacked columns (host + backend, status, owner, traffic).
  • Navigation: "Access groups" added to the Security section; Statistics and Traffic map cross-linked. WAF events and access-logs pages are now full width.

Fixes

  • Geo IP/CIDR lists broke the whole node: entering allow/block IPs one-per-line stored them as a newline blob that was only split on commas, so the entire list reached Caddy as a single bogus remote_ip value and /load was rejected with 400 - the node ended up with no config at all (geo, hosts, everything down). The lists are now validated and normalized at save (a bad entry is surfaced to the operator, not silently dropped), and the config generator splits on any delimiter. Allow-list bypass entries fail closed (drop), while block-list entries are kept so an unparseable deny rule fails loudly instead of silently letting blocked traffic through.
  • WAF "Clear events" never stuck: the node agent kept its log read position only in memory, so every restart re-read the whole Coraza/access log from the start and re-sent the entire history (and the panel had no dedup). The agent now persists its read offset; restarts resume instead of replaying. On a fresh start it forwards the existing backlog once (set HPG_FORWARD_TAIL_ONLY=1 to skip it).
  • Geo blocking deny mode was inverted (it blocked every country except the one you listed). Deny now correctly blocks the listed countries. Continent selection no longer emits unsupported matcher keys (which could reject Caddy /load and take a node offline) - continents are expanded to their member countries panel-side. Allow-listed IPs/CIDRs bypass the block in both modes.
  • Incremental route pushes now gate WAF/Geo/Rate-limit by the node's probed module capability (matching a full /load), so a single-host save no longer silently drops those handlers or causes config-hash resync churn.
  • Access-logs charts were empty: the traffic-trend and error-rate series rendered all zeros because the DB session timezone was not pinned. The connection is now fixed to UTC so time-bucketed queries match the stored UTC timestamps.
  • Audit and WAF: the "Clear log" button and pagination controls now render reliably even when the count query is slow or fails.

Housekeeping

  • Documentation, code comments, the OpenAPI spec, templates and tests now use generic example.com placeholders. OTP emails fall back to the brand name as text when no logo URL is configured.

Upgrade notes

  • Rebuild + restart the panel, and redeploy the node agent, for the WAF, geo, and access-log fixes to take effect. After upgrading, re-push affected host configs (Save changes / rebuild node config) so the corrected geo rules reach the nodes.
  • Geo deny behavior changed: hosts using deny mode will now actually block the listed countries. Add trusted IPs to the geo allow-list before enabling, to avoid locking yourself out.
  • If a host's geo allow/block IP list was saved one-per-line before this release, open the host and re-save it once so the list is normalized and re-pushed correctly.

v1.0.0 - First stable release

Choose a tag to compare

@marcoome marcoome released this 28 Jun 20:01

Hostyt Proxy Gateway v1.0.0

Self-hosted control panel for a fleet of Caddy reverse-proxy nodes. Multi-tenant, WireGuard-meshed, full security stack. Single Go binary — ~21 MB image, ~28 MB idle RAM.


Screenshots

Install wizard Admin dashboard
Install wizard Dashboard
Host list Stats
Hosts Stats
World map AI assistant
World map AI chat

Full install wizard walkthrough: docs/install_video/install_wizard.webm


Quick start

git clone https://github.com/host-yt/caddy-proxy-manager.git hostyt-proxy-gateway
cd hostyt-proxy-gateway
cp .env.example .env
# edit .env: APP_URL, APP_SECRET (openssl rand -hex 32), DB_PASSWORD
docker compose -f deploy/docker-compose.yml --env-file .env up -d

Open http://localhost:8080 — complete the install wizard — sign in.


What's in v1.0.0

Auth & Access

  • Argon2id passwords, brute-force lockout (Redis-backed)
  • 2FA: TOTP, Email OTP, SMS OTP, WebAuthn/passkeys
  • OIDC (Authentik / Microsoft / generic), OAuth2 (GitHub, Google)
  • Multi-provider CAPTCHA: Turnstile, hCaptcha, reCAPTCHA v3
  • API keys with per-key RPM cap; auditable client impersonation
  • Scoped admin access; RequireRole + scopeCheckRoute before every write

Proxy

  • LB: round_robin, least_conn, ip_hash, uri_hash, header, cookie (HMAC)
  • Active + passive health checks, per-route timeouts, multi-upstream
  • WAF (Coraza/OWASP CRS): detection + blocking, rule suppression, event pipeline
  • GeoIP (MaxMind GeoLite2): country/continent allow-deny, fail-closed, CIDR lists
  • mTLS: per-tenant CA, client cert issue/revoke, path-based RBAC
  • L4 TCP/UDP streams, HTTP cache (Souin), per-route rate limiting
  • On-demand TLS, DNS-01 wildcard ACME, manual cert import, ZeroSSL
  • SSO forward-auth, built-in portal, HTTP basic auth, IP access lists, maintenance mode

WireGuard

  • L3 mesh — one-command node auto-join (curl | sudo bash)
  • WireGuard-over-WSS (wstunnel) for CGNAT traversal
  • Key rotation scheduler, nftables verification via node-agent

AI Assistant

  • Providers: Anthropic, OpenAI, Gemini, OpenRouter (streaming SSE)
  • Scoped read-only tool-calling — admin vs client, cross-tenant isolation
  • Floating bubble, conversation history, per-user rate limit

Monitoring

  • Prometheus poller (60 s), hourly log rollups, 14-day retention
  • World traffic map (country-level heatmap, both roles)
  • KPI cards, Chart.js charts, per-node traffic table, top clients

Multi-tenancy

  • Plans with max_domains / port / RPM quotas, restricted + npm types
  • Customer portal (/app): route CRUD, DNS check, SSL retry, 2FA, API keys
  • Client self-registration, custom fields, host groups, tags

REST API v1

  • Services, routes, nodes, health; idempotency keys; per-key RPM
  • FOSSBilling provisioning integration

Deployment

  • 4 install profiles: homelab / smallteam / advanced / provider
  • Docker Compose: app + MariaDB 11 + Redis 7 + Caddy + node-agent + WG sidecar
  • 117 goose migrations, auto-applied on boot; out-of-order apply supported
  • Distroless nonroot binary

UI

  • Dark-ops console: teal accent, command palette (Cmd+K), right-sheet drawer
  • Dark / light mode, Inter font, 40+ admin templates, 13 client portal templates

Security fixes in this release

  • IDOR protections on tunnels, bandwidth, host logs, exports, live streams
  • SSRF-guarded HTTP client on OIDC discovery / JWKS / token endpoints
  • CSP nonce enforced; inline handlers replaced with delegated data-action
  • Stored-XSS in custom-field list patched
  • API key plaintext never returned via redirect
  • AES-256-GCM encryption for all secrets at rest (_enc column pattern)

Full details: CHANGELOG.md · SECURITY.md