0020: Webhooks run their own tunnels
On this page
Status: accepted
Context
Webhooks used to come in by sharing a whole site (0010). That ties a webhook's address to a site, offers no way to answer before the app exists, and leaves deliveries unchecked. Sharing's password can't protect a webhook, because providers can't type one, yet tunnel traffic is untrusted.
Decision
- Tunnels. Each webhook runs its own tunnel into the existing share listener.
- A random address is a quick tunnel (
webhook:<id>) whose Host header is an internal name,<id>.webhook.leash.invalid. - On your domain, a named webhook gets a CNAME for its name on the computer's one named tunnel.
- A webhook name may not equal a site name, because their domain addresses would collide.
- A random address is a quick tunnel (
- Access. Tunnel requests for a webhook's host keep the IP allowlist and skip only the share password.
- Signature check. Each delivery's signature is checked (
webhooks::signature; HMAC throughring, constant-time). Optionally, a delivery that fails is answered401before any site sees it. - Forwarding. Deliveries are forwarded to their sites through the proxy as local HTTPS requests, so they never trust forwarded headers.
- Storage. Signing secrets live in
secrets/webhooks.json(0600), as 0012 does for sharing. They never go inconfig.tomlor the API. Deliveries are kept in memory, the last 500 per webhook. Amended by 0029: deliveries are kept in Short Leash's database, the last 500 per webhook for the days chosen (14 by default), held ones until they're released.
Consequences
- A webhook's address no longer depends on sharing a site, and Short Leash can hold deliveries, answer for them, replay them and explain failed signatures.
- A webhook without a secret, or with rejection off, takes anything sent to its address. The screen marks such deliveries Not checked or Failed.
- Random addresses still change on restart; your domain is the fix.
- Every webhook on a random address is one more cloudflared process.