Skip to content

Document Copilot agent firewall probe blocker - #19912

Draft
Ankit Jain (radical) with Copilot wants to merge 2 commits into
mainfrom
copilot/identify-firewall-rules
Draft

Document Copilot agent firewall probe blocker#19912
Ankit Jain (radical) with Copilot wants to merge 2 commits into
mainfrom
copilot/identify-firewall-rules

Conversation

Copilot AI commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

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.

  • Probe runbook
    • Adds docs/ci/copilot-agent-firewall.md with cold-cache commands for environment capture, NuGet cache cleanup, restore, targeted build/test, AppHost run, and blocked-entry extraction.
  • Observed evidence
    • Session: https://github.com/microsoft/aspire/actions/runs/33795336775
    • Commit: 952569852f1cab078e655a2cd2cc0716034c4985
    • ./build.sh -restore failed restoring Microsoft.DotNet.Arcade.Sdk.10.0.0-beta.26423.1
    • Firewall blocked DNS for ukkvsblobprodcus352.vsblob.vsassets.io
  • Admin guidance
    • Proposes only the observed required endpoint.
    • Separates optional telemetry/control-plane endpoints.
    • Leaves setup-time restore removal for a later issue after admin changes are validated in three fresh sessions.

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No

Copilot AI balanced review requested due to automatic review settings September 3, 2026 19:15

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.

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>
Copilot AI review requested due to automatic review settings September 3, 2026 19:20
Copilot AI changed the title [WIP] Investigate firewall rules for Copilot builds and tests Document Copilot agent firewall probe blocker Sep 3, 2026
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 19912

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 19912"

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.

🟡 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
@radical

Copy link
Copy Markdown
Member

[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 vsblob.vsassets.io.

For each phase:

  1. Record the current byte offset of ${COPILOT_AGENT_FIREWALL_LOG_FILE:-/home/runner/work/_temp/runtime-logs/fw.jsonl}.
  2. Capture the command's complete output and exit code under /tmp/aspire-copilot-firewall-probe.
  3. Inspect only the firewall-log entries after that offset.
  4. Report unique allowed and blocked domains, if the log contains both. If it records only blocked traffic, state that explicitly.
  5. Do not include URL query strings, credentials, tokens, or other sensitive values.
  6. Tie each blocked domain to the phase and command that required it.

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.log

Stop 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.log

CLI E2E starter and AppHost

ASPIRE_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.log

CLI E2E standalone dashboard

ASPIRE_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.log

Hosting dashboard and container paths

dotnet 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.log

Dashboard server startup

dotnet 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.log

Dashboard component rendering

dotnet 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.log

Dashboard Playwright browser path

This 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.log

Continue 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:

  • Every command and exit code.
  • Every newly required domain, grouped by phase.
  • Which traffic was already permitted by the recommended allowlist.
  • Optional telemetry/control-plane traffic separately from required package, tool, browser, container, or test traffic.
  • Any phase that could not run and the exact blocker.

Do not broaden firewall rules speculatively and do not edit or commit repository files.

Copilot AI commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

[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 en...

Probe run from the cloud-agent shell with the firewall enabled. No repository files were modified or committed (git status clean, HEAD still 9395ca9). Logs are under /tmp/aspire-copilot-firewall-probe.

Result: restore still blocked, so phases 2–8 were not run

The custom allowlist entry did not take effect in this session.

Commands and exit codes

Phase Command Exit code
0 dotnet nuget locals all --clear + rm -rf artifacts/... 0
1 ./build.sh -restore (with set -o pipefail) 1
diag curl -sSL <dnceng.pkgs.visualstudio.com flat2 nupkg> 0 (redirect followed, DNS resolution of target failed)
2–8 localhive / CLI E2E / hosting / dashboard / Playwright not run (stopped per instructions after restore failure)

Phase 1 firewall log (offset 33260 → 95665)

The log records both allowed and blocked traffic.

Allowed (HTTP proxy handling request):

  • pkgs.dev.azure.com (24) — required, NuGet feed index/metadata
  • dnceng.pkgs.visualstudio.com (5) — required, NuGet feed index/metadata
  • api.enterprise.githubcopilot.com (27) — agent control plane, not build traffic
  • telemetry.enterprise.githubcopilot.com (1) — optional telemetry

Blocked (DNS BLOCKED, 12 entries, all identical):

  • ukkvsblobprodcus352.vsblob.vsassets.iobecause: NotInAllowList, ruleSource: NotInAllowList, ruleSourceComment: "Domain doesn't match any allowlist prefixes", blockedAt: dns, port 53. Attributed to the dotnet msbuild ... artifacts/toolset/restore.proj /t:__WriteToolsetLocation process, i.e. the Arcade SDK resolution step of phase 1.

Restore error: Failed to download package 'Microsoft.DotNet.Arcade.Sdk.10.0.0-beta.26423.1'Resource temporarily unavailable (ukkvsblobprodcus352.vsblob.vsassets.io:443)MSB4236: The SDK 'Microsoft.DotNet.Arcade.Sdk' specified could not be found.

Why the allowlist change did not help

This session's effective allow list (/home/runner/work/_temp/runtime-logs/params.log, line 2) contains no vsassets.io entry at all. Its Azure-related entries are only dotnetbuilds.blob.core.windows.net and dnceng.pkgs.visualstudio.com. Two possibilities, both for an admin to confirm:

  1. The allowlist update has not propagated to agent sessions yet, or
  2. The entry was added in a form the matcher rejects. The block reason is "Domain doesn't match any allowlist prefixes", and the blocked host is the region-specific ukkvsblobprodcus352.vsblob.vsassets.io, so a bare vsblob.vsassets.io would not cover it. The list already uses the wildcard form elsewhere (https://*.githubusercontent.com), so https://*.vsblob.vsassets.io is the shape most likely to match.

Additional evidence: no alternate feed avoids this host

I checked whether the already-allowed dnceng.pkgs.visualstudio.com mirror serves the package content directly. It does not — the flat2 nupkg request returns HTTP 303 and redirects to the same ukkvsblobprodcus352.vsblob.vsassets.io CDN host, which then fails DNS resolution. That phase produced exactly one allowed entry (dnceng.pkgs.visualstudio.com) and one DNS BLOCKED entry for the same host. So allowing the feed front doors alone is insufficient; the vsassets CDN host is genuinely required.

Required vs optional, so far

  • Required (package acquisition): *.vsblob.vsassets.io (observed: ukkvsblobprodcus352.vsblob.vsassets.io). pkgs.dev.azure.com and dnceng.pkgs.visualstudio.com are already permitted.
  • Optional (telemetry/control plane): telemetry.enterprise.githubcopilot.com, api.enterprise.githubcopilot.com.
  • Unknown: every later phase (localhive, CLI E2E starter/dashboard, hosting, dashboard server/components, Playwright browser download, container traffic). No evidence gathered — restore is a hard prerequisite for all of them.

No firewall rules were broadened and nothing speculative was added. Once the allowlist actually shows a matching vsblob.vsassets.io entry in params.log, re-run this probe and phases 2–8 can be measured.

@radical

Copy link
Copy Markdown
Member

[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 vsblob.vsassets.io.

For each phase:

  1. Record the current byte offset of ${COPILOT_AGENT_FIREWALL_LOG_FILE:-/home/runner/work/_temp/runtime-logs/fw.jsonl}.
  2. Capture the command's complete output and exit code under /tmp/aspire-copilot-firewall-probe.
  3. Inspect only the firewall-log entries after that offset.
  4. Report unique allowed and blocked domains, if the log contains both. If it records only blocked traffic, state that explicitly.
  5. Do not include URL query strings, credentials, tokens, or other sensitive values.
  6. Tie each blocked domain to the phase and command that required it.

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.log

Stop 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.log

CLI E2E starter and AppHost

ASPIRE_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.log

CLI E2E standalone dashboard

ASPIRE_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.log

Hosting dashboard and container paths

dotnet 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.log

Dashboard server startup

dotnet 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.log

Dashboard component rendering

dotnet 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.log

Dashboard Playwright browser path

This 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.log

Continue 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:

  • Every command and exit code.
  • Every newly required domain, grouped by phase.
  • Which traffic was already permitted by the recommended allowlist.
  • Optional telemetry/control-plane traffic separately from required package, tool, browser, container, or test traffic.
  • Any phase that could not run and the exact blocker.

Do not broaden firewall rules speculatively and do not edit or commit repository files.

Copilot AI commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

[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 en...

Probe re-run from the cloud-agent shell with the firewall enabled. vsblob.vsassets.io is now present in this session's allowlist (params.log), and restore succeeded, so all phases ran. No repository files were modified or committed (git status clean, HEAD still 9395ca9). Logs are under /tmp/aspire-copilot-firewall-probe.

Commands and exit codes

Phase Command Exit Firewall blocks
0 dotnet nuget locals all --clear + rm -rf artifacts/... 0
1 ./build.sh -restore 0 none
2 ./localhive.sh -o /tmp/aspire-e2e -r linux-x64 --archive 0 none
3 CLI E2E *.CreateAndRunAspireStarterProject 2 cli.github.com
4 CLI E2E *.DashboardRunWithOtelTracesReturnsNoTraces 2 cli.github.com
5 Hosting *.StartAsync_DashboardUrls_DisplayPropertiesSet + *.VerifyContainerArgs 2 none (non-firewall failure)
6 Dashboard *.EndPointAccessors_AppStarted_BrowserGet_Success 0 (1 passed) none
7 Dashboard Components *.UpdateResources_FiltersUpdated 0 (1 passed) none
8 Dashboard Playwright *.ViewOptionsMenu_ReportsExpandedState 2 none (non-firewall failure)
8b Isolated cold playwright install chromium (added, see below) 0 none

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 phase

Phase 1 — restore (required, package acquisition). No blocks. The previously-blocked CDN is now allowed and is used heavily:

  • pkgs.dev.azure.com (3691), <region>.vsblob.vsassets.io (1048 requests across ~90 distinct region hostnames, e.g. 0t3vsblobprodcus362, 4myvsblobprodcus32, kijvsblobprodcus387), dnceng.pkgs.visualstudio.com (648).
  • The wide spread of region hostnames confirms the wildcard/suffix form is necessary — pinning individual hosts would not be stable.

Phase 2 — localhive archive (required). No blocks. Only already-permitted hosts: pkgs.dev.azure.com, dnceng.pkgs.visualstudio.com, <region>.vsblob.vsassets.io. Produced /tmp/aspire-e2e.tar.gz (590 MB).

Phases 3 and 4 — CLI E2E (required, newly blocked).

  • Blocked: cli.github.comcurl -fsSL inside the Dockerfile.e2e image build.
  • Failure chain: curl: (6) Could not resolve host: cli.github.comgpg: no valid OpenPGP data found[runtime 4/15] RUN apt-get ... gh exits 2 → docker build failed with exit code 1 → both tests fail. This is a firewall block, not a product failure.
  • Already permitted and used by the same image build: archive.ubuntu.com, security.ubuntu.com. Container base image pulls from mcr.microsoft.com/dotnet/sdk:10.0 succeeded.
  • Both phases fail at the identical step, so cli.github.com currently gates the entire CLI E2E suite.

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 ::1 and the client Watch timed out — Polly.Timeout.TimeoutRejectedException from KubernetesService.ExecuteWithRetry / DcpExecutor.CreateDcpObjectsAsync. Classified as a product/environment failure, not a firewall block.

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).

  • Phase 8's test failed with System.TimeoutException ... after reaching the limit of 30000ms on a UI interaction, with zero blocked entries, so it is a product/test failure rather than a firewall block. The dashboard itself started fine (Now listening on: http://127.0.0.1:44355).
  • Phase 8 did not actually exercise a cold download — the deps directory repopulated with no download traffic logged — so I added phase 8b to probe the browser download path in isolation: removed artifacts/bin/playwright-deps and ~/.cache/ms-playwright, then ran the same Microsoft.Playwright.dll install chromium command the ProvisionBrowsersForPlaywright target uses. It succeeded (exit 0), downloading ffmpeg and Chromium Headless Shell (104.3 MiB).
  • Domains used, all allowed, none blocked: cdn.playwright.dev, playwright.download.prss.microsoft.com. Neither appears verbatim in the printed allowlist, but both were permitted in practice, so no change is needed for the browser path.

Traffic already permitted by the current allowlist

pkgs.dev.azure.com, dnceng.pkgs.visualstudio.com, *.vsblob.vsassets.io, archive.ubuntu.com, security.ubuntu.com, mcr.microsoft.com (container base image), cdn.playwright.dev, playwright.download.prss.microsoft.com.

Optional telemetry / control-plane traffic

Present in every phase window, unrelated to build/test correctness: api.enterprise.githubcopilot.com, telemetry.enterprise.githubcopilot.com, dc.services.visualstudio.com (571 requests in phase 2 — Visual Studio App Insights from the build/CLI, not required for the build to succeed).

Phases that could not run, and the exact blocker

None were skipped. The only firewall-caused failures are phases 3 and 4, blocked on cli.github.com during the E2E Docker image build.

Single new required endpoint

  • cli.github.com — required by Dockerfile.e2e to fetch the GitHub CLI apt keyring and install gh. This is the only newly observed required domain across all phases.

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.

@radical

Copy link
Copy Markdown
Member

[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 https://cli.github.com/packages/.

The default-branch setup workflow runs ./build.sh -restore before the agent starts. Simulate removing that step by deleting all state it created, including the repo-local SDK, NuGet caches, and build artifacts. Then perform the restore again from the agent shell after the firewall is active.

For every command:

  1. Record the firewall-log byte offset immediately before it.
  2. Capture complete output and the real exit code under /tmp/aspire-copilot-firewall-rerun.
  3. Inspect only firewall entries after that offset.
  4. Report unique allowed and blocked domains for that phase.
  5. Do not include query strings, credentials, tokens, or other sensitive values.
  6. Continue to later independent phases after failures.

First confirm that the effective firewall configuration for this run contains https://cli.github.com/packages/. If it does not, stop and report that the updated rule was not applied.

Remove pre-agent restore state

Use the system SDK installed by the later actions/setup-dotnet setup steps after deleting the repo-local SDK:

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.log

Report whether dotnet --info still works after .dotnet is removed. If it does not, stop the restore and CLI phases and explain which setup dependency remains necessary.

Agent-shell cold restore

./build.sh -restore \
  2>&1 | tee /tmp/aspire-copilot-firewall-rerun/02-restore.log

This 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.log

Stop the CLI test phases if restore or local-hive creation fails, but continue with Hosting and Playwright when possible.

CLI E2E starter and AppHost

ASPIRE_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.log

CLI E2E standalone dashboard

ASPIRE_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.log

Hosting dashboard path

dotnet 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.log

Hosting container path

dotnet 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.log

Dashboard Playwright browser path

Remove 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.log

If 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:

  • Every command and exit code.
  • Whether deleting .dotnet and all caches accurately left a usable system SDK.
  • Every required domain, grouped by phase.
  • Whether https://cli.github.com/packages/ admitted the keyring, APT metadata, and gh package.
  • Any redirect host discovered afterward.
  • Optional telemetry/control-plane traffic separately.
  • Non-firewall product/test failures with their key error and retry result.
  • Whether the evidence supports removing ./build.sh -restore || true from .github/workflows/copilot-setup-steps.yml.

Do not broaden firewall rules speculatively and do not edit or commit repository files.

Copilot AI commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

[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....

Rerun from the cloud-agent shell with the firewall enabled. No repository files were modified or committed (git status clean, HEAD still 9395ca9). Logs are under /tmp/aspire-copilot-firewall-rerun.

Firewall configuration confirmed

params.log for this session contains both https://cli.github.com/packages/ and vsblob.vsassets.io. The updated rule was applied, so I proceeded.

Commands and exit codes

Phase Command Exit Firewall blocks
00 dotnet build-server shutdown + rm -rf .dotnet artifacts/... + dotnet --info 0 none
01 dotnet nuget locals all --clear 0 none
02 ./build.sh -restore (cold, no .dotnet) 0 none
03 ./localhive.sh -o /tmp/aspire-e2e -r linux-x64 --archive 0 none
04 CLI E2E *.CreateAndRunAspireStarterProject 2 none (new cause, see below)
05 CLI E2E *.DashboardRunWithOtelTracesReturnsNoTraces 2 none (same cause)
06 Hosting *.StartAsync_DashboardUrls_DisplayPropertiesSet 2 none
06r same, retry 2 none (reproduced)
07 Hosting *.VerifyContainerArgs 2 none
07r same, retry 2 none (reproduced)
08 Dashboard Playwright *.ViewOptionsMenu_ReportsExpandedState, cold browser cache 2 none
08r same, retry 2 none (reproduced)
diag host-side curl of keyring / APT metadata / gh .deb 0 none

Every phase in this rerun recorded zero blocked domains. The cli.github.com DNS block from the previous run is gone.

Did deleting .dotnet leave a usable system SDK?

Yes, but with an important caveat that changes the conclusion.

  • After rm -rf .dotnet, dotnet --info worked: SDK 10.0.400 at /usr/share/dotnet (also 8.0.130), host 10.0.11. global.json pins 10.0.400 with rollForward: major, so the system SDK satisfies it.
  • However, ./build.sh -restore did not use it. It re-bootstrapped its own repo-local SDK, downloading dotnet-install.sh and the full 240 MB dotnet-sdk-10.0.400-linux-x64.tar.gz from builds.dotnet.microsoft.com. So the repo-local .dotnet is self-healing and the system SDK is not what made the restore work.

Required domains, by phase

Phase 02 — cold restore (SDK bootstrap + package payload). All allowed, no blocks:

  • pkgs.dev.azure.com (3653), <region>.vsblob.vsassets.io (1048), dnceng.pkgs.visualstudio.com (648) — NuGet feeds and package payloads.
  • builds.dotnet.microsoft.com (42) and ci.dot.net (36) — SDK/toolset bootstrap. These are newly exercised in this run because .dotnet was deleted; the earlier probes never removed it, so they never appeared. Both are already permitted.
  • Restore succeeded in 1m 15s with 0 errors.

Phase 03 — local-hive. No blocks; only already-permitted feed hosts. Produced /tmp/aspire-e2e.tar.gz (590 MB).

Phases 04 and 05 — CLI E2E. No blocks. Allowed and used by the image build: archive.ubuntu.com, security.ubuntu.com.

Phases 06, 06r, 07, 07r — hosting. No blocks; only feed/telemetry traffic.

Phases 08, 08r — Playwright. No blocks. Unlike the previous run, the cold cache genuinely forced acquisition in-phase: cdn.playwright.dev and playwright.download.prss.microsoft.com were both permitted and the browsers installed successfully. Browser acquisition behind the firewall is confirmed working.

Did https://cli.github.com/packages/ admit the keyring, APT metadata, and gh package?

Yes — verified directly from the host, all logged as ALLOWED with no blocks:

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 afterwardcli.github.com served all three directly, unlike the earlier dnceng.pkgs.visualstudio.comvsblob.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:

  1. Network-wise it is fully supported. I deleted .dotnet, every artifacts directory, and all NuGet caches, then ran ./build.sh -restore from 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.
  2. But a later step in that workflow depends on the artifact it produces. The Install verify tool step runs ./dotnet.sh tool install …, which needs the repo-local .dotnet that the restore step creates. Removing the restore step without adjusting that step would break it. Since build.sh re-bootstraps .dotnet on demand, the cleanest change is to make the verify-tool step not depend on a pre-existing repo-local SDK (the actions/setup-dotnet steps 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Identify firewall rules for on-demand Copilot builds and tests

3 participants