From 0dd1a6eac82e9bb4a6f8c37a66f18c8f77741a01 Mon Sep 17 00:00:00 2001 From: Codex Date: Sun, 21 Jun 2026 07:22:28 +0000 Subject: [PATCH] fix: align agentrun git mirror ssh proxy --- scripts/src/agentrun.ts | 51 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/scripts/src/agentrun.ts b/scripts/src/agentrun.ts index ea2eb0c1..d2630d7b 100644 --- a/scripts/src/agentrun.ts +++ b/scripts/src/agentrun.ts @@ -4173,41 +4173,80 @@ function yamlLaneGitMirrorJobManifest(spec: AgentRunLaneSpec, action: "sync" | " } function yamlLaneGitMirrorSshSetupShellLines(spec: AgentRunLaneSpec): string[] { + const proxyHost = spec.gitMirror.githubProxy.host; + const proxyPort = spec.gitMirror.githubProxy.port; + const proxyUrl = `http://${proxyHost}:${proxyPort}`; + const proxySummary = `agentrun git-mirror-egress-proxy node=${spec.nodeId} lane=${spec.lane} host=${proxyHost} port=${proxyPort} ssh=GIT_SSH-wrapper source=yaml`; + const proxyCommand = `ProxyCommand=node /tmp/agentrun-github-proxy-connect.cjs ${proxyHost} ${proxyPort} %h %p`; return [ "mkdir -p /root/.ssh", "cp /git-ssh/ssh-privatekey /root/.ssh/id_rsa", "chmod 0400 /root/.ssh/id_rsa", + `printf '%s\\n' ${shQuote(proxySummary)} >&2`, "cat > /tmp/agentrun-github-proxy-connect.cjs <<'NODE_PROXY'", + "#!/usr/bin/env node", "const net = require('node:net');", "const [proxyHost, proxyPortRaw, targetHost, targetPortRaw] = process.argv.slice(2);", "const proxyPort = Number.parseInt(proxyPortRaw || '', 10);", "const targetPort = Number.parseInt(targetPortRaw || '', 10);", - "if (!proxyHost || !Number.isInteger(proxyPort) || !targetHost || !Number.isInteger(targetPort)) process.exit(64);", + "if (!proxyHost || !Number.isInteger(proxyPort) || !targetHost || !Number.isInteger(targetPort)) {", + " console.error('agentrun git-mirror proxy-connect: invalid ProxyCommand arguments');", + " process.exit(64);", + "}", + "let settled = false;", + "let tunnelEstablished = false;", + "function finish(code, message) {", + " if (settled) return;", + " settled = true;", + " if (message) console.error('agentrun git-mirror proxy-connect: ' + message);", + " process.exit(code);", + "}", "const socket = net.createConnection({ host: proxyHost, port: proxyPort });", "let buffer = Buffer.alloc(0);", - "socket.setTimeout(10000, () => { socket.destroy(); process.exit(65); });", + "socket.setTimeout(15000, () => { socket.destroy(); finish(65, 'timeout connecting via ' + proxyHost + ':' + proxyPort + ' to ' + targetHost + ':' + targetPort); });", "socket.on('connect', () => socket.write('CONNECT ' + targetHost + ':' + targetPort + ' HTTP/1.1\\r\\nHost: ' + targetHost + ':' + targetPort + '\\r\\nProxy-Connection: Keep-Alive\\r\\n\\r\\n'));", - "socket.on('error', () => process.exit(66));", + "socket.on('error', (error) => finish(tunnelEstablished ? 69 : 66, (tunnelEstablished ? 'tunnel socket error: ' : 'tcp error connecting to proxy: ') + (error && error.message ? error.message : String(error))));", + "socket.on('close', () => { if (!tunnelEstablished) finish(68, 'proxy closed before CONNECT completed via ' + proxyHost + ':' + proxyPort + ' to ' + targetHost + ':' + targetPort); else finish(0); });", "function onData(chunk) {", " buffer = Buffer.concat([buffer, chunk]);", " const headerEnd = buffer.indexOf('\\r\\n\\r\\n');", " if (headerEnd === -1 && buffer.length < 8192) return;", + " if (headerEnd === -1) { socket.destroy(); finish(68, 'proxy response header exceeded 8192 bytes before CONNECT status via ' + proxyHost + ':' + proxyPort + ' to ' + targetHost + ':' + targetPort); return; }", " const head = buffer.slice(0, headerEnd + 4).toString('latin1');", " const statusLine = head.split('\\r\\n', 1)[0] || '';", " const statusCode = Number.parseInt(statusLine.split(' ')[1] || '', 10);", - " if (!statusLine.startsWith('HTTP/1.') || !Number.isInteger(statusCode) || statusCode < 200 || statusCode > 299) { socket.destroy(); process.exit(67); }", + " if (!statusLine.startsWith('HTTP/1.') || !Number.isInteger(statusCode) || statusCode < 200 || statusCode > 299) {", + " const safeStatus = statusLine.replace(/[^\\x20-\\x7e]/g, '?').slice(0, 160);", + " socket.destroy();", + " finish(67, 'proxy CONNECT failed via ' + proxyHost + ':' + proxyPort + ' to ' + targetHost + ':' + targetPort + ': ' + safeStatus);", + " return;", + " }", " socket.off('data', onData);", " socket.setTimeout(0);", + " tunnelEstablished = true;", " const rest = buffer.slice(headerEnd + 4);", " if (rest.length) process.stdout.write(rest);", + " process.stdin.on('error', () => {});", + " process.stdout.on('error', () => {});", " process.stdin.pipe(socket);", " socket.pipe(process.stdout);", "}", "socket.on('data', onData);", - "socket.on('close', () => process.exit(0));", "NODE_PROXY", "chmod 0700 /tmp/agentrun-github-proxy-connect.cjs", - `export GIT_SSH_COMMAND=${shQuote(`ssh -i /root/.ssh/id_rsa -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/root/.ssh/known_hosts -o ConnectTimeout=15 -o ServerAliveInterval=5 -o ServerAliveCountMax=1 -o ProxyCommand='node /tmp/agentrun-github-proxy-connect.cjs ${spec.gitMirror.githubProxy.host} ${spec.gitMirror.githubProxy.port} %h %p'`)}`, + "cat > /tmp/agentrun-git-ssh-proxy.sh <<'SH_PROXY'", + "#!/bin/sh", + `exec ssh -i /root/.ssh/id_rsa -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/root/.ssh/known_hosts -o ConnectTimeout=15 -o ServerAliveInterval=5 -o ServerAliveCountMax=1 -o ${shQuote(proxyCommand)} "$@"`, + "SH_PROXY", + "chmod 0700 /tmp/agentrun-git-ssh-proxy.sh", + `export HTTP_PROXY=${shQuote(proxyUrl)}`, + `export HTTPS_PROXY=${shQuote(proxyUrl)}`, + `export ALL_PROXY=${shQuote(proxyUrl)}`, + `export http_proxy=${shQuote(proxyUrl)}`, + `export https_proxy=${shQuote(proxyUrl)}`, + `export all_proxy=${shQuote(proxyUrl)}`, + "export GIT_SSH=/tmp/agentrun-git-ssh-proxy.sh", + "unset GIT_SSH_COMMAND", ]; }