Webhook Outbound Policy
Bugsink supports sending alerts via webhooks (Slack, Discord, Mattermost). These integrations work by making an HTTP request to a configured URL.
That URL is user-provided. (by the person configuring the project-level settings)
In most cases this is exactly what you want: send alerts to a chat system or other external service. But it also means Bugsink will make outbound requests to whatever destination is configured.
In practice, the impact is limited (the request format is fixed), but it is still possible to point a webhook at places you did not intend, such as:
- internal services (
localhost,10.x.x.x) - cloud metadata endpoints
- other services reachable from your network
For that reason, Bugsink applies a set of checks to outbound webhook requests.
What Bugsink does
When a webhook is configured or sent, Bugsink:
- validates the URL format
- resolves the hostname to IP addresses
- checks those IPs against configured rules
- repeats the check at send time (DNS can change)
Redirects are disabled.
The checks apply to both hostnames and direct IPs.
Modes
Bugsink supports two modes for outbound webhook policy:
- a default-open mode
- a default-deny mode
ALERTS_WEBHOOK_OUTBOUND_MODE
Allowed values:
openallowlist_only
Semantics:
open: allow by default, then apply deny rulesallowlist_only: deny by default, only allow listed destinations
For most setups, this is the only setting you need to consider.
Allow and deny rules
In addition to the mode, Bugsink lets you define explicit rules about which destinations are allowed or denied.
These rules work on two levels:
- the hostname in the URL
- the IP addresses the hostname resolves to
This matters because a hostname that looks safe can still resolve to an unexpected address.
There are two types of rules:
- allow rules: explicitly permit certain destinations
- deny rules: explicitly block certain destinations
Deny rules always take precedence.
Settings
ALERTS_WEBHOOK_ALLOW_LISTALERTS_WEBHOOK_DENY_LIST
Entries can be:
- hostnames (
hooks.slack.com) - IP addresses (
203.0.113.10) - CIDR ranges (
203.0.113.0/24)
Notes:
- hostnames match the URL hostname
- IPs/CIDRs are matched against resolved addresses
- if both allow and deny match, deny wins
- full URLs are not accepted
Blocking internal addresses
By default, Bugsink also blocks non-public addresses.
ALERTS_WEBHOOK_DENY_NON_GLOBAL(default:True)
When enabled, Bugsink rejects destinations that resolve to non-public addresses
(e.g. localhost, 10.x.x.x, link-local ranges).
This applies even in open mode.
Recommended setups
Default-safe
ALERTS_WEBHOOK_OUTBOUND_MODE = "open" ALERTS_WEBHOOK_DENY_NON_GLOBAL = TrueALERTS_WEBHOOK_OUTBOUND_MODE = "open" ALERTS_WEBHOOK_DENY_NON_GLOBAL = True
Allows normal public webhooks, blocks internal targets.
Strict
ALERTS_WEBHOOK_OUTBOUND_MODE = "allowlist_only" ALERTS_WEBHOOK_ALLOW_LIST = ["hooks.slack.com", "discord.com"]ALERTS_WEBHOOK_OUTBOUND_MODE = "allowlist_only" ALERTS_WEBHOOK_ALLOW_LIST = ["hooks.slack.com", "discord.com"]
Only allows explicitly listed destinations.
Example configuration
local.py
BUGSINK = { "ALERTS_WEBHOOK_OUTBOUND_MODE": "allowlist_only", "ALERTS_WEBHOOK_ALLOW_LIST": [ "hooks.slack.com", "discord.com", "203.0.113.0/24", ], "ALERTS_WEBHOOK_DENY_LIST": [ "bad.example.com", "198.51.100.0/24", ], "ALERTS_WEBHOOK_DENY_NON_GLOBAL": True, }BUGSINK = { "ALERTS_WEBHOOK_OUTBOUND_MODE": "allowlist_only", "ALERTS_WEBHOOK_ALLOW_LIST": [ "hooks.slack.com", "discord.com", "203.0.113.0/24", ], "ALERTS_WEBHOOK_DENY_LIST": [ "bad.example.com", "198.51.100.0/24", ], "ALERTS_WEBHOOK_DENY_NON_GLOBAL": True, }
Docker environment
ALERTS_WEBHOOK_OUTBOUND_MODE=allowlist_only ALERTS_WEBHOOK_ALLOW_LIST=hooks.slack.com,discord.com,203.0.113.0/24 ALERTS_WEBHOOK_DENY_LIST=bad.example.com,198.51.100.0/24 ALERTS_WEBHOOK_DENY_NON_GLOBAL=trueALERTS_WEBHOOK_OUTBOUND_MODE=allowlist_only ALERTS_WEBHOOK_ALLOW_LIST=hooks.slack.com,discord.com,203.0.113.0/24 ALERTS_WEBHOOK_DENY_LIST=bad.example.com,198.51.100.0/24 ALERTS_WEBHOOK_DENY_NON_GLOBAL=true
Notes
Validation happens both when saving configuration and when sending.
A webhook that was valid earlier may be rejected later if DNS starts resolving to blocked addresses. That is intentional.
