Skip to content

fix: add WEBHOOK_ALLOWED_HOSTS allowlist for internal webhook targets#9078

Merged
sriramveeraghanta merged 3 commits into
canaryfrom
fix/webhook-allowed-hosts
May 14, 2026
Merged

fix: add WEBHOOK_ALLOWED_HOSTS allowlist for internal webhook targets#9078
sriramveeraghanta merged 3 commits into
canaryfrom
fix/webhook-allowed-hosts

Conversation

@sriramveeraghanta
Copy link
Copy Markdown
Member

@sriramveeraghanta sriramveeraghanta commented May 14, 2026

Summary

Ports webhook allowlist changes from the enterprise repo to community. PR #8884 dropped the implicit private-IP allow on self-managed deployments, which breaks integrations that register webhooks against internal service hostnames (docker-compose / k8s service DNS resolves to a private IP and is now rejected). Container service IPs are dynamic, so a CIDR allowlist isn't a practical fix.

  • Adds WEBHOOK_ALLOWED_HOSTS env var (comma-separated hostnames) that bypasses the IP-based SSRF check at validate_url. Matching is exact and case-insensitive — subdomains of an allowed host stay blocked. Bypassed hostnames skip the DNS lookup too, so allowlisting a name that isn't resolvable from the API container still works.
  • Adds WEBHOOK_DISALLOWED_DOMAINS env var (empty by default for self-hosted) replacing the previously hardcoded ["plane.so"] list. Allowlisted hosts also bypass this check so legitimate sibling services that share a parent domain with Plane can still receive webhooks.
  • Wires both into WebhookSerializer.
  • Exposes WEBHOOK_ALLOWED_HOSTS in aio/cli deployment env files and the cli docker-compose x-app-env.

Operator notes

Self-managed deployments that register integration webhooks against an internal service hostname must add it to the env:

WEBHOOK_ALLOWED_HOSTS=internal-service
# or e.g.
WEBHOOK_ALLOWED_HOSTS=internal-service,internal-service.namespace.svc.cluster.local

Test plan

  • WEBHOOK_ALLOWED_HOSTS=<host> → webhook with http://<host>:3000/... is accepted (was 400 after fix: replace IS_SELF_MANAGED with WEBHOOK_ALLOWED_IPS allowlist #8884).
  • Unset / empty WEBHOOK_ALLOWED_HOSTS → private-IP targets still rejected.
  • An allowed host's subdomain (e.g. http://attacker.<host>.internal/) is still rejected.
  • Public URLs still validated normally.
  • Unit tests pass: pytest apps/api/plane/tests/unit/bg_tasks/test_work_item_link_task.py -k TestValidateUrlAllowlist.

The IP-based allowlist alone isn't practical for containerised deployments
where service IPs are dynamic. Adds a hostname-based bypass for trusted
internal services (e.g. Silo via docker-compose / k8s service DNS) and
makes the previously hardcoded ["plane.so"] domain blocklist configurable
via WEBHOOK_DISALLOWED_DOMAINS.

- validate_url accepts allowed_hosts (exact, case-insensitive match;
  skips DNS lookup for trusted names)
- WebhookSerializer wires both settings through and lets allowlisted
  hosts bypass the disallowed-domain check
- Exposes WEBHOOK_ALLOWED_HOSTS in aio/cli deployment env files
Copilot AI review requested due to automatic review settings May 14, 2026 19:22
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 14, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dd0bb959-150c-41de-9f5a-6283819ab041

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/webhook-allowed-hosts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Ports an enterprise fix to community, adding a WEBHOOK_ALLOWED_HOSTS hostname allowlist (and replacing the previously hardcoded plane.so block with a configurable WEBHOOK_DISALLOWED_DOMAINS) so self-managed deployments can register webhooks against internal service hostnames (docker-compose / k8s service DNS) that resolve to dynamic private IPs.

Changes:

  • Adds allowed_hosts parameter to validate_url (exact, case-insensitive match; skips DNS lookup so unresolvable internal names work).
  • Adds WEBHOOK_ALLOWED_HOSTS and WEBHOOK_DISALLOWED_DOMAINS Django settings and wires them through WebhookSerializer (allowlisted hosts also bypass the disallowed-domain check).
  • Exposes WEBHOOK_ALLOWED_HOSTS (and WEBHOOK_ALLOWED_IPS) in aio/cli env files and the cli docker-compose x-app-env, plus unit tests for the new allowlist behavior.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
apps/api/plane/utils/ip_address.py Adds allowed_hosts bypass before DNS lookup.
apps/api/plane/settings/common.py Parses WEBHOOK_ALLOWED_HOSTS / WEBHOOK_DISALLOWED_DOMAINS env vars.
apps/api/plane/app/serializers/webhook.py Passes allowed_hosts to validate_url; uses configurable disallowed domains; bypasses domain check for allowlisted hosts.
apps/api/plane/tests/unit/bg_tasks/test_work_item_link_task.py Adds tests covering allowed-host bypass, case-insensitivity, DNS skip, exact-match enforcement, and empty allowlist.
deployments/cli/community/variables.env Documents and exposes WEBHOOK_ALLOWED_IPS / WEBHOOK_ALLOWED_HOSTS.
deployments/cli/community/docker-compose.yml Wires the two webhook env vars into x-app-env.
deployments/aio/community/variables.env Documents and exposes the two webhook env vars for the aio image.

Comment on lines +30 to +34
validate_url(
url,
allowed_ips=settings.WEBHOOK_ALLOWED_IPS,
allowed_hosts=settings.WEBHOOK_ALLOWED_HOSTS,
)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in aadc28a. The send-time re-validation in webhook_task.py now also passes allowed_hosts=settings.WEBHOOK_ALLOWED_HOSTS so allowlisted hostnames survive both creation and send.

@sriramveeraghanta sriramveeraghanta merged commit 761c999 into canary May 14, 2026
5 checks passed
@sriramveeraghanta sriramveeraghanta deleted the fix/webhook-allowed-hosts branch May 14, 2026 19:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants