Skip to content

fix(passthrough): enforce the borrowed model's client-IP allowlist (#557 parity) - #704

Merged
jarvis9443 merged 1 commit into
mainfrom
fix/issue-697-passthrough-ip
Jul 2, 2026
Merged

fix(passthrough): enforce the borrowed model's client-IP allowlist (#557 parity)#704
jarvis9443 merged 1 commit into
mainfrom
fix/issue-697-passthrough-ip

Conversation

@jarvis9443

@jarvis9443 jarvis9443 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Problem

Audit finding #697 (parent: api7/AISIX-Cloud#950). Every typed handler gates on dispatch::check_ip_access (#557), but the raw /passthrough/:provider/* tunnel resolved a model entry — which can carry allowed_cidrs — without checking it, and never resolved a client IP at all. A per-model IP restriction was bypassable by lending the same credentials through passthrough.

Fix

Extract ClientContext in the passthrough handler and call check_ip_access on the borrowed model right after the #449 ACL resolution — the same borrowed-model basis the #911 [6] guardrail resolution uses. Requests with no resolvable peer fail closed against a configured allowlist, matching the typed handlers.

LiteLLM has no per-model client-IP allowlist equivalent on its passthrough routes — no baseline to compare against; this restores internal parity with our own #557 feature.

Tests

Router-level: a model with allowed_cidrs: [10.0.0.0/8] → oneshot (no peer) gets 403 permission_denied and the upstream is never contacted; injecting ConnectInfo(10.1.2.3) passes through 200. Verified fail-before/pass-after.

Fixes #697

Summary by CodeRabbit

  • Bug Fixes
    • Requests now respect IP-based access rules for passthrough traffic.
    • If a client IP is not allowed, the request is rejected with 403 Forbidden before reaching the upstream service.
    • Requests from permitted IPs continue to work normally through the passthrough flow.

 parity)

Every typed handler gates on dispatch::check_ip_access, but the raw
/passthrough/:provider/* tunnel resolved a model entry (which can carry
allowed_cidrs) without ever checking it — and never resolved a client
IP at all. An operator's per-model IP restriction was bypassable by
lending the same credentials through passthrough.

Extract ClientContext in the handler and gate on the borrowed model's
allowed_cidrs right after ACL resolution — the same borrowed-model
basis as the #911 [6] guardrail chain. Oneshot/no-peer requests fail
closed against a configured allowlist, matching the typed handlers.

Fixes #697
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3212a58d-3c68-43d2-b24d-f5df89cf4cf1

📥 Commits

Reviewing files that changed from the base of the PR and between cb1bcd0 and a0637c4.

📒 Files selected for processing (1)
  • crates/aisix-proxy/src/passthrough.rs

📝 Walkthrough

Walkthrough

The passthrough handler now captures client IP context via ClientContext and threads source_ip into the internal dispatcher. The dispatcher enforces the resolved model's allowed_cidrs by calling check_ip_access before resolving provider credentials, returning 403 for disallowed IPs. Tests cover both outcomes.

Changes

Passthrough IP Allowlist Enforcement

Layer / File(s) Summary
Thread client IP through handler and dispatcher
crates/aisix-proxy/src/passthrough.rs
The passthrough handler signature gains a ClientContext parameter, passes client.source_ip to dispatch, and the internal dispatch function signature is extended with a source_ip: &str parameter.
Enforce allowlist check and add coverage
crates/aisix-proxy/src/passthrough.rs
dispatch calls check_ip_access(model, source_ip) after model resolution and before contacting upstream; new tests verify a 403 for disallowed client IPs and success for allowed IPs injected via ConnectInfo.

Estimated code review effort: 2 (Simple) | ~15 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant PassthroughHandler
  participant Dispatch
  participant Upstream

  Client->>PassthroughHandler: request with ConnectInfo
  PassthroughHandler->>PassthroughHandler: extract ClientContext.source_ip
  PassthroughHandler->>Dispatch: dispatch(..., source_ip)
  Dispatch->>Dispatch: check_ip_access(model, source_ip)
  alt IP not allowed
    Dispatch-->>PassthroughHandler: 403 Forbidden
  else IP allowed
    Dispatch->>Upstream: forward request
    Upstream-->>Dispatch: response
    Dispatch-->>PassthroughHandler: response
  end
Loading

Possibly related PRs

  • api7/aisix#516: Both PRs add and thread per-request client IP context via ClientContext/ConnectInfo, with this PR's passthrough handler using that source_ip for access gating.
🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
E2e Test Quality Review ⚠️ Warning The new checks are in-process router tests (Router::oneshot) with a WireMock upstream, so they don’t reach a true E2E boundary. Add at least one listener-backed end-to-end test (or clearly scope these as integration tests) that drives the running proxy and validates the full request path.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: enforcing passthrough client-IP allowlists.
Linked Issues check ✅ Passed The PR matches #697 by extracting client IP context and checking the model allowlist before passthrough dispatch.
Out of Scope Changes check ✅ Passed The changes stay focused on passthrough IP-allowlist enforcement and its tests, with no unrelated scope detected.
Security Check ✅ Passed No security issues: passthrough now enforces check_ip_access before dispatch, and tests confirm 403/no upstream for disallowed IPs and 200 for allowed IPs.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-697-passthrough-ip

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

@jarvis9443
jarvis9443 merged commit 99bba33 into main Jul 2, 2026
10 checks passed
@jarvis9443
jarvis9443 deleted the fix/issue-697-passthrough-ip branch July 2, 2026 13:44
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.

passthrough endpoints skip the per-model client-IP allowlist (#557 check_ip_access)

1 participant