feat: add upstream corporate proxy support for self-hosted runners#1976
feat: add upstream corporate proxy support for self-hosted runners#1976
Conversation
Add --upstream-proxy flag and auto-detection from host https_proxy/ http_proxy/no_proxy environment variables. When configured, Squid chains outbound traffic through the corporate proxy via cache_peer. Key changes: - New upstream-proxy.ts with parseProxyUrl(), parseNoProxy(), detectUpstreamProxy(), and PROXY_ENV_VARS constant - UpstreamProxyConfig interface in types.ts - generateUpstreamProxySection() in squid-config.ts for cache_peer, always_direct (no_proxy bypass), and never_direct directives - CLI auto-detection with --upstream-proxy explicit override - Host proxy env vars excluded from --env-all passthrough - Security: reject credentials, loopback, HTTPS scheme, injection chars - 35 new tests across upstream-proxy, squid-config, docker-manager - Documentation in docs/environment.md Closes #1975 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
✅ Coverage Check PassedOverall Coverage
📁 Per-file Coverage Changes (3 files)
✨ New Files (1 files)
Coverage comparison generated by |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
Adds first-class support for self-hosted runners that require outbound traffic to go through a corporate proxy, by auto-detecting host proxy settings and configuring Squid to chain via cache_peer (with no_proxy-based bypass).
Changes:
- Introduces upstream proxy parsing/validation + host env auto-detection (
src/upstream-proxy.ts) and wires it into CLI/config generation. - Extends Squid config generation to emit
cache_peer+always_direct/never_directdirectives when an upstream proxy is configured. - Prevents host proxy env vars from leaking into containers (even with
--env-all), and adds tests + documentation for the new behavior.
Show a summary per file
| File | Description |
|---|---|
| src/upstream-proxy.ts | Implements proxy URL parsing, no_proxy parsing, and host env auto-detection; defines PROXY_ENV_VARS. |
| src/upstream-proxy.test.ts | Adds unit tests for proxy parsing and detection behavior. |
| src/types.ts | Adds UpstreamProxyConfig and threads it through WrapperConfig/SquidConfig. |
| src/squid-config.ts | Generates Squid upstream chaining directives (cache_peer, bypass ACLs, directness rules). |
| src/squid-config.test.ts | Adds tests asserting correct upstream proxy directive generation. |
| src/docker-manager.ts | Ensures proxy env vars are excluded from env passthrough and passes upstream proxy config into Squid config generation. |
| src/docker-manager.test.ts | Adds a regression test ensuring host proxy vars don’t leak with --env-all. |
| src/cli.ts | Adds --upstream-proxy flag and auto-detection path into wrapper config creation. |
| docs/environment.md | Documents upstream proxy chaining behavior, limitations, and env var exclusion. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comments suppressed due to low confidence (2)
src/upstream-proxy.ts:181
detectUpstreamProxy()compares the rawhttp_proxy/https_proxystrings for equality. Equivalent proxies can differ textually (e.g., trailing/, missing scheme, different casing) and this will incorrectly throw and force--upstream-proxy. Consider normalizing both values first (e.g., parse both withparseProxyUrl()and compare host/port), or compare vianew URL()fields after adding a default scheme/port.
// If both are set and differ, we can't determine which to use
if (httpsProxy && httpProxy && httpsProxy !== httpProxy) {
throw new Error(
'Host has different http_proxy and https_proxy values. ' +
'AWF cannot determine which upstream proxy to use. ' +
`Use --upstream-proxy to specify explicitly.\n` +
` http_proxy: ${httpProxy}\n` +
` https_proxy: ${httpsProxy}`
);
src/upstream-proxy.ts:139
- In
parseNoProxy(), the IPv6 filter (entry.includes(':')) runs before the “entry with port” check. This means common values likehost:8080(orhttp://host:8080) will be classified as “IPv6” and won’t hit the intended port-specific warning/message. Reorder the checks (port first) and/or usenet.isIP()on a bracket-stripped host to detect true IPv6 literals.
// Skip IPv6 addresses
if (entry.includes(':') || entry.startsWith('[')) {
logger.warn(`Ignoring no_proxy IPv6 entry "${entry}" — only domain suffixes are supported for upstream proxy bypass`);
continue;
}
// Skip entries with ports (e.g., "host:8080")
if (/:\d+$/.test(entry)) {
logger.warn(`Ignoring no_proxy entry with port "${entry}" — port-based bypass is not supported for upstream proxy`);
continue;
}
- Files reviewed: 9/9 changed files
- Comments generated: 3
| // Reject loopback addresses — Squid runs in a container and localhost != host localhost | ||
| const loopbackPatterns = ['localhost', '127.0.0.1', '::1', '0.0.0.0']; | ||
| if (loopbackPatterns.includes(host.toLowerCase())) { | ||
| throw new Error( | ||
| `Upstream proxy "${host}" is a loopback address. Squid runs in a Docker container ` + | ||
| 'where localhost refers to the container, not the host. ' + | ||
| 'Use the host machine\'s network IP or configure --enable-host-access with host.docker.internal.' | ||
| ); | ||
| } |
src/squid-config.ts
Outdated
| // Domain suffixes: .corp.com matches *.corp.com | ||
| // Exact domains: internal.corp.com matches only that host | ||
| const squidDomain = domain.startsWith('.') ? domain : `.${domain}`; | ||
| lines.push(`acl upstream_bypass dstdomain ${squidDomain}`); | ||
| // Also add exact match for non-wildcard domains |
| ## Upstream (Corporate) Proxy Support | ||
|
|
||
| When running on self-hosted runners behind a corporate proxy, AWF can chain Squid | ||
| through the upstream proxy using the `cache_peer` directive. | ||
|
|
||
| ### Auto-detection | ||
|
|
||
| If the host has `https_proxy`/`HTTPS_PROXY` or `http_proxy`/`HTTP_PROXY` set, AWF | ||
| automatically configures Squid to route outbound traffic through that proxy. | ||
| `no_proxy`/`NO_PROXY` domain suffixes are honored as bypass rules (`always_direct`). |
This comment has been minimized.
This comment has been minimized.
- Robust loopback detection: check full 127.0.0.0/8 range and IPv6 variants via isLoopback() helper instead of exact-match list - Fix misleading comments in squid-config.ts: non-dot no_proxy entries are treated as suffix matches (domain + subdomains), not exact-only - Update docs/environment.md: clarify that host proxy vars are excluded from container passthrough but are read for upstream proxy detection Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Smoke test results (run
Overall: PASS
|
🤖 Smoke Test Results
Overall: PASS PR by @lpcox, no assignees.
|
|
Smoke test summary
|
Chroot Version Comparison Results
Overall: ❌ Not all tests passed — Python and Node.js versions differ between host and chroot environment.
|
🏗️ Build Test Suite Results
Overall: 8/8 ecosystems passed — ✅ PASS
|
Smoke Test: GitHub Actions Services Connectivity
All checks passed. Note:
|
Summary
Adds support for self-hosted runners behind a corporate proxy. AWF now auto-detects host proxy environment variables (
https_proxy/http_proxy/no_proxy) and configures Squid to chain through the corporate proxy viacache_peer.Changes
New:
src/upstream-proxy.tsparseProxyUrl()— validates proxy URLs, rejects credentials/loopback/HTTPS/injection charsparseNoProxy()— parsesno_proxyinto domain suffixes, skipping IPs/CIDRs/wildcardsdetectUpstreamProxy()— auto-detects from host env, fails if http_proxy ≠ https_proxyPROXY_ENV_VARS— constant listing all proxy env var names for exclusionModified:
src/squid-config.tsgenerateUpstreamProxySection()— generatescache_peer,always_direct(bypass),never_directdirectivesModified:
src/cli.ts--upstream-proxy <url>flag for explicit overrideModified:
src/docker-manager.tsPROXY_ENV_VARSadded toEXCLUDED_ENV_VARS— prevents host proxy vars from leaking into containers via--env-allupstreamProxypassed through togenerateSquidConfig()Modified:
src/types.tsUpstreamProxyConfiginterface (host,port, optionalnoProxy)upstreamProxy?field toWrapperConfigandSquidConfigTests (35 new)
src/upstream-proxy.test.ts— 30 tests covering parsing, validation, detectionsrc/squid-config.test.ts— 4 tests for cache_peer generationsrc/docker-manager.test.ts— 1 test for proxy env var exclusionDocumentation
docs/environment.md— new 'Upstream (Corporate) Proxy Support' sectionSecurity considerations
Traffic flow (with upstream proxy)
Domains in
no_proxybypass the corporate proxy viaalways_direct.Closes #1975