Skip to content

fix: add webhook reverse proxy to gateway for tunnel support - #768

Closed
lighterEB wants to merge 7 commits into
nearai:stagingfrom
lighterEB:fix/tunnel-webhook-port
Closed

lighterEB wants to merge 7 commits into
nearai:stagingfrom
lighterEB:fix/tunnel-webhook-port

Conversation

@lighterEB

Copy link
Copy Markdown
Contributor

Summary

Closes #738.

  • Add reverse proxy in GatewayChannel to forward /webhook/* requests to WebhookServer (port 8080)
  • Tunnel now correctly routes webhook requests through gateway (port 3000) instead of returning 404
  • Preserve request headers and body when proxying to webhook server
  • Return 503 when webhook proxy is not configured instead of crashing

Changes

File What
src/channels/web/server.rs Add webhook_proxy_addr field to GatewayState; add webhook_proxy_handler and /webhook/{*path} route; add unit tests
src/channels/web/mod.rs Add webhook_proxy_addr to state initialization; add with_webhook_proxy() builder method
src/main.rs Save webhook server address and pass to GatewayChannel
src/channels/web/test_helpers.rs Add webhook_proxy_addr to test state
src/channels/web/ws.rs Add webhook_proxy_addr to test state

Test plan

  • cargo check — compiles
  • cargo test --lib — all tests pass
  • cargo test --lib web::server::tests::test_webhook — webhook proxy tests pass
  • Manual: tunnel correctly forwards /webhook/slack to webhook server on port 8080

lighterEB and others added 3 commits March 8, 2026 23:08
Gateway now proxies /webhook/* requests to WebhookServer (port 8080),
allowing external webhooks via ngrok tunnel (bound to gateway port 3000)
to reach webhook endpoints correctly.

- Add webhook_proxy_addr field to GatewayState
- Add webhook_proxy_handler for reverse proxying requests
- Add with_webhook_proxy() method to GatewayChannel
- Safe error handling without unwrap()
- Add unit tests for webhook proxy functionality

Fixes nearai#738
@github-actions github-actions Bot added scope: channel/web Web gateway channel size: L 200-499 changed lines risk: medium Business logic, config, or moderate-risk modules contributor: regular 2-5 merged PRs labels Mar 9, 2026
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a crucial enhancement to the system's networking capabilities by integrating a reverse proxy within the Gateway. This change specifically addresses issues where webhook requests, originating from a tunnel, failed to reach the dedicated WebhookServer. By establishing a clear forwarding mechanism, the system now reliably routes these requests, preventing service disruptions and improving the overall robustness of webhook handling.

Highlights

  • Webhook Reverse Proxy: Implemented a reverse proxy in the GatewayChannel to forward /webhook/* requests to the WebhookServer (port 8080).
  • Tunnel Webhook Routing: Ensured the tunnel correctly routes webhook requests through the gateway (port 3000), resolving previous 404 errors.
  • Request Preservation: Preserved original request headers and body during the proxying process.
  • Error Handling: Added graceful error handling, returning a 503 status code when the webhook proxy is requested but not configured.
Changelog
  • src/channels/web/mod.rs
    • Added webhook_proxy_addr field to GatewayState initialization.
    • Implemented with_webhook_proxy() builder method to set the webhook server address.
  • src/channels/web/server.rs
    • Included webhook_proxy_addr in the GatewayState struct.
    • Registered /webhook/{*path} route for both GET and POST requests.
    • Implemented webhook_proxy_handler to manage the forwarding of webhook requests, including header and body preservation, and error handling for unconfigured proxies.
    • Added new unit tests to verify webhook proxy functionality, including successful forwarding and 503 error for unconfigured proxy.
  • src/channels/web/test_helpers.rs
    • Added webhook_proxy_addr to the TestGatewayBuilder initialization.
  • src/channels/web/ws.rs
    • Added webhook_proxy_addr to the GatewayState initialization within test contexts.
  • src/main.rs
    • Modified the webhook server startup logic to store its final address.
    • Passed the webhook server's address to the GatewayChannel using the new with_webhook_proxy method.
Activity
  • No specific activity (comments, reviews, progress) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a reverse proxy to the gateway for webhook requests, significantly improving tunnel support. However, a critical path traversal vulnerability was identified in the webhook_proxy_handler due to unsanitized user input being used to construct the proxy destination URI, which could allow access to unintended internal endpoints. Additionally, there are opportunities to enhance the performance and robustness of the new proxy handler and reduce code duplication in the tests for better maintainability.

Comment thread src/channels/web/server.rs
Comment thread src/channels/web/server.rs Outdated
Comment thread src/channels/web/server.rs Outdated
Comment thread src/channels/web/server.rs Outdated
- Add path traversal protection (block '..' in path)
- Use static reqwest::Client for connection pool reuse
- Handle resp_builder error instead of unwrap()
- Extract test helper function for GatewayState
Add missing webhook_proxy_addr field to GatewayState in:
- tests/openai_compat_integration.rs (2 locations)
- tests/ws_gateway_integration.rs

@zmanian zmanian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review

Adds a reverse proxy on the gateway at `/webhook/{*path}` that forwards requests to the webhook server (typically port 8080), enabling tunnel providers (bound to the gateway port) to reach webhook endpoints.

Implementation

  • `webhook_proxy_handler` reads the body (up to 15MB), copies headers (except Host), and forwards via a static `reqwest::Client`.
  • Path traversal check: `path.contains("..")`
  • `webhook_proxy_addr: Option` added to `GatewayState`, set via `with_webhook_proxy()` builder method.
  • Returns 503 when not configured, 502 on upstream failure.

Concerns

  1. Same concern as PR #778 (which includes this): `path.contains("..")` may miss URL-encoded traversal. Since the path comes from Axum's router which decodes, this is likely fine, but document the assumption.

  2. Body buffering: Reads the entire request body into memory before forwarding. For webhook payloads this is fine (they're small), but the 15MB limit is generous. Consider lowering to 1MB (matching the gateway's `DefaultBodyLimit`).

  3. No auth forwarding awareness: The proxy forwards all headers except Host. If the webhook endpoint requires the gateway auth token, the proxy would need to inject it. Currently the webhook endpoints use their own per-channel secrets, so this is probably fine.

  4. Overlap with PR #778: This change appears to be a subset of PR #778 (the larger refactor). Are these coordinated? If #778 merges first, this PR may need rebasing or closing.

Tests are thorough -- proxy forwarding, 503 when unconfigured, different paths.

@lighterEB

lighterEB commented Mar 10, 2026 •

Copy link
Copy Markdown
Contributor Author

Review

Adds a reverse proxy on the gateway at `/webhook/{*path}` that forwards requests to the webhook server (typically port 8080), enabling tunnel providers (bound to the gateway port) to reach webhook endpoints.

Implementation

  • `webhook_proxy_handler` reads the body (up to 15MB), copies headers (except Host), and forwards via a static `reqwest::Client`.
  • Path traversal check: `path.contains("..")`
  • `webhook_proxy_addr: Option` added to `GatewayState`, set via `with_webhook_proxy()` builder method.
  • Returns 503 when not configured, 502 on upstream failure.

Concerns

  1. Same concern as PR refactor: encapsulate leaked abstractions into owning modules #778 (which includes this): `path.contains("..")` may miss URL-encoded traversal. Since the path comes from Axum's router which decodes, this is likely fine, but document the assumption.

  2. Body buffering: Reads the entire request body into memory before forwarding. For webhook payloads this is fine (they're small), but the 15MB limit is generous. Consider lowering to 1MB (matching the gateway's `DefaultBodyLimit`).

  3. No auth forwarding awareness: The proxy forwards all headers except Host. If the webhook endpoint requires the gateway auth token, the proxy would need to inject it. Currently the webhook endpoints use their own per-channel secrets, so this is probably fine.

  4. Overlap with PR refactor: encapsulate leaked abstractions into owning modules #778: This change appears to be a subset of PR refactor: encapsulate leaked abstractions into owning modules #778 (the larger refactor). Are these coordinated? If refactor: encapsulate leaked abstractions into owning modules #778 merges first, this PR may need rebasing or closing.

Tests are thorough -- proxy forwarding, 503 when unconfigured, different paths.

@zmanian @ilblackdragon — Thanks for the thorough reviews. After looking at #778, I can see it includes the same webhook proxy implementation and also moves the initialization into the module-owned factory pattern, which is cleaner than my approach of passing the address through main.rs.
I’m happy to close this PR once #778 merges, since it supersedes #768 with a better architecture. Let me know if there’s anything useful I can salvage from this (e.g. the unit tests in server.rs) that #778 might want to incorporate.

@lighterEB

Copy link
Copy Markdown
Contributor Author

I'd like to pause and discuss the broader architectural implications before this PR gets reviewed/merged.

While implementing this fix, I realized there's an underlying architectural question that might need clarification from the maintainers.

Gateway (port 3000)        WebhookServer (port 8080)
- Web UI (/api/*, /ws)     - /webhook/*
- Reverse proxy (this PR)  - WASM channel routes
        ↑
    Tunnel binds here

My Question:

The tunnel currently binds to Gateway (3000), but webhooks are handled by WebhookServer (8080). This mismatch caused #738, which this PR addresses with a reverse proxy.

But I'm wondering: what was the original design intent?

  1. Was the tunnel meant primarily for remote Web UI access?
  2. Or was it meant for webhook exposure?
  3. Why separate Gateway and WebhookServer in the first place?
  4. Is the reverse proxy approach the intended solution, or was binding to 8080 considered but rejected for some reason?

Alternative approaches I considered:

  • Bind tunnel to 8080 directly → webhooks work, but no remote Web UI
  • Make tunnel port configurable → users choose based on their needs
  • Keep current design + reverse proxy → what this PR does

I went with the reverse proxy approach because it enables both use cases (remote Web UI + webhooks) with a single tunnel, which seems practical. But I want to make sure this aligns with the project's long-term vision before committing to this architecture.

@ilblackdragon @zmanian

@zmanian

zmanian commented Mar 12, 2026

Copy link
Copy Markdown
Collaborator

Closing — superseded by #778 (merged).

@zmanian zmanian closed this Mar 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: regular 2-5 merged PRs risk: medium Business logic, config, or moderate-risk modules scope: channel/web Web gateway channel size: L 200-499 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Managed Tunnel binds to Web Gateway port (3000) instead of Webhook Server port (8080), causing all external webhook channels to return 404

2 participants