Uh oh!
There was an error while loading. Please reload this page.
fix(passthrough): enforce the borrowed model's client-IP allowlist (#557 parity) - #704
Conversation
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
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe passthrough handler now captures client IP context via ChangesPassthrough IP Allowlist Enforcement
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
Possibly related PRs
🚥 Pre-merge checks | ✅ 5 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Uh oh!
There was an error while loading. Please reload this page.
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 carryallowed_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
ClientContextin the passthrough handler and callcheck_ip_accesson 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 403permission_deniedand the upstream is never contacted; injectingConnectInfo(10.1.2.3)passes through 200. Verified fail-before/pass-after.Fixes#697
Summary by CodeRabbit
403 Forbiddenbefore reaching the upstream service.