mirror of
https://github.com/anomalyco/opencode.git
synced 2026-10-21 10:50:37 +00:00
fix(console): use private inference IP handoff (#56427)
This commit is contained in:
parent
a801306ddf
commit
8b2aaaf55f
4 changed files with 78 additions and 4 deletions
|
|
@ -64,7 +64,12 @@ new sst.cloudflare.x.SolidStart("Console", {
|
|||
if (migrationDomain)
|
||||
args.bindings = $resolve(args.bindings).apply((bindings) => [
|
||||
...(bindings ?? []),
|
||||
{ type: "service", name: "INFERENCE", service: `opencode-inference-${$app.stage}` },
|
||||
{
|
||||
type: "service",
|
||||
name: "INFERENCE",
|
||||
service: `opencode-inference-${$app.stage}`,
|
||||
entrypoint: "LegacyIngress",
|
||||
},
|
||||
])
|
||||
},
|
||||
},
|
||||
|
|
|
|||
20
packages/console/app/src/lib/inference-ip.ts
Normal file
20
packages/console/app/src/lib/inference-ip.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// Only direct Cloudflare requests can supply visitor-IP evidence to the private inference binding.
|
||||
// Worker subrequests can derive CF-Connecting-IP from script-controlled X-Real-IP.
|
||||
export function inferenceHeaders(request: Request & { readonly cf?: unknown }) {
|
||||
const ip =
|
||||
request.cf !== undefined && !["cf-worker", "cf-ew-via", "x-real-ip"].some((name) => request.headers.has(name))
|
||||
? request.headers.get("cf-connecting-ip")
|
||||
: null
|
||||
const headers = new Headers(request.headers)
|
||||
for (const name of [
|
||||
"x-real-ip",
|
||||
"x-forwarded-for",
|
||||
"forwarded",
|
||||
"cf-connecting-ip",
|
||||
"cf-connecting-ipv6",
|
||||
"x-zen-ip",
|
||||
])
|
||||
headers.delete(name)
|
||||
if (ip) headers.set("x-real-ip", ip)
|
||||
return headers
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { Resource } from "@opencode/console-resource"
|
||||
import { inferenceHeaders } from "./inference-ip"
|
||||
|
||||
const paths: Record<string, string | undefined> = {
|
||||
"POST /zen/v1/chat/completions": "/openai/v1/chat/completions",
|
||||
|
|
@ -36,14 +37,12 @@ export async function proxyInference(request: Request) {
|
|||
destination.search = url.search
|
||||
destination.hash = ""
|
||||
|
||||
const forwarded = new Request(destination, request)
|
||||
const forwarded = new Request(destination, new Request(request, { headers: inferenceHeaders(request) }))
|
||||
for (const name of ["host", "content-length"]) forwarded.headers.delete(name)
|
||||
// Keyless requests must reach new inference without credentials; `public` is not an API key there.
|
||||
const anonymous = !key || key === "public"
|
||||
if (anonymous) for (const name of ["authorization", "x-api-key", "x-goog-api-key"]) forwarded.headers.delete(name)
|
||||
if (!anonymous) forwarded.headers.set("authorization", `Bearer ${key}`)
|
||||
const ip = request.headers.get("cf-connecting-ip")
|
||||
if (ip) forwarded.headers.set("x-real-ip", ip)
|
||||
const requestID = request.headers.get("x-opencode-request-id") ?? request.headers.get("x-opencode-request")
|
||||
if (requestID) forwarded.headers.set("x-opencode-request-id", requestID)
|
||||
|
||||
|
|
|
|||
50
packages/console/app/test/inferenceIp.test.ts
Normal file
50
packages/console/app/test/inferenceIp.test.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import { expect, test } from "bun:test"
|
||||
import { inferenceHeaders } from "../src/lib/inference-ip"
|
||||
|
||||
test.each(["203.0.113.10", "2001:db8::10"])("forwards only the direct edge address: %s", (ip) => {
|
||||
const request = Object.assign(
|
||||
new Request("https://opencode.ai/zen/v1/models", {
|
||||
headers: {
|
||||
"cf-connecting-ip": ip,
|
||||
"cf-connecting-ipv6": "2001:db8::bad",
|
||||
"x-forwarded-for": "198.51.100.1",
|
||||
forwarded: "for=198.51.100.1",
|
||||
"x-zen-ip": "198.51.100.1",
|
||||
authorization: "Bearer test-only",
|
||||
},
|
||||
}),
|
||||
{ cf: { colo: "TEST" } },
|
||||
)
|
||||
expect([...inferenceHeaders(request)]).toEqual([
|
||||
["authorization", "Bearer test-only"],
|
||||
["x-real-ip", ip],
|
||||
])
|
||||
})
|
||||
|
||||
test.each(["cf-worker", "cf-ew-via", "x-real-ip"])("rejects relay evidence: %s", (name) => {
|
||||
const request = Object.assign(
|
||||
new Request("https://opencode.ai/zen/v1/models", {
|
||||
headers: { "cf-connecting-ip": "203.0.113.10", [name]: "spoofed" },
|
||||
}),
|
||||
{ cf: { colo: "TEST" } },
|
||||
)
|
||||
expect(inferenceHeaders(request).has("x-real-ip")).toBe(false)
|
||||
expect(inferenceHeaders(request).has("cf-connecting-ip")).toBe(false)
|
||||
})
|
||||
|
||||
test("missing runtime metadata cannot turn headers into trusted evidence", () => {
|
||||
const request = new Request("http://origin/zen/v1/models", {
|
||||
headers: { "cf-connecting-ip": "203.0.113.10" },
|
||||
})
|
||||
expect([...inferenceHeaders(request)]).toEqual([])
|
||||
})
|
||||
|
||||
test("missing edge IP strips claims rather than retaining a fallback", () => {
|
||||
const request = Object.assign(
|
||||
new Request("https://opencode.ai/zen/v1/models", {
|
||||
headers: { "x-forwarded-for": "198.51.100.1", "cf-connecting-ipv6": "2001:db8::1" },
|
||||
}),
|
||||
{ cf: { colo: "TEST" } },
|
||||
)
|
||||
expect([...inferenceHeaders(request)]).toEqual([])
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue