Skip to content

fix: route CLI proxy through isolated egress relay - #7338

Merged
lpcox merged 4 commits into
mainfrom
copilot/awf-cli-proxy-network-isolation-fix
Aug 14, 2026
Merged

fix: route CLI proxy through isolated egress relay#7338
lpcox merged 4 commits into
mainfrom
copilot/awf-cli-proxy-network-isolation-fix

Conversation

Copilot AI commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

In network-isolation mode, awf-net is internal and cannot reach a host or external DIFC proxy. The previous implementation fixed that reachability issue by dual-homing the credential-bearing cli-proxy, which gave agent-controlled gh and Git subprocesses an unrestricted route through awf-ext.

This revision keeps cli-proxy on awf-net only. For an external DIFC target, AWF now creates a separate cli-proxy-egress service that:

  • contains no GitHub credentials or writable mounts;
  • runs only a fixed-target TCP relay;
  • forwards solely to the configured DIFC host and port;
  • is the only CLI-proxy component attached to awf-ext;
  • uses a read-only filesystem, dropped capabilities, and resource limits.

The credential-bearing proxy connects to the relay over awf-net. Attached sibling DIFC proxies continue to work directly and do not create the relay. Loopback DIFC addresses are normalized to host.docker.internal so the relay reaches the runner-host proxy rather than itself.

Tests cover external relay creation, credential and mount isolation, attached sibling behavior, loopback normalization, network membership, and dependency ordering.

Fixes #7335

Copilot AI changed the title [WIP] Fix cli-proxy in network isolation mode for external DIFC proxies Dual-home cli-proxy on awf-ext when the DIFC proxy is external Aug 13, 2026
Copilot AI requested a review from lpcox August 13, 2026 21:10
@lpcox
lpcox marked this pull request as ready for review August 13, 2026 21:21
Copilot AI balanced review requested due to automatic review settings August 13, 2026 21:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds conditional awf-ext connectivity for cli-proxy when targeting external DIFC proxies in network-isolation mode.

Changes:

  • Adds DIFC host classification and conditional dual-homing.
  • Adds classifier and Compose-network tests.
  • Documents the revised network topology.
Show a summary per file
File Description
src/services/cli-proxy-service.ts Classifies DIFC hosts and conditionally attaches awf-ext.
src/services/cli-proxy-service.test.ts Tests host classification.
src/compose-generator.test.ts Tests conditional dual-homing.
docs/network-isolation-design.md Documents cli-proxy network behavior.

Review details

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 4/4 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment thread src/services/cli-proxy-service.ts Outdated
Comment on lines +45 to +46
if (isValidIPv4(normalized) || isValidIPv6(normalized)) return true;
if (normalized === 'localhost') return true;
Comment on lines +47 to +49
// Dotted names (host.docker.internal, difc.example.com) resolve outside the
// Compose project; bare labels are Docker service/container names.
return normalized.includes('.');
Comment thread docs/network-isolation-design.md Outdated
Comment on lines +21 to +25
- The **cli-proxy is dual-homed too, but only when it targets an *external* DIFC proxy**
(`host.docker.internal`, a bare IP, or a dotted DNS name). Without a route off the
internal network its tcp-tunnel fails with `ENETUNREACH` and the container never
becomes healthy. When the DIFC proxy is an attached sibling container (a bare Docker
service name), the cli-proxy stays on `awf-net` only.
@lpcox

lpcox commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

@copilot address review feedback

Copilot AI commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

@copilot address review feedback

Addressed in 28791eb:

  • Loopback DIFC hosts (localhost, 127.0.0.0/8, ::1) are now normalized to host.docker.internal before use, so the tcp-tunnel no longer dials itself.
  • isExternalDifcProxyHost now treats IP literals inside awf-net's own subnet (172.30.0.0/24) as attached siblings instead of external, fixing the case of a sibling addressed by its internal IP. Genuinely external single-label hosts remain a documented caveat since Docker service names are also bare labels — there's no reliable signal to distinguish the two without an explicit flag.
  • Reworded the docs summary so Squid's "sole egress path" claim is scoped to the agent, and clarified the cli-proxy dual-homing bullet is unrelated to the still-unsupported gh-aw mcpg/DIFC host-network model described further down.

@github-actions

Copy link
Copy Markdown
Contributor

✅ Copilot review passed with no inline comments.

@copilot Add the ready-for-aw label to this PR to trigger agentic CI smoke tests.

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Documentation Preview

Documentation build failed for this PR. View logs.

Built from commit 93b1b1c

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Coverage Regression Detected

This PR decreases test coverage. Please add tests to maintain coverage levels.

Overall Coverage

Metric Base PR Delta
Lines 94.11% 93.79% 📉 -0.32%
Statements 93.00% 92.63% 📉 -0.37%
Functions 93.27% 92.74% 📉 -0.53%
Branches 86.33% 85.77% 📉 -0.56%
📁 Per-file Coverage Changes (7 files)
File Lines (Before → After) Statements (Before → After)
src/cloud-hypervisor/manager.ts 89.2% → 88.3% (-0.86%) 88.3% → 87.3% (-1.01%)
src/cloud-hypervisor-runtime-backend.ts 97.9% → 97.2% (-0.69%) 95.1% → 94.8% (-0.27%)
src/cloud-hypervisor/launcher.ts 93.9% → 93.8% (-0.18%) 92.8% → 92.5% (-0.22%)
src/cloud-hypervisor/preflight.ts 95.4% → 95.8% (+0.45%) 94.8% → 95.3% (+0.50%)
src/microvm/workspace.ts 89.5% → 90.1% (+0.59%) 88.4% → 88.8% (+0.48%)
src/services/cli-proxy-service.ts 95.0% → 97.8% (+2.77%) 95.0% → 94.2% (-0.77%)
src/log-directory-setup.ts 96.2% → 100.0% (+3.78%) 96.3% → 100.0% (+3.71%)
✨ New Files (3 files)
  • src/cloud-hypervisor/exports.ts: 87.9% lines
  • src/cloud-hypervisor/virtiofsd.ts: 76.2% lines
  • src/microvm/rootfs.ts: 72.0% lines

Coverage comparison generated by scripts/ci/compare-coverage.ts

Keep the credential-bearing CLI proxy on the internal network.

Route external DIFC traffic through a credential-free fixed-target relay.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0ee67ebb-e8fc-45a1-a3a1-3d0cdcbc022e
@lpcox lpcox changed the title Dual-home cli-proxy on awf-ext when the DIFC proxy is external fix: route CLI proxy through isolated egress relay Aug 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Coverage Regression Detected

This PR decreases test coverage. Please add tests to maintain coverage levels.

Overall Coverage

Metric Base PR Delta
Lines 94.11% 93.86% 📉 -0.25%
Statements 93.00% 92.69% 📉 -0.31%
Functions 93.27% 92.63% 📉 -0.64%
Branches 86.31% 85.85% 📉 -0.46%
📁 Per-file Coverage Changes (10 files)
File Lines (Before → After) Statements (Before → After)
src/cloud-hypervisor-runtime-backend.ts 97.9% → 97.1% (-0.83%) 95.1% → 94.6% (-0.52%)
src/cloud-hypervisor/launcher.ts 93.9% → 93.8% (-0.18%) 92.8% → 92.5% (-0.22%)
src/firecracker-runtime-backend.ts 98.1% → 98.0% (-0.16%) 94.7% → 94.3% (-0.44%)
src/services/optional-services.ts 100.0% → 100.0% (+0.00%) 96.6% → 96.7% (+0.04%)
src/cloud-hypervisor/preflight.ts 95.4% → 95.8% (+0.45%) 94.8% → 95.3% (+0.50%)
src/microvm/workspace.ts 89.5% → 90.1% (+0.59%) 88.4% → 88.8% (+0.48%)
src/cloud-hypervisor/manager.ts 89.2% → 90.7% (+1.45%) 88.3% → 89.3% (+1.02%)
src/services/cli-proxy-service.ts 95.0% → 98.0% (+3.03%) 95.0% → 94.8% (-0.18%)
src/log-directory-setup.ts 96.2% → 100.0% (+3.78%) 96.3% → 100.0% (+3.71%)
src/microvm/network.ts 96.0% → 100.0% (+3.95%) 95.3% → 100.0% (+4.69%)
✨ New Files (11 files)
  • src/cloud-hypervisor/diagnostics.ts: 84.7% lines
  • src/cloud-hypervisor/exports.ts: 87.9% lines
  • src/cloud-hypervisor/guest-execution.ts: 100.0% lines
  • src/cloud-hypervisor/manager-types.ts: 100.0% lines
  • src/cloud-hypervisor/virtiofsd.ts: 76.2% lines
  • src/cloud-hypervisor/vm-config-builder.ts: 89.5% lines
  • src/microvm/guest-environment.ts: 100.0% lines
  • src/microvm/network-commands.ts: 97.8% lines
  • src/microvm/network-manager.ts: 98.4% lines
  • src/microvm/network-plan.ts: 95.2% lines
  • src/microvm/rootfs.ts: 72.0% lines

Coverage comparison generated by scripts/ci/compare-coverage.ts

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

🛡️ Smoke Copilot Network Isolation confirmed the egress allowlist is enforced. ✅

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • example.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "example.com"

See Network Configuration for more information.

🛡️ Egress verdict from Smoke Copilot Network Isolation

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Smoke Copilot BYOK AOAI (Entra) reports failed. AOAI BYOK (Entra) mode investigation needed...

🪪 BYOK (AOAI Entra) report filed by Smoke Copilot BYOK AOAI (Entra)

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

✨ The prophecy is fulfilled... Smoke Codex has completed its mystical journey. The stars align. 🌟

Warning

Firewall blocked 5 domains

The following domains were blocked by the firewall during workflow execution:

  • msfeed12.pkgs.visualstudio.com
  • msfeed17.pkgs.visualstudio.com
  • msfeed2.pkgs.visualstudio.com
  • msfeed25.pkgs.visualstudio.com
  • registry.npmjs.org

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "msfeed12.pkgs.visualstudio.com"
    - "msfeed17.pkgs.visualstudio.com"
    - "msfeed2.pkgs.visualstudio.com"
    - "msfeed25.pkgs.visualstudio.com"
    - "registry.npmjs.org"

See Network Configuration for more information.

🔮 The oracle has spoken through Smoke Codex

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Security Guard completed successfully!

Security review of PR #7338: No security issues found. Changes to cli-proxy network isolation are properly gated feature flags with secure defaults (loopback-only binding unless explicitly enabled). No firewall, capability, ACL, or validation weaknesses introduced.

Generated by Security Guard for #7338

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

📡 Smoke OTel Tracing completed. All tracing scenarios validated. ✅

📡 OTel tracing validated by Smoke OTel Tracing

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

🔌 Smoke Services — All services reachable! ✅

🔌 Service connectivity validated by Smoke Services

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Smoke Gemini reports failed. Facets need polishing...

💎 Faceted by Smoke Gemini

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

📰 VERDICT: Smoke Docker Sbx has concluded. All systems operational. This is a developing story. 🎤

📰 BREAKING: Report filed by Smoke Docker Sbx

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Build Test Suite completed successfully!

Generated by Build Test Suite for #7338

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Chroot tests passed! Smoke Chroot - All security and functionality tests succeeded.

Tested by Smoke Chroot

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Smoke Copilot BYOK AOAI (api-key) reports failed. AOAI BYOK (api-key) mode investigation needed...

🔑 BYOK (AOAI api-key) report filed by Smoke Copilot BYOK AOAI (api-key)

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test Results @lpcox

Overall: PASS

📰 BREAKING: Report filed by Smoke Copilot
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test: Claude Engine Validation

Check Result
API status ✅ PASS
gh check ✅ PASS
File status ✅ PASS

Overall result: PASS

Generated by Smoke Claude for #7338 · haiku45 · 37.2 AIC · ⊞ 3.6K ·
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Copilot BYOK (Direct) Smoke Test

Recent merged PRs: #7370 docs: include Cloud Hypervisor in runtime guides, #7362 Remove Firecracker support

  • MCP connectivity: ✅
  • GitHub.com connectivity (HTTP 200): ✅
  • File write/read: ✅
  • BYOK inference (agent → api-proxy → api.githubcopilot.com): ✅

Running in direct BYOK mode (COPILOT_PROVIDER_API_KEY) via api-proxy → api.githubcopilot.com

Overall: PASS

cc @lpcox

🔑 BYOK report filed by Smoke Copilot BYOK
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test: Copilot Network Isolation Egress Enforcement

EGRESS_RESULT allow=pass deny=pass

✅ Allowed domain (github.com) reachable — HTTP 200
✅ Blocked domain (example.com) denied — CONNECT tunnel failed (403)

Overall: PASS

cc @lpcox

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • example.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "example.com"

See Network Configuration for more information.

🛡️ Egress verdict from Smoke Copilot Network Isolation
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test: GitHub Actions Services Connectivity

  • Redis PING: ❌ (Temporary failure in name resolution)
  • PostgreSQL pg_isready: ❌ (no response)
  • PostgreSQL SELECT 1: ❌ (could not translate host name)

Overall: FAIL — sandbox cannot resolve/reach host.docker.internal.

🔌 Service connectivity validated by Smoke Services
Add label ready-for-aw to run again

@github-actions github-actions Bot added the smoke-copilot-network-isolation Copilot network-isolation egress smoke test label Aug 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test: API Proxy OTel Tracing — results

  • Scenario 1 (Module Loading): otel.js loaded successfully, isEnabled() → true, exports include startRequestSpan, setTokenAttributes, setBudgetAttributes, endSpan, endSpanError, shutdown, isEnabled, plus internal exporter/parsing helpers.
  • Scenario 2 (Test Suite): npx jest --testPathPatterns=otel → 3 suites / 68 tests, all passed (otel.test.js, otel-fanout.test.js, otel-workload-identity.test.js).
  • Scenario 3 (Env Var Forwarding): GITHUB_AW_OTEL_TRACE_ID/GITHUB_AW_OTEL_PARENT_SPAN_ID present in env-passthrough.ts; GH_AW_OTLP_ENDPOINTS, OTEL_EXPORTER_OTLP_ENDPOINT, and both trace-context vars present in api-proxy-env-config.ts.
  • Scenario 4 (Token Tracker Integration): onUsage callback confirmed in token-tracker-http.js as the OTEL hook point.
  • i️ Scenario 5 (OTEL Diagnostics): No sandbox firewall/logs/api-proxy/otel.jsonl span file was produced this run (api-proxy sidecar not exercised via --enable-api-proxy in this workflow), so no proxied-request spans to inspect. The workflow's own OTLP fan-out (workflow-level trace, not api-proxy) did export one root span successfully, confirming the OTLP export path itself works.

Overall: all applicable scenarios pass; Scenario 5 is expected-pending since this smoke test doesn't invoke the api-proxy sidecar directly.

📡 OTel tracing validated by Smoke OTel Tracing
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test: Docker Sbx@lpcox @Copilot

PR #7338: fix: route CLI proxy through isolated egress relay

Overall: PASS

📰 BREAKING: Report filed by Smoke Docker Sbx
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke test: FAIL
PR titles:

  • unavailable: safeinputs-gh missing
  • unavailable: safeinputs-gh missing
  • GitHub MCP review: ❌
  • GH CLI query: ❌
  • Playwright title check: ✅
  • File write/readback: ✅
  • Discussion query/comment: ❌
  • Build (npm ci && npm run build): ❌ npm ci: Exit handler never called!
    Overall: FAIL

Warning

Firewall blocked 5 domains

The following domains were blocked by the firewall during workflow execution:

  • msfeed12.pkgs.visualstudio.com
  • msfeed17.pkgs.visualstudio.com
  • msfeed2.pkgs.visualstudio.com
  • msfeed25.pkgs.visualstudio.com
  • registry.npmjs.org

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "msfeed12.pkgs.visualstudio.com"
    - "msfeed17.pkgs.visualstudio.com"
    - "msfeed2.pkgs.visualstudio.com"
    - "msfeed25.pkgs.visualstudio.com"
    - "registry.npmjs.org"

See Network Configuration for more information.

🔮 The oracle has spoken through Smoke Codex
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Chroot Version Comparison

Runtime Host Version Chroot Version Match?
Python Python 3.12.13 Python 3.12.13 ✅ YES
Node.js v24.19.0 v22.23.2 ❌ NO
Go go1.22.12 go1.22.12 ✅ YES

Overall: FAILED — Node.js version mismatch between host and chroot environments. smoke-chroot label not applied since not all tests passed.

Tested by Smoke Chroot
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

🏗️ Build Test Suite Results

Ecosystem Project Build/Install Tests Status
Bun elysia ✅ passed ✅ PASS
Bun hono ✅ passed ✅ PASS
C++ fmt N/A ✅ PASS
C++ json N/A ✅ PASS
Deno oak N/A ✅ passed ✅ PASS
Deno std N/A ✅ passed ✅ PASS
.NET hello-world N/A ✅ PASS
.NET json-parse N/A ✅ PASS
Go color ✅ passed ✅ PASS
Go env ✅ passed ✅ PASS
Go uuid ✅ passed ✅ PASS
Java gson 0/0 ❌ FAIL
Java caffeine 0/0 ❌ FAIL
Node.js clsx ✅ passed ✅ PASS
Node.js execa ✅ passed ✅ PASS
Node.js p-limit ✅ passed ✅ PASS
Rust fd ✅ passed ✅ PASS
Rust zoxide ✅ passed ✅ PASS

Overall: 7/8 ecosystems passed — FAIL

Failure details

Java (gson, caffeine): mvn compile failed with:

[ERROR] Could not create local repository at /home/runner/.m2/repository -> [Help 1]

Root cause: /home/runner/.m2 directory was pre-existing and owned by root:root (not the runner user), so Maven could not create the repository subdirectory before any network/proxy activity occurred. This is an environment/filesystem permission issue unrelated to the firewall proxy configuration itself (the ~/.m2/settings.xml proxy config was applied correctly, as seen from the JAVA_TOOL_OPTIONS proxy vars picked up in the log).

Generated by Build Test Suite for #7338 · auto · 24.1 AIC · ⊞ 11.5K ·
Add label ready-for-aw to run again

@lpcox
lpcox merged commit 9ea3175 into main Aug 14, 2026
142 of 149 checks passed
@lpcox
lpcox deleted the copilot/awf-cli-proxy-network-isolation-fix branch August 14, 2026 23:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[awf] network-isolation: cli-proxy still cannot reach an external DIFC proxy on v0.28.0 (supersedes #7063)

3 participants