github kOlapsis/maintenant v1.3.6
SSRF in webhook subscriptions

6 hours ago

Security release: closes an unauthenticated SSRF in webhook subscriptions, hardens notification-channel delivery against internal targets, and fixes email-channel creation. Upgrading is recommended.

Security

SSRF in webhook subscriptions

Webhook subscription URLs were only checked for an https:// prefix, not for where they resolved. A POST /api/v1/webhooks could register a URL pointing at a private or internal address ? 127.0.0.1, the LAN, or the cloud metadata endpoint 169.254.169.254 ? and both the Send test action and real event dispatch would then issue the request. Because the API delegates authentication to the reverse proxy, this was reachable without authentication anywhere the port is exposed without the proxy in front. Notification channels already resolved and rejected private IPs; the webhook path never wired that check in.

The fix adds two layers:

  • Create-time validation rejects non-HTTPS URLs and hostnames that resolve to loopback, link-local, private (RFC 1918 / ULA), CGNAT (RFC 6598), unspecified, or multicast addresses.
  • A dial-time guard on the delivery client blocks those same ranges on the actual connection ? after DNS resolution and on every redirect hop ? so it also defeats DNS rebinding between validation and delivery. This is the real boundary, and it now covers both webhook subscriptions and notification channels (Slack / Teams / generic webhook).

Private targets remain available for local and self-hosted setups behind the existing MAINTENANT_ALLOW_PRIVATE_WEBHOOKS flag.

Thanks to Stephane Robert (@stephrobert) for reporting this issue.

Bug fixes

  • Email channels could not be created (Pro). The URL validation required https://, but email channels carry the recipient address in that field and deliver over SMTP ? so every email channel was rejected at create time. The address is now validated as an email, and the HTTPS / SSRF rule applies only to HTTP webhook channel types.
  • Channel updates skipped URL validation. PUT /api/v1/channels/{id} stored any URL unchecked, so an existing channel could be edited to a private or internal address the create path would reject. Update now runs the same validation whenever the URL or type changes.
  • Channel wizard swallowed errors. A rejected channel create (for example a non-HTTPS or private-IP URL returning 400) left the wizard on the config step with no feedback ? the button appeared to do nothing. The error message is now shown in the form.

Upgrade notes

  • No database migration and no configuration changes ? rebuild the image to upgrade.
  • Delivery to private or internal targets is now blocked. If you send webhooks or notifications to an endpoint on a private IP or an internal hostname (for example a self-hosted, Slack-compatible service on your LAN), set MAINTENANT_ALLOW_PRIVATE_WEBHOOKS=true to keep it working; otherwise those deliveries fail at dial time.
  • No API breaking changes.

Don't miss a new maintenant release

NewReleases is sending notifications on new releases.