Document Copilot agent firewall probe blocker - #19912
Document Copilot agent firewall probe blocker#19912Ankit Jain (radical) with Copilot wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Copilot wasn't able to review any files in this pull request.
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: radical <1472+radical@users.noreply.github.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 19912Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 19912" |
There was a problem hiding this comment.
🟡 Changes recommended
The restore pipeline can mask failures because it does not enable pipefail.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 1
- Review effort level: Balanced
| dotnet nuget locals plugins-cache --clear | ||
| rm -rf artifacts/bin artifacts/obj artifacts/tmp artifacts/package-cache artifacts/packages artifacts/TestResults | ||
|
|
||
| ./build.sh -restore 2>&1 | tee /tmp/aspire-copilot-firewall-probe/01-build-restore.log |
|
[automated] Copilot Continue the firewall investigation without modifying or committing repository files. Run everything below only from the Copilot cloud-agent shell with the integrated firewall enabled. The repository custom allowlist now includes For each phase:
Start with: set -o pipefail
mkdir -p /tmp/aspire-copilot-firewall-probe
dotnet nuget locals all --clear
rm -rf artifacts/bin artifacts/obj artifacts/tmp artifacts/package-cache artifacts/packages artifacts/TestResults
./build.sh -restore 2>&1 | tee /tmp/aspire-copilot-firewall-probe/01-restore.logStop the remaining phases if restore fails. If restore succeeds, run these phases independently so each has its own firewall-log offset. CLI local-hive archive./localhive.sh -o /tmp/aspire-e2e -r linux-x64 --archive \
2>&1 | tee /tmp/aspire-copilot-firewall-probe/02-localhive.logCLI E2E starter and AppHostASPIRE_E2E_ARCHIVE=/tmp/aspire-e2e.tar.gz \
dotnet test --project tests/Aspire.Cli.EndToEnd.Tests/Aspire.Cli.EndToEnd.Tests.csproj \
--no-launch-profile -- \
--filter-method "*.CreateAndRunAspireStarterProject" \
--filter-not-trait "quarantined=true" \
--filter-not-trait "outerloop=true" \
2>&1 | tee /tmp/aspire-copilot-firewall-probe/03-cli-smoke.logCLI E2E standalone dashboardASPIRE_E2E_ARCHIVE=/tmp/aspire-e2e.tar.gz \
dotnet test --project tests/Aspire.Cli.EndToEnd.Tests/Aspire.Cli.EndToEnd.Tests.csproj \
--no-launch-profile -- \
--filter-method "*.DashboardRunWithOtelTracesReturnsNoTraces" \
--filter-not-trait "quarantined=true" \
--filter-not-trait "outerloop=true" \
2>&1 | tee /tmp/aspire-copilot-firewall-probe/04-cli-dashboard.logHosting dashboard and container pathsdotnet test --project tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj \
--no-launch-profile -- \
--filter-method "*.StartAsync_DashboardUrls_DisplayPropertiesSet" \
--filter-method "*.VerifyContainerArgs" \
--filter-not-trait "quarantined=true" \
--filter-not-trait "outerloop=true" \
2>&1 | tee /tmp/aspire-copilot-firewall-probe/05-hosting.logDashboard server startupdotnet test --project tests/Aspire.Dashboard.Tests/Aspire.Dashboard.Tests.csproj \
--no-launch-profile -- \
--filter-method "*.EndPointAccessors_AppStarted_BrowserGet_Success" \
--filter-not-trait "quarantined=true" \
--filter-not-trait "outerloop=true" \
2>&1 | tee /tmp/aspire-copilot-firewall-probe/06-dashboard-server.logDashboard component renderingdotnet test --project tests/Aspire.Dashboard.Components.Tests/Aspire.Dashboard.Components.Tests.csproj \
--no-launch-profile -- \
--filter-method "*.UpdateResources_FiltersUpdated" \
--filter-not-trait "quarantined=true" \
--filter-not-trait "outerloop=true" \
2>&1 | tee /tmp/aspire-copilot-firewall-probe/07-dashboard-components.logDashboard Playwright browser pathThis outerloop test is intentionally selected to probe the browser download and real dashboard UI path. Remove only the browser caches before this phase: rm -rf artifacts/bin/playwright-deps "$HOME/.cache/ms-playwright"
PLAYWRIGHT_INSTALLED=true \
dotnet test --project tests/Aspire.Dashboard.Tests/Aspire.Dashboard.Tests.csproj \
-p:InstallBrowsersForPlaywright=true \
--no-launch-profile -- \
--filter-method "*.ViewOptionsMenu_ReportsExpandedState" \
--filter-not-trait "quarantined=true" \
2>&1 | tee /tmp/aspire-copilot-firewall-probe/08-dashboard-playwright.logContinue to later independent phases after a test failure when possible, but do not hide exit codes or report a failed/skipped test as passing. Distinguish product/test failures from firewall blocks. At the end, summarize:
Do not broaden firewall rules speculatively and do not edit or commit repository files. |
Probe run from the cloud-agent shell with the firewall enabled. No repository files were modified or committed ( Result: restore still blocked, so phases 2–8 were not runThe custom allowlist entry did not take effect in this session. Commands and exit codes
Phase 1 firewall log (offset 33260 → 95665)The log records both allowed and blocked traffic. Allowed (
Blocked (
Restore error: Why the allowlist change did not helpThis session's effective allow list (
Additional evidence: no alternate feed avoids this hostI checked whether the already-allowed Required vs optional, so far
No firewall rules were broadened and nothing speculative was added. Once the allowlist actually shows a matching |
|
[automated] Copilot Continue the firewall investigation without modifying or committing repository files. Run everything below only from the Copilot cloud-agent shell with the integrated firewall enabled. The repository custom allowlist now includes For each phase:
Start with: set -o pipefail
mkdir -p /tmp/aspire-copilot-firewall-probe
dotnet nuget locals all --clear
rm -rf artifacts/bin artifacts/obj artifacts/tmp artifacts/package-cache artifacts/packages artifacts/TestResults
./build.sh -restore 2>&1 | tee /tmp/aspire-copilot-firewall-probe/01-restore.logStop the remaining phases if restore fails. If restore succeeds, run these phases independently so each has its own firewall-log offset. CLI local-hive archive./localhive.sh -o /tmp/aspire-e2e -r linux-x64 --archive \
2>&1 | tee /tmp/aspire-copilot-firewall-probe/02-localhive.logCLI E2E starter and AppHostASPIRE_E2E_ARCHIVE=/tmp/aspire-e2e.tar.gz \
dotnet test --project tests/Aspire.Cli.EndToEnd.Tests/Aspire.Cli.EndToEnd.Tests.csproj \
--no-launch-profile -- \
--filter-method "*.CreateAndRunAspireStarterProject" \
--filter-not-trait "quarantined=true" \
--filter-not-trait "outerloop=true" \
2>&1 | tee /tmp/aspire-copilot-firewall-probe/03-cli-smoke.logCLI E2E standalone dashboardASPIRE_E2E_ARCHIVE=/tmp/aspire-e2e.tar.gz \
dotnet test --project tests/Aspire.Cli.EndToEnd.Tests/Aspire.Cli.EndToEnd.Tests.csproj \
--no-launch-profile -- \
--filter-method "*.DashboardRunWithOtelTracesReturnsNoTraces" \
--filter-not-trait "quarantined=true" \
--filter-not-trait "outerloop=true" \
2>&1 | tee /tmp/aspire-copilot-firewall-probe/04-cli-dashboard.logHosting dashboard and container pathsdotnet test --project tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj \
--no-launch-profile -- \
--filter-method "*.StartAsync_DashboardUrls_DisplayPropertiesSet" \
--filter-method "*.VerifyContainerArgs" \
--filter-not-trait "quarantined=true" \
--filter-not-trait "outerloop=true" \
2>&1 | tee /tmp/aspire-copilot-firewall-probe/05-hosting.logDashboard server startupdotnet test --project tests/Aspire.Dashboard.Tests/Aspire.Dashboard.Tests.csproj \
--no-launch-profile -- \
--filter-method "*.EndPointAccessors_AppStarted_BrowserGet_Success" \
--filter-not-trait "quarantined=true" \
--filter-not-trait "outerloop=true" \
2>&1 | tee /tmp/aspire-copilot-firewall-probe/06-dashboard-server.logDashboard component renderingdotnet test --project tests/Aspire.Dashboard.Components.Tests/Aspire.Dashboard.Components.Tests.csproj \
--no-launch-profile -- \
--filter-method "*.UpdateResources_FiltersUpdated" \
--filter-not-trait "quarantined=true" \
--filter-not-trait "outerloop=true" \
2>&1 | tee /tmp/aspire-copilot-firewall-probe/07-dashboard-components.logDashboard Playwright browser pathThis outerloop test is intentionally selected to probe the browser download and real dashboard UI path. Remove only the browser caches before this phase: rm -rf artifacts/bin/playwright-deps "$HOME/.cache/ms-playwright"
PLAYWRIGHT_INSTALLED=true \
dotnet test --project tests/Aspire.Dashboard.Tests/Aspire.Dashboard.Tests.csproj \
-p:InstallBrowsersForPlaywright=true \
--no-launch-profile -- \
--filter-method "*.ViewOptionsMenu_ReportsExpandedState" \
--filter-not-trait "quarantined=true" \
2>&1 | tee /tmp/aspire-copilot-firewall-probe/08-dashboard-playwright.logContinue to later independent phases after a test failure when possible, but do not hide exit codes or report a failed/skipped test as passing. Distinguish product/test failures from firewall blocks. At the end, summarize:
Do not broaden firewall rules speculatively and do not edit or commit repository files. |
Probe re-run from the cloud-agent shell with the firewall enabled. Commands and exit codes
Each phase used its own firewall-log offset; every window contained both allowed and blocked traffic where applicable, and I state explicitly below where a window had no blocks. Newly required domains, by phasePhase 1 — restore (required, package acquisition). No blocks. The previously-blocked CDN is now allowed and is used heavily:
Phase 2 — localhive archive (required). No blocks. Only already-permitted hosts: Phases 3 and 4 — CLI E2E (required, newly blocked).
Phase 5 — hosting (no firewall blocks). Both tests failed, but the log window contains zero blocked entries. Root cause is local, not network: DCP's API server started on Phases 6 and 7 — dashboard server and components. Both passed, no blocks. Only agent control-plane traffic in the windows. Phase 8 / 8b — Playwright browser path (required, already permitted).
Traffic already permitted by the current allowlist
Optional telemetry / control-plane trafficPresent in every phase window, unrelated to build/test correctness: Phases that could not run, and the exact blockerNone were skipped. The only firewall-caused failures are phases 3 and 4, blocked on Single new required endpoint
Phases 5 and 8 failed for non-firewall reasons and are reported as failures, not passes. No firewall rules were broadened speculatively and no repository files were edited or committed. |
|
[automated] Copilot Rerun the remaining firewall probes without modifying or committing repository files. Run everything only from the Copilot cloud-agent shell with the integrated firewall enabled. The live custom allowlist now includes the URL-prefix rule The default-branch setup workflow runs For every command:
First confirm that the effective firewall configuration for this run contains Remove pre-agent restore stateUse the system SDK installed by the later set -o pipefail
mkdir -p /tmp/aspire-copilot-firewall-rerun
dotnet build-server shutdown || true
rm -rf .dotnet \
artifacts/bin \
artifacts/obj \
artifacts/tmp \
artifacts/package-cache \
artifacts/packages \
artifacts/TestResults
hash -r
dotnet --info 2>&1 | tee /tmp/aspire-copilot-firewall-rerun/00-system-dotnet.log
dotnet nuget locals all --clear \
2>&1 | tee /tmp/aspire-copilot-firewall-rerun/01-clear-caches.logReport whether Agent-shell cold restore./build.sh -restore \
2>&1 | tee /tmp/aspire-copilot-firewall-rerun/02-restore.logThis command must run after the firewall is active. Report every required host it accesses, including SDK/toolset bootstrapping and package payload hosts. CLI local-hive archive./localhive.sh -o /tmp/aspire-e2e -r linux-x64 --archive \
2>&1 | tee /tmp/aspire-copilot-firewall-rerun/03-localhive.logStop the CLI test phases if restore or local-hive creation fails, but continue with Hosting and Playwright when possible. CLI E2E starter and AppHostASPIRE_E2E_ARCHIVE=/tmp/aspire-e2e.tar.gz \
dotnet test --project tests/Aspire.Cli.EndToEnd.Tests/Aspire.Cli.EndToEnd.Tests.csproj \
--no-launch-profile -- \
--filter-method "*.CreateAndRunAspireStarterProject" \
--filter-not-trait "quarantined=true" \
--filter-not-trait "outerloop=true" \
2>&1 | tee /tmp/aspire-copilot-firewall-rerun/04-cli-smoke.logCLI E2E standalone dashboardASPIRE_E2E_ARCHIVE=/tmp/aspire-e2e.tar.gz \
dotnet test --project tests/Aspire.Cli.EndToEnd.Tests/Aspire.Cli.EndToEnd.Tests.csproj \
--no-launch-profile -- \
--filter-method "*.DashboardRunWithOtelTracesReturnsNoTraces" \
--filter-not-trait "quarantined=true" \
--filter-not-trait "outerloop=true" \
2>&1 | tee /tmp/aspire-copilot-firewall-rerun/05-cli-dashboard.logHosting dashboard pathdotnet test --project tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj \
--no-launch-profile -- \
--filter-method "*.StartAsync_DashboardUrls_DisplayPropertiesSet" \
--filter-not-trait "quarantined=true" \
--filter-not-trait "outerloop=true" \
2>&1 | tee /tmp/aspire-copilot-firewall-rerun/06-hosting-dashboard.logHosting container pathdotnet test --project tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj \
--no-launch-profile -- \
--filter-method "*.VerifyContainerArgs" \
--filter-not-trait "quarantined=true" \
--filter-not-trait "outerloop=true" \
2>&1 | tee /tmp/aspire-copilot-firewall-rerun/07-hosting-container.logDashboard Playwright browser pathRemove only the browser caches before this phase so acquisition also occurs behind the firewall: rm -rf artifacts/bin/playwright-deps "$HOME/.cache/ms-playwright"
PLAYWRIGHT_INSTALLED=true \
dotnet test --project tests/Aspire.Dashboard.Tests/Aspire.Dashboard.Tests.csproj \
-p:InstallBrowsersForPlaywright=true \
--no-launch-profile -- \
--filter-method "*.ViewOptionsMenu_ReportsExpandedState" \
--filter-not-trait "quarantined=true" \
2>&1 | tee /tmp/aspire-copilot-firewall-rerun/08-dashboard-playwright.logIf a Hosting or Playwright test fails without firewall blocks, rerun that exact test once with a new firewall-log offset and report whether the same failure reproduces. At the end, summarize:
Do not broaden firewall rules speculatively and do not edit or commit repository files. |
Rerun from the cloud-agent shell with the firewall enabled. No repository files were modified or committed ( Firewall configuration confirmed
Commands and exit codes
Every phase in this rerun recorded zero blocked domains. The Did deleting
|
| Artifact | Result |
|---|---|
githubcli-archive-keyring.gpg |
HTTP 200, 4528 bytes, file reports a valid OpenPGP Public Key Version 4, RSA 4096 |
dists/stable/Release (APT metadata) |
HTTP 200, 3035 bytes |
pool/main/g/gh/gh_2.100.0_amd64.deb |
HTTP 200, 15227466 bytes |
No redirect host was discovered afterward — cli.github.com served all three directly, unlike the earlier dnceng.pkgs.visualstudio.com → vsblob.vsassets.io 303 case.
Non-firewall failures
CLI E2E (04, 05) — new blocker, not the allowlist. The DNS block is resolved, but the Docker image build now fails one step later with:
curl: (60) SSL certificate problem: self-signed certificate in certificate chain
gpg: no valid OpenPGP data found.
cli.github.com never appears in the firewall log during these phases (0 occurrences), i.e. the request never reached the HTTP proxy. The firewall itself logs the reason at startup:
runc-shim log not found or empty, tls interception will not work for docker containers
So traffic from inside docker build is intercepted, but the container's trust store lacks the firewall's mkcert CA and the proxy cannot do proper TLS interception for containers. The same host URL succeeds from the agent shell. This is an environment/TLS-trust limitation, not an allowlist gap — no additional domain will fix it.
Hosting (06, 07). Polly.Timeout.TimeoutRejectedException after 60s from KubernetesService.ExecuteWithRetry / DcpExecutor.CreateDcpObjectsAsync; DCP's API server binds ::1 and every Watch task over Kubernetes … resources terminated unexpectedly. Retried once each — identical failure reproduced, zero blocks.
Playwright (08). System.TimeoutException … 30000ms on a UI interaction; dashboard started fine on http://127.0.0.1:41941. Retried once — identical failure reproduced, zero blocks.
Optional telemetry / control-plane traffic
Present in every window, unrelated to correctness: api.enterprise.githubcopilot.com, telemetry.enterprise.githubcopilot.com, dc.services.visualstudio.com (329 in phase 03).
Does the evidence support removing ./build.sh -restore || true from copilot-setup-steps.yml?
Yes for the restore's network access; no as a drop-in single-line deletion. Two findings qualify it:
- Network-wise it is fully supported. I deleted
.dotnet, every artifacts directory, and all NuGet caches, then ran./build.sh -restorefrom the agent shell behind the active firewall. It succeeded (exit 0) with zero blocked domains, including SDK bootstrap. Nothing about restore depends on running before the firewall. - But a later step in that workflow depends on the artifact it produces. The
Install verify toolstep runs./dotnet.sh tool install …, which needs the repo-local.dotnetthat the restore step creates. Removing the restore step without adjusting that step would break it. Sincebuild.shre-bootstraps.dotneton demand, the cleanest change is to make the verify-tool step not depend on a pre-existing repo-local SDK (theactions/setup-dotnetsteps already provide a system SDK), rather than deleting one line.
The tradeoff is cost, not capability: removing the step moves a ~1m15s restore plus a 240 MB SDK download into the agent session.
No firewall rules were broadened speculatively and no repository files were edited or committed.
Description
Copilot setup restore currently masks agent-shell firewall gaps; this documents the reproducible probe and the first observed required blocker without proposing speculative access for later build/test/run steps.
docs/ci/copilot-agent-firewall.mdwith cold-cache commands for environment capture, NuGet cache cleanup, restore, targeted build/test, AppHost run, and blocked-entry extraction.https://github.com/microsoft/aspire/actions/runs/33795336775952569852f1cab078e655a2cd2cc0716034c4985./build.sh -restorefailed restoringMicrosoft.DotNet.Arcade.Sdk.10.0.0-beta.26423.1ukkvsblobprodcus352.vsblob.vsassets.ioChecklist
<remarks />and<code />elements on your triple slash comments?