Skip to content

call_webhook SSRF blocklist covers 100.64.0.0/10 (CGNAT), making webhooks unusable for Tailscale-based self-hosters #6500

Description

@WifiDan

Summary

Workflow call_webhook runs SSRF protection that rejects private and reserved address ranges, including 100.64.0.0/10. That range is CGNAT, which is what Tailscale assigns to every node. The practical effect is that a self-hosted relay physically cannot reach any service on the operator's own tailnet, so call_webhook is unusable for a whole class of deployment rather than merely restricted.

To be clear up front: the SSRF policy itself is correct and I'm not asking for it to be weakened. #4884 discusses the same protection for local commands and treats the policy as right, which I agree with. The ask is for an operator-controlled allowlist.

Why CGNAT is different from the other blocked ranges

Blocking RFC1918 and loopback is uncontroversial — those are ambient, attacker-guessable, and mean different things on every host. 100.64.0.0/10 in a Tailscale deployment is not ambient: addresses are assigned by a coordination server, reachable only inside an authenticated mesh the operator controls, and stable per-node.

For self-hosters, that range is the internal network. Tailscale is probably the single most common way people wire self-hosted services together now. So this isn't "webhooks are restricted in some edge case" — for a tailnet-based deployment, call_webhook cannot reach anything the operator actually runs.

Made worse by the failure being silent

#5122 reports call_webhook steps never delivering with no error surfaced, and names restricted egress as one cause. Combined, an operator on a tailnet gets a workflow step that silently does nothing. Nothing in the UI or logs says "this destination is blocked by SSRF policy," so the natural conclusion is that webhooks are broken, not that they're deliberately refusing.

Even without any policy change, surfacing "destination blocked by SSRF policy" as the step's error would save people a lot of time.

What would help

  1. An operator-configurable destination allowlist — an env var of CIDRs or hostnames that check_ssrf permits. Nothing exists for this in .env.example today. Defaulting to empty keeps current behaviour for everyone who doesn't set it, so it's opt-in and the safe default is unchanged.
  2. Failing loudly — report a blocked destination as an explicit error on the workflow step rather than a silent non-delivery, per [Bug] Workflow call_webhook action never delivers — silent, no error, no run record #5122.

Option 1 alone would unblock this entirely for self-hosters, and it puts the decision with the person who owns both ends of the connection.

Possibly related

Environment

Self-hosted buzz-relay (ghcr.io/block/buzz:main, image built 2026-08-09). All internal services reachable only over Tailscale (100.64.0.0/10).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions