Reverse Proxy Caddy - sparkycz1/honeypot-shelf GitHub Wiki
🔒 Reverse proxy: Caddy
HTTPS with zero certificate ceremony — Caddy just handles it.
There are two ways to use Caddy with Honeypot Shelf:
- Bundled:
docker-compose.caddy.ymlin this repo runs Caddy for you, wired to thewebservice automatically. Use this unless you already run a reverse proxy on this host. - Standalone: you already run your own Caddy instance (other sites, or
you prefer managing it outside this repo). Point it at Honeypot Shelf's
127.0.0.1:8080(or whateverAPP_PORTyou set). Once working, consider settingAPP_BIND_ADDRESS=127.0.0.1too (nodocker-compose.ymledit needed), so the app is only reachable through this proxy.
📦 Option A — the bundled Caddy
Requirements
DOMAINandACME_EMAILset in.env.- DNS: an A (and/or AAAA) record for
DOMAINpointing at this host's public IP. - Ports
80/tcp,443/tcp, and443/udpopen and reachable from the internet:80/tcp— used for the ACME HTTP-01 challenge and to redirect plain HTTP to HTTPS.443/tcp— HTTPS (HTTP/1.1 and HTTP/2).443/udp— HTTP/3 (QUIC).
Run it
docker compose -f docker-compose.yml -f docker-compose.caddy.yml up -d --build
Caddy will automatically request and renew a certificate from Let's
Encrypt for DOMAIN, store it (and its ACME account state) in the
caddy_data named volume, and reverse-proxy everything to web:8080
over the internal Docker network.
What's configured (./Caddyfile)
- TLS 1.3 only —
tls { protocols tls1.3 tls1.3 }on the site block; TLS 1.2 and older are rejected outright. - HTTP/3 — enabled via the global
servers { protocols h1 h2 h3 }option. - HSTS —
Strict-Transport-Securitywith a two-year max-age. Thepreloaddirective is included; remove it until you've confirmed everything works correctly over HTTPS, since preload-list submission is hard to undo. - Hardened headers —
X-Content-Type-Options: nosniff,Referrer-Policy: no-referrer, and theServerheader is stripped. - Request/idle timeouts — conservative defaults under
servers { timeouts { ... } }. - 5MB request body cap — the app has no file-upload endpoint; the
largest legitimate body is a pasted runbook, SSH key, or a honeypot/
company config JSON/CSV import, all comfortably under that. Rejects an
oversized POST at the proxy, before it reaches
webat all.
Verifying it worked
curl -sIv https://your-domain.example.com/healthz 2>&1 | grep -Ei 'HTTP/|strict-transport|server:'
You should see HTTP/2 or HTTP/3 in the response line (curl needs HTTP/3
compiled in to show HTTP/3; otherwise it negotiates HTTP/2, still
correct), a 200 status, and the Strict-Transport-Security header. To
confirm HTTP/3 specifically:
curl --http3 -sI https://your-domain.example.com/healthz
To confirm only TLS 1.3 is accepted:
openssl s_client -connect your-domain.example.com:443 -tls1_2 </dev/null
# should fail to negotiate
openssl s_client -connect your-domain.example.com:443 -tls1_3 </dev/null
# should succeed
Troubleshooting
- Certificate not issued: check
docker compose logs caddy. Common causes: DNS not yet propagated, port 80 blocked by a firewall or another process already bound to it, orACME_EMAIL/DOMAINleft as the.env.exampleplaceholders. - Works on 443/tcp but not HTTP/3: check that
443/udpis actually open on any firewall/cloud security group in front of this host — it's easy to forget the UDP rule since most setups only think about TCP. - Local/LAN-only use, no public domain: this bundled config assumes a
public domain reachable by Let's Encrypt — see
Installation's
tls internalsteps for a LAN-only self-signed alternative (also needed to make WebAuthn/passkeys and the honeypot terminal's clipboard copy/paste work at all on a plain-HTTP LAN deployment, which browsers disable outright regardless of app config).
🔧 Option B — your own standalone Caddy instance
If you run Caddy separately (not via this repo's compose files), add a
site block pointing at wherever Honeypot Shelf's web service is reachable
from your Caddy host — typically 127.0.0.1:8080 if Caddy runs directly
on the same machine as docker compose up -d --build (the base file,
without docker-compose.caddy.yml):
your-domain.example.com {
tls {
protocols tls1.3 tls1.3
}
request_body {
max_size 5MB
}
header {
Strict-Transport-Security "max-age=63072000; includeSubDomains"
X-Content-Type-Options "nosniff"
Referrer-Policy "no-referrer"
-Server
}
reverse_proxy 127.0.0.1:8080 {
header_up X-Forwarded-Proto {scheme}
}
}
If your Caddy instance is itself a container in a different Compose
project, join Honeypot Shelf's Docker network so it can resolve web by
name instead of the published 8080 port — the base
docker-compose.yml publishes that port on every interface, so joining
the network is the more locked-down option.