diff --git a/apps/harness/pty-server.js b/apps/harness/pty-server.js index c682be3..3cbff7c 100644 --- a/apps/harness/pty-server.js +++ b/apps/harness/pty-server.js @@ -53,6 +53,7 @@ function attachPtyWebSocket(server) { try { const res = await fetch( `http://127.0.0.1:${port}/api/agents/${encodeURIComponent(agentId)}/env`, + { headers: { "x-internal-token": process.env.INTERNAL_API_TOKEN || "" } }, ); if (!res.ok) { const body = await res.json().catch(() => ({})); diff --git a/apps/harness/server.js b/apps/harness/server.js index 05884ec..fe8e92d 100644 --- a/apps/harness/server.js +++ b/apps/harness/server.js @@ -2,11 +2,16 @@ const { createServer } = require("http"); const path = require("path"); const { parse } = require("url"); +const crypto = require("crypto"); + const dev = process.env.NODE_ENV !== "production"; // HOSTNAME in K8s is the pod name — always bind to 0.0.0.0 const hostname = "0.0.0.0"; const port = parseInt(process.env.PORT || "3100", 10); +// Shared secret for internal PTY→API calls (generated per process) +process.env.INTERNAL_API_TOKEN = crypto.randomBytes(32).toString("hex"); + // In production, load the standalone config to avoid webpack dependency if (!dev) { try { diff --git a/apps/harness/src/app/api/agents/[id]/env/route.ts b/apps/harness/src/app/api/agents/[id]/env/route.ts index 1a9b7e3..23adad3 100644 --- a/apps/harness/src/app/api/agents/[id]/env/route.ts +++ b/apps/harness/src/app/api/agents/[id]/env/route.ts @@ -6,13 +6,9 @@ export async function GET( _request: NextRequest, { params }: { params: Promise<{ id: string }> }, ) { - // Only allow localhost access - const forwarded = _request.headers.get("x-forwarded-for"); - const host = _request.headers.get("host") ?? ""; - const isLocal = - !forwarded && - (host.startsWith("localhost") || host.startsWith("127.0.0.1")); - if (!isLocal) { + // Only allow internal calls from the PTY server (same process) + const token = _request.headers.get("x-internal-token"); + if (!token || token !== process.env.INTERNAL_API_TOKEN) { return NextResponse.json({ error: "forbidden" }, { status: 403 }); }