fix(egress): allowlist the e2e/staging webhook callback host (A1)#113
Merged
Merged
Conversation
The SSRF egress guard (audit_28_06_26.md #2) resolves every webhook/alert target and rejects any host resolving to a private/loopback/link-local address. The E2E and Staging deployments deliver the test webhook to the host gateway (host.docker.internal) / the in-pod loopback relay (127.0.0.1), both of which the guard correctly rejects — so POST /v1/webhooks returned 400 and tests/e2e/test_smoke.py::test_webhook_test_endpoint_delivers_callback failed in *both* the E2E Tests and Staging Deploy workflows (not required checks, so main went red unnoticed). Add an opt-in allowlist, AGENTFLOW_EGRESS_ALLOWED_HOSTS (comma-separated, exact hostnames, case-insensitive, default empty). When the URL host is on the list the public-address check is waived; the http(s) scheme check still applies and production keeps the full guard because nothing sets the env. Wire the controlled callback host into each deployment that needs it: - docker-compose.e2e.yml -> the configured callback host (host.docker.internal) - scripts/k8s_staging_up.sh -> 127.0.0.1, alongside the loopback relay setup - tests/e2e/conftest.py -> local uvicorn (127.0.0.1) and compose-override paths Also add workflow_dispatch to staging-deploy.yml so the fix can be verified on a branch before merging (mirrors e2e.yml). Regression tests in tests/unit/test_egress_guard.py prove the allowlist permits the listed host, stays case-insensitive, still rejects bad schemes and non-listed private hosts, and that an empty allowlist preserves the loopback rejection. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DORA Metrics
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Fixes the SSRF egress guard vs E2E/staging webhook-callback collision — the single root cause of both the red E2E Tests and Staging Deploy workflows on
main(rubric item R1 ofroad-to-9.8).Root cause
The egress guard (
audit_28_06_26.md#2) resolves every webhook/alert target and rejects any host on a private/loopback/link-local address. The E2E and Staging suites deliver the test webhook to:host.docker.internal(the container's host gateway → private IP)127.0.0.1:18080(the in-pod loopback relay)Both are correctly rejected by the guard, so
POST /v1/webhooksreturned 400 andtests/e2e/test_smoke.py::test_webhook_test_endpoint_delivers_callbackfailed (assert 400 == 201). Neither workflow is a required check, somainwent red unnoticed.Fix
Opt-in allowlist
AGENTFLOW_EGRESS_ALLOWED_HOSTS(comma-separated, exact hostnames, case-insensitive, default empty). When the URL host is listed, the public-address check is waived; the http(s) scheme check still applies. Production keeps the full guard because nothing sets the env.Wired into exactly the deployments that need it:
docker-compose.e2e.yml→ the configured callback host (host.docker.internal)scripts/k8s_staging_up.sh→127.0.0.1, next to the loopback-relay setuptests/e2e/conftest.py→ local uvicorn (127.0.0.1) + compose-override pathsAdded
workflow_dispatchtostaging-deploy.yml(mirrorse2e.yml) so the deploy can be re-run manually.Verification
tests/unit/test_egress_guard.py: allowlist permits the listed host, is case-insensitive, still rejects bad schemes + non-listed private hosts, and an empty allowlist preserves loopback rejection.mainafter merge (itsworkflow_dispatchonly becomes available once on the default branch).🤖 Generated with Claude Code