block / block/buzz

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

Open
#6,500 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
32.7k
Forks
4.3k
Avg merge
1d 13h
Merged PRs (30d)
253

Description

## 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 #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

- #4884 — allowlisted local commands; cites `check_ssrf` rejecting private/reserved addresses (lines 745–766) and proposes out-of-band registration. Different remedy, same underlying tension.
- #5122 — `call_webhook` silently not delivering, restricted egress named as a cause.
- #4198 — private/VPN relay URLs rejected by mobile pairing validation. Different code path, same philosophy applied to self-hosters.

## 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`).

Contributor guide

Open the contributing guide

Research direction

Start at the call_webhook SSRF protection and the check_ssrf entry point, then inspect .env.example for existing configuration patterns. Add an opt-in destination allowlist with the current empty default and surface blocked destinations as explicit workflow-step errors; completion should preserve existing blocking behavior when unset.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
backend-api-design, security
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.