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:

  • open
  • allowlist_only

Semantics:

  • open: allow by default, then apply deny rules
  • allowlist_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_LIST
  • ALERTS_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.

Default-safe

ALERTS_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"]

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,
}

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=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.