Skip to content

Improve Linux dev certificate trust handling#18851

Merged
danegsta merged 18 commits into
mainfrom
danegsta/investigate-ssl-cert-dir
Jul 24, 2026
Merged

Improve Linux dev certificate trust handling#18851
danegsta merged 18 commits into
mainfrom
danegsta/investigate-ssl-cert-dir

Conversation

@danegsta

Copy link
Copy Markdown
Member

Description

Linux AppHosts launched with dotnet run or an IDE can bypass the aspire run setup that materializes SSL_CERT_DIR. When Aspire then configures local project/executable resources with only its generated certificate directory, Linux workloads can lose OpenSSL's implicit system certificate roots and fail outbound HTTPS requests.

This change preserves Linux system trust for append-scope local executable resources by appending the existing SSL_CERT_DIR when it is set, or by materializing well-known system certificate directories that actually exist when it is unset. Explicit SSL_CERT_DIR values, including an empty value, are preserved as user configuration.

It also expands aspire doctor to detect stale, missing, or corrupt OpenSSL development certificate cache entries under the dev-certs trust directory and recommend aspire certs clean followed by aspire certs trust. The Unix trust probe now treats corrupt expected PEM files as an OpenSSL trust miss instead of aborting the entire doctor check.

User-facing usage

Users with a stale or corrupt Linux development certificate setup can discover the repair guidance with:

aspire doctor
aspire certs clean
aspire certs trust

Validation:

dotnet test --project tests/Aspire.Cli.Tests/Aspire.Cli.Tests.csproj --no-launch-profile -- --filter-class "*.DevCertsCheckTests" --filter-class "*.UnixCertificateManagerTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"
dotnet test --project tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj --no-launch-profile -- --filter-method "*.ExecutableCertificateDirectoriesPath_IncludesExistingWellKnownDirectoriesForAppendWhenSslCertDirIsUnset" --filter-method "*.ExecutableCertificateDirectoriesPath_PreservesExistingSslCertDirForAppendWithoutAddingWellKnownDirectories" --filter-method "*.ExecutableCertificateDirectoriesPath_PreservesEmptySslCertDirForAppendWithoutAddingWellKnownDirectories" --filter-method "*.ExecutableCertificateDirectoriesPath_DoesNotIncludeExistingOrWellKnownDirectoriesForOverride" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"
dotnet build /t:UpdateXlf src/Aspire.Cli/Aspire.Cli.csproj

Fixes #18066

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

Preserve Linux system certificate directories for local executable resources when AppHosts are launched without aspire run materializing SSL_CERT_DIR, and add doctor diagnostics for stale or corrupt OpenSSL dev certificate cache entries.

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

Copilot-Session: 677e0f4a-653a-4d97-b690-f86d67107226
Copilot AI review requested due to automatic review settings July 21, 2026 23:13
@github-actions

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 -- 18851

Or

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

@github-actions

This comment has been minimized.

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

Preserves Linux system certificate trust for local workloads and improves diagnosis of broken OpenSSL development-certificate caches.

Changes:

  • Appends ambient or well-known certificate directories to SSL_CERT_DIR.
  • Adds doctor warnings and repair guidance for missing, stale, or corrupt caches.
  • Makes Unix trust probing tolerate corrupt PEM files and adds regression tests.

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/Aspire.Hosting/Dcp/ExecutableCreator.cs Builds Linux certificate-directory paths.
src/Aspire.Cli/Utils/EnvironmentChecker/DevCertsCheck.cs Adds OpenSSL cache diagnostics.
src/Aspire.Cli/Certificates/CertificateGeneration/UnixCertificateManager.cs Handles corrupt PEM files.
src/Aspire.Cli/Resources/DoctorCommandStrings.resx Adds diagnostic strings.
src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs Exposes new resources.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.cs.xlf Synchronizes Czech localization.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.de.xlf Synchronizes German localization.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.es.xlf Synchronizes Spanish localization.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.fr.xlf Synchronizes French localization.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.it.xlf Synchronizes Italian localization.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.ja.xlf Synchronizes Japanese localization.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.ko.xlf Synchronizes Korean localization.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.pl.xlf Synchronizes Polish localization.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.pt-BR.xlf Synchronizes Portuguese localization.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.ru.xlf Synchronizes Russian localization.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.tr.xlf Synchronizes Turkish localization.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.zh-Hans.xlf Synchronizes Simplified Chinese localization.
src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.zh-Hant.xlf Synchronizes Traditional Chinese localization.
tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs Tests certificate-directory composition.
tests/Aspire.Cli.Tests/Utils/DevCertsCheckTests.cs Tests cache diagnostics.
tests/Aspire.Cli.Tests/Certificates/UnixCertificateManagerTests.cs Tests corrupt PEM handling.
Files not reviewed (1)
  • src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs: Generated file

Comment thread src/Aspire.Cli/Utils/EnvironmentChecker/DevCertsCheck.cs Outdated
Only inspect OpenSSL cache files derived from certificates found in the current user certificate store so doctor does not report unrelated corrupt files that aspire certs clean will not remove.

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

Copilot-Session: 677e0f4a-653a-4d97-b690-f86d67107226
Copilot AI review requested due to automatic review settings July 21, 2026 23:26
@github-actions

This comment has been minimized.

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

Copilot reviewed 20 out of 21 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs: Generated file

Comment thread src/Aspire.Cli/Utils/EnvironmentChecker/DevCertsCheck.cs
Comment thread tests/Aspire.Cli.Tests/Certificates/UnixCertificateManagerTests.cs Outdated
Comment thread src/Aspire.Cli/Resources/DoctorCommandStrings.resx Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 677e0f4a-653a-4d97-b690-f86d67107226
Copilot AI review requested due to automatic review settings July 21, 2026 23:52

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

Copilot reviewed 20 out of 21 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs: Generated file
Comments suppressed due to low confidence (1)

src/Aspire.Cli/Utils/EnvironmentChecker/DevCertsCheck.cs:277

  • This only checks files derived from certificates still in the current-user store, so it cannot detect an orphaned stale aspnetcore-localhost-*.pem entry. If the current certificate has a valid cache file and an older PEM remains alongside it, this returns no warning even though OpenSSL still trusts the old certificate. Also, aspire certs clean currently removes cache files only for certificates found in the store, so the suggested repair would leave that orphan behind. Please enumerate the managed PEM entries against the current thumbprint set and update cleanup to remove orphaned entries.
        foreach (var certificate in currentCertificates)
        {
            var certificateFileName = GetOpenSslCertificateFileName(certificate.Thumbprint!);
            var certificateFile = Path.Combine(trustPath, certificateFileName);

@github-actions

This comment has been minimized.

@danegsta

Copy link
Copy Markdown
Member Author

PR Testing Report

PR Information

Artifact Version Verification

  • Expected Commit: 37f6e03
  • Installed Version: 13.5.0-pr.18851.g37f6e03c
  • Status: Verified. The installed PR CLI version includes the PR head short SHA 37f6e03c.

Changes Analyzed

Files Changed

  • src/Aspire.Hosting/Dcp/ExecutableCreator.cs - local executable/project certificate directory path construction
  • src/Aspire.Cli/Certificates/CertificateGeneration/UnixCertificateManager.cs - Linux OpenSSL trust probing for corrupt expected PEM files
  • src/Aspire.Cli/Utils/EnvironmentChecker/DevCertsCheck.cs - aspire doctor Linux OpenSSL dev-cert cache diagnostics
  • src/Aspire.Cli/Resources/DoctorCommandStrings.* and XLF files - localized diagnostic text
  • tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs - Hosting SSL_CERT_DIR regression coverage
  • tests/Aspire.Cli.Tests/Utils/DevCertsCheckTests.cs - doctor diagnostic regression coverage
  • tests/Aspire.Cli.Tests/Certificates/UnixCertificateManagerTests.cs - Unix trust probe regression coverage

Change Categories

  • CLI changes detected
  • Hosting changes detected
  • Dashboard changes detected
  • Template changes detected
  • Client/Component changes detected
  • CI infrastructure changes detected
  • VS Code extension changes detected
  • Test changes detected

Test Scenarios Executed

Scenario 1: PR dogfood CLI artifact verification

Objective: Install the dogfood CLI for PR #18851 in a Linux container workspace and verify it matches the PR head commit.
Coverage Type: Artifact/version verification
Status: Passed

Steps:

  1. Created a fresh temp workspace for the repo container runner.
  2. Ran the PR dogfood install command from the PR comment.
  3. Executed /workspace/.aspire/dogfood/pr-18851/bin/aspire --version.
  4. Verified the output contains 37f6e03c.

Evidence:

  • Install log: logs/install-and-version.log
  • Version log: logs/version-verified.log

Observations:

  • Installed CLI version: 13.5.0-pr.18851.g37f6e03c.
  • The installer used workflow run https://github.com/microsoft/aspire/actions/runs/29878512637.

Scenario 2: Focused Linux source regression tests

Objective: Verify the PR's Linux certificate handling changes with the focused test coverage added by the PR.
Coverage Type: Regression/unit tests
Status: Passed

Steps:

  1. Cloned the PR branch into the Linux container workspace and checked out commit 37f6e03cfc2fa251c265742c942161c85c70eb27.
  2. Ran ./restore.sh to install the repo-local Linux SDK.
  3. Ran focused CLI certificate tests:
    /workspace/src/.dotnet/dotnet test --project tests/Aspire.Cli.Tests/Aspire.Cli.Tests.csproj --no-launch-profile -- --filter-class "*.DevCertsCheckTests" --filter-class "*.UnixCertificateManagerTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"
  4. Ran focused Hosting executable certificate directory tests:
    /workspace/src/.dotnet/dotnet test --project tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj --no-launch-profile -- --filter-method "*.ExecutableCertificateDirectoriesPath_IncludesExistingWellKnownDirectoriesForAppendWhenSslCertDirIsUnset" --filter-method "*.ExecutableCertificateDirectoriesPath_PreservesExistingSslCertDirForAppendWithoutAddingWellKnownDirectories" --filter-method "*.ExecutableCertificateDirectoriesPath_PreservesEmptySslCertDirForAppendWithoutAddingWellKnownDirectories" --filter-method "*.ExecutableCertificateDirectoriesPath_DoesNotIncludeExistingOrWellKnownDirectoriesForOverride" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"

Evidence:

  • Full test log: logs/linux-source-regression-tests-rerun.log

Observations:

  • Aspire.Cli.Tests: 25 passed, 0 failed, 0 skipped.
  • Aspire.Hosting.Tests: 4 passed, 0 failed, 0 skipped.
  • Directly mounting the macOS worktree into the Linux container did not work because the host worktree metadata and SDK layout are not usable inside the container; the successful run used a fresh Linux clone inside the temp workspace.

Scenario 3: Generated AppHost smoke with executable SSL_CERT_DIR probe

Objective: Verify the PR dogfood CLI can create and run a fresh generated AppHost, and that a local executable resource receives an appended Linux SSL_CERT_DIR containing Aspire's generated cert directory plus the existing system/dev-cert directories.
Coverage Type: Happy path plus changed Hosting behavior smoke
Status: Passed

Steps:

  1. Created a fresh C# file-based AppHost from the PR hive:
    /workspace/.aspire/dogfood/pr-18851/bin/aspire new aspire-empty \
      --name PrSmokeEmpty \
      --output /workspace/scenario-generated-app-empty/PrSmokeEmpty \
      --source /workspace/.aspire/hives/pr-18851/packages \
      --version 13.5.0-pr.18851.g37f6e03c \
      --language csharp \
      --localhost-tld false \
      --suppress-agent-init \
      --non-interactive
  2. Added a temporary executable resource to the generated AppHost that writes its SSL_CERT_DIR to /workspace/logs/executable-ssl-cert-dir.txt.
  3. Ran the AppHost with the PR CLI in the Linux container.
  4. Verified the executable received an appended value:
    /tmp/aspire-dcpdpLcOq/sslprobe-qtnjdkcx/certs:/usr/lib/ssl/certs:/workspace/.aspnet/dev-certs/trust
    

Evidence:

  • Creation/run logs: logs/generated-app-empty-smoke-rerun.log, logs/generated-app-empty-detached-run-with-sdk.log, logs/generated-app-executable-ssl-cert-dir.log
  • Assertion log: logs/generated-app-executable-ssl-cert-dir-assertion.log
  • Captured value: logs/executable-ssl-cert-dir.txt

Observations:

  • The generated AppHost started successfully and produced a dashboard URL.
  • The executable's SSL_CERT_DIR preserved the existing Linux OpenSSL system directory (/usr/lib/ssl/certs) and dev-certs trust directory while prepending Aspire's generated certificate directory.
  • An initial attempt used the old aspire-starter template name, which this PR build no longer exposes; the smoke was rerun with the available aspire-empty template.
  • File-based AppHost execution required a .NET SDK on PATH in the container; the repo-local SDK from ./restore.sh was used.

Scenario 4: aspire doctor corrupt OpenSSL cache diagnostic smoke

Objective: Verify the dogfood CLI surfaces the new Linux doctor diagnostic and repair guidance when the expected OpenSSL dev-cert cache PEM is corrupt.
Coverage Type: Unhappy path / diagnostic behavior
Status: Passed

Steps:

  1. Located the generated OpenSSL dev-cert cache file under /workspace/.aspnet/dev-certs/trust.
  2. Temporarily replaced that expected PEM with invalid content.
  3. Ran:
    /workspace/.aspire/dogfood/pr-18851/bin/aspire doctor --non-interactive
  4. Restored the original PEM.
  5. Verified the output includes the OpenSSL HTTPS development certificate cache contains unreadable certificate files warning and recommends aspire certs clean / aspire certs trust.

Evidence:

  • Doctor smoke log: logs/doctor-corrupt-cache-smoke.log
  • Captured doctor output: logs/doctor-corrupt-cache-output.txt

Observations:

  • aspire doctor reported the corrupt expected PEM by deterministic dev-cert cache filename.
  • The diagnostic included guidance to run aspire certs clean and then aspire certs trust.
  • The command exited 0 while reporting warnings, which is consistent with doctor warning behavior.

CI Infrastructure Validation

N/A - this PR does not change GitHub Actions, Azure DevOps pipelines, CI scripts, or CI selection/build/test plumbing.

Summary

Scenario Status Notes
PR dogfood CLI artifact verification Passed Installed version matched PR head short SHA
Focused Linux source regression tests Passed 25 CLI tests and 4 Hosting tests passed in Linux container
Generated AppHost executable SSL_CERT_DIR smoke Passed Executable received generated cert dir plus /usr/lib/ssl/certs plus dev-cert trust dir
aspire doctor corrupt cache diagnostic smoke Passed Doctor reported corrupt expected PEM and clean/trust guidance

Overall Result

PR VERIFIED

Recommendations

  • No follow-up test issues found.

@danegsta
danegsta marked this pull request as ready for review July 22, 2026 00:49

@JamesNK JamesNK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Reviewed with 1 correctness issue: the recommended corrupt-cache repair does not succeed when openssl is unavailable.

Comment thread src/Aspire.Cli/Utils/EnvironmentChecker/DevCertsCheck.cs Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 677e0f4a-653a-4d97-b690-f86d67107226
Copilot AI review requested due to automatic review settings July 22, 2026 01:59
@github-actions

This comment has been minimized.

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

Copilot reviewed 20 out of 21 changed files in this pull request and generated 1 comment.

Files not reviewed (1)
  • src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs: Generated file

@github-actions

This comment has been minimized.

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

Copilot reviewed 21 out of 22 changed files in this pull request and generated 1 comment.

Files not reviewed (1)
  • src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs: Generated file
Comments suppressed due to low confidence (1)

src/Aspire.Cli/Utils/EnvironmentChecker/DevCertsCheck.cs:368

  • A failed or timed-out openssl x509 -hash probe is treated exactly like a successful probe with no matching hash entry. The caller then reports that subject-hash links are missing and recommends cleaning certificates, even when the cache is valid and OpenSSL itself simply could not run. Preserve an “unable to probe” state and either report that tool failure separately or skip the hash-link diagnosis; only return “missing” after a hash was computed successfully.
            var (success, hash) = await TryGetOpenSslHashAsync(openSslPath, certificateFile, cancellationToken).ConfigureAwait(false);
            return success &&
                HasMatchingHashEntry(trustPath, hash, certificate);

Comment thread tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 677e0f4a-653a-4d97-b690-f86d67107226
Copilot AI review requested due to automatic review settings July 23, 2026 20:53
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 677e0f4a-653a-4d97-b690-f86d67107226

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

Copilot reviewed 22 out of 23 changed files in this pull request and generated 1 comment.

Files not reviewed (1)
  • src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs: Generated file

Comment thread src/Aspire.Hosting/Dcp/ExecutableCreator.cs
Copilot AI review requested due to automatic review settings July 23, 2026 20:58
@github-actions

This comment has been minimized.

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

Copilot reviewed 22 out of 23 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs: Generated file
Comments suppressed due to low confidence (2)

src/Aspire.Cli/Utils/EnvironmentChecker/DevCertsCheck.cs:367

  • A failed openssl x509 probe is treated as proof that the subject-hash entry is missing. For example, the new start-failure test creates a valid 12345678.0 entry, yet this path reports “missing subject-hash links” and recommends only cleaning/retrusting; neither action fixes an OpenSSL executable that cannot start or returns an error. Preserve an “unable to validate with OpenSSL” outcome (or skip the hash-link diagnosis) separately from a successful hash computation whose matching entry is absent.
            var (success, hash) = await TryGetOpenSslHashAsync(openSslPath, certificateFile, cancellationToken).ConfigureAwait(false);
            return success &&
                HasMatchingHashEntry(trustPath, hash, certificate);

src/Aspire.Cli/Utils/EnvironmentChecker/DevCertsCheck.cs:435

  • This new external-process probe has no bounded timeout. aspire doctor passes only its user-cancellation token, so an openssl executable that hangs will block this check—and therefore all later doctor checks—indefinitely. Add a short linked timeout, as DeprecatedWorkloadCheck does, and treat timeout as an unavailable probe while ensuring the child process is terminated.
            var exitCode = await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false);

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

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

Copilot-Session: 677e0f4a-653a-4d97-b690-f86d67107226
Copilot AI review requested due to automatic review settings July 23, 2026 21:47

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

Copilot reviewed 22 out of 23 changed files in this pull request and generated 1 comment.

Files not reviewed (1)
  • src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs: Generated file
Comments suppressed due to low confidence (1)

src/Aspire.Cli/Utils/EnvironmentChecker/DevCertsCheck.cs:367

  • A failed openssl x509 probe is being treated as proof that the subject-hash entry is missing. The link can be present and valid while OpenSSL fails to start or exits nonzero (the new tests explicitly exercise this), so aspire doctor will report a false cache-corruption warning and recommend clean/trust even though that cannot repair the probe failure. Preserve an “unable to validate” result separately from “hash entry absent” (or skip the hash-link diagnosis when the probe fails).
            var (success, hash) = await TryGetOpenSslHashAsync(openSslPath, certificateFile, cancellationToken).ConfigureAwait(false);
            return success &&
                HasMatchingHashEntry(trustPath, hash, certificate);

Comment thread src/Aspire.Cli/Utils/EnvironmentChecker/DevCertsCheck.cs Outdated
@github-actions

This comment has been minimized.

@danegsta

Copy link
Copy Markdown
Member Author

PR Testing Report

PR Information

Artifact Version Verification

  • Current Expected Commit: 80bd41b
  • Installed Dogfood CLI Version: 13.5.0-pr.18851.g2e29241d
  • Dogfood Artifact Commit: 2e29241
  • Status: ⚠️ Source checkout verified at current head; dogfood CLI artifact was verified against the immediately previous PR commit. The current head adds only a regression-test correction after testing found the empty SSL_CERT_DIR test setup was deleting the inherited empty value.

Changes Analyzed

Files Changed

  • src/Aspire.Hosting/Dcp/ExecutableCreator.cs
  • src/Aspire.Hosting/ApplicationModel/CertificateTrustExecutionConfigurationGatherer.cs
  • src/Aspire.Cli/Utils/EnvironmentChecker/DevCertsCheck.cs
  • src/Aspire.Cli/Certificates/CertificateGeneration/UnixCertificateManager.cs
  • src/Aspire.Cli/Resources/DoctorCommandStrings.*
  • tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs
  • tests/Aspire.Cli.Tests/Utils/DevCertsCheckTests.cs
  • tests/Aspire.Cli.Tests/Certificates/UnixCertificateManagerTests.cs
  • tests/Aspire.Cli.Tests/TestServices/TestProcessExecutionFactory.cs

Change Categories

  • CLI changes detected: aspire doctor OpenSSL dev-cert cache diagnostics and Unix certificate manager cleanup/trust behavior.
  • Hosting changes detected: Linux executable/project SSL_CERT_DIR append fallback behavior.
  • Dashboard changes detected.
  • CI infrastructure changes detected.
  • VS Code extension changes detected.
  • Test changes detected.

Test Scenarios Executed

Scenario 1: Current-head Linux source regression tests

Objective: Verify the changed Linux certificate trust behavior and new doctor diagnostics from source at the current PR head.
Coverage Type: Regression / unhappy path / boundary
Status: ✅ Passed

Steps:

  1. Created a fresh source checkout in the container workspace from the current PR branch.
  2. Restored the repo with ./restore.sh.
  3. Ran focused CLI tests:
    dotnet test --project tests/Aspire.Cli.Tests/Aspire.Cli.Tests.csproj --no-launch-profile -- --filter-class "*.DevCertsCheckTests" --filter-class "*.UnixCertificateManagerTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"
  4. Ran focused Hosting tests:
    dotnet test --project tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj --no-launch-profile -- --filter-method "*.PlainExecutableCertificateDirectoriesPath_IncludesExistingWellKnownDirectoriesForAppendWhenSslCertDirIsUnsetOnLinux" --filter-method "*.PlainExecutableCertificateDirectoriesPath_PreservesAppHostSslCertDirForAppend" --filter-method "*.PlainExecutableCertificateDirectoriesPath_PreservesEmptyAppHostSslCertDirForAppend" --filter-method "*.PlainExecutableCertificateDirectoriesPath_DoesNotIncludeAppHostSslCertDirForOverride" --filter-method "*.PlainExecutableCertificateDirectoriesPath_IgnoresResourceSslCertDirForAppend" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"

Evidence:

  • Source checkout: /var/folders/97/44fp_02d4kd90s9p6sy_jwgh0000gn/T/aspire-pr18851-test-XXXXXX.EkNKsZFCnJ/repo-current
  • Source head: 80bd41b1e0e876fe646bf0b0bf635b86309b2165
  • Restore log: /var/folders/97/44fp_02d4kd90s9p6sy_jwgh0000gn/T/aspire-pr18851-test-XXXXXX.EkNKsZFCnJ/restore-current-head.log
  • CLI test log: /var/folders/97/44fp_02d4kd90s9p6sy_jwgh0000gn/T/aspire-pr18851-test-XXXXXX.EkNKsZFCnJ/cli-focused-tests-current-head.log
  • Hosting test log: /var/folders/97/44fp_02d4kd90s9p6sy_jwgh0000gn/T/aspire-pr18851-test-XXXXXX.EkNKsZFCnJ/hosting-focused-tests-current-head.log

Observations:

  • CLI focused tests passed: 32 total, 32 succeeded.
  • Hosting focused tests passed: 5 total, 5 succeeded.
  • The first Linux Hosting run exposed a test setup issue for empty ambient SSL_CERT_DIR: calling Environment.SetEnvironmentVariable("SSL_CERT_DIR", string.Empty) in the child process deleted the inherited empty value. The test was corrected and pushed as commit 80bd41b1e0.

Unhappy-path coverage included:

  • Missing/corrupt/stale OpenSSL dev-cert cache entries.
  • Missing OpenSSL hash entries.
  • Missing openssl fallback behavior.
  • OpenSSL hash-probe start failure.
  • Caller cancellation propagation.
  • Corrupt PEM trust miss and cleanup without openssl.
  • Missing ambient SSL_CERT_DIR Linux fallback to well-known dirs.
  • Non-empty ambient SSL_CERT_DIR preservation.
  • Empty ambient SSL_CERT_DIR suppression of inferred defaults.
  • Override scope not appending ambient/default dirs.
  • Resource-level WithEnvironment("SSL_CERT_DIR", ...) not influencing certificate-trust-owned SSL_CERT_DIR.

Scenario 2: Dogfood CLI empty AppHost smoke

Objective: Verify the PR dogfood CLI can create and run a generated Aspire app in the Linux container.
Coverage Type: Happy path smoke
Status: ✅ Passed with artifact caveat

Steps:

  1. Installed the PR dogfood CLI inside the repo container workspace.
  2. Verified installed CLI version: 13.5.0-pr.18851.g2e29241d.
  3. Created a file-based C# AppHost with:
    aspire new aspire-empty --name Pr18851Smoke --output /workspace/scenario-empty-smoke/Pr18851Smoke --source /workspace/.aspire/hives/pr-18851/packages --version 13.5.0-pr.18851.g2e29241d --language csharp --localhost-tld false --suppress-agent-init --non-interactive
  4. Ran the generated AppHost in the foreground under a bounded timeout with the repo-local .NET SDK on PATH.

Evidence:

  • New project log: /var/folders/97/44fp_02d4kd90s9p6sy_jwgh0000gn/T/aspire-pr18851-test-XXXXXX.EkNKsZFCnJ/empty-smoke-new.log
  • Run log: /var/folders/97/44fp_02d4kd90s9p6sy_jwgh0000gn/T/aspire-pr18851-test-XXXXXX.EkNKsZFCnJ/empty-smoke-run.log
  • Generated app: /var/folders/97/44fp_02d4kd90s9p6sy_jwgh0000gn/T/aspire-pr18851-test-XXXXXX.EkNKsZFCnJ/scenario-empty-smoke/Pr18851Smoke

Observations:

  • Project creation succeeded.
  • AppHost build and run reached the dashboard startup output.
  • The container reported Developer certificates may not be fully trusted (trust exit code was: PartiallyFailedToTrustTheCertificate), which is expected in this isolated Linux container context and did not block startup.
  • Detached mode was not used as final evidence because the repo container runner uses ephemeral containers; the foreground timeout run provided the reliable smoke signal.

Summary

Scenario Status Notes
Current-head Linux source regression tests ✅ Passed Source checkout at 80bd41b1e0; CLI 32/32 and Hosting 5/5 passed
Dogfood CLI empty AppHost smoke ✅ Passed with caveat Dogfood artifact was 2e29241d, before the final test-only commit

Overall Result

✅ PR source verified at current head. The Linux regression coverage passed in the repo container at 80bd41b1e0. The dogfood CLI artifact smoke also passed, but the installed artifact currently reports 2e29241d because a final test-only commit was pushed after the artifact was installed.

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

Copilot-Session: 677e0f4a-653a-4d97-b690-f86d67107226
Copilot AI review requested due to automatic review settings July 23, 2026 22:09

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

Copilot reviewed 22 out of 23 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs: Generated file
Comments suppressed due to low confidence (2)

src/Aspire.Cli/Utils/EnvironmentChecker/DevCertsCheck.cs:367

  • A failed openssl probe is treated as proof that the subject-hash entry is missing. If the executable exists but cannot start or exits unsuccessfully, HasMatchingHashEntry is never evaluated, yet doctor reports cache corruption and recommends only clean/trust; those commands can fail for the same broken OpenSSL installation. Preserve a distinct “probe failed” result and report OpenSSL repair/install guidance instead of the missing-link message.
            var (success, hash) = await TryGetOpenSslHashAsync(openSslPath, certificateFile, cancellationToken).ConfigureAwait(false);
            return success &&
                HasMatchingHashEntry(trustPath, hash, certificate);

src/Aspire.Cli/Utils/EnvironmentChecker/DevCertsCheck.cs:435

  • This new external process wait has no timeout, so a hung or malfunctioning openssl can block aspire doctor indefinitely. Other doctor subprocess checks use linked cancellation tokens with bounded timeouts; apply the same pattern here so a probe timeout degrades to a warning while caller cancellation still propagates.
            var exitCode = await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false);

@github-actions

Copy link
Copy Markdown
Contributor

Tests selector (audit mode)

The full test matrix and all jobs still run in audit mode. The tests and jobs below are what selective CI would run under enforcement.

50 / 100 test projects · 5 jobs, from 23 changed files.

Selected test projects (50 / 100)

Aspire.Cli.EndToEnd.Tests, Aspire.Cli.Tests, Aspire.EndToEnd.Tests, Aspire.Hosting.Analyzers.Tests, Aspire.Hosting.Azure.Kubernetes.Tests, Aspire.Hosting.Azure.Kusto.Tests, Aspire.Hosting.Azure.Tests, Aspire.Hosting.Blazor.Tests, Aspire.Hosting.Browsers.Tests, Aspire.Hosting.CodeGeneration.Go.Tests, Aspire.Hosting.CodeGeneration.Java.Tests, Aspire.Hosting.CodeGeneration.Python.Tests, Aspire.Hosting.CodeGeneration.Rust.Tests, Aspire.Hosting.CodeGeneration.TypeScript.Tests, Aspire.Hosting.Containers.Tests, Aspire.Hosting.DevTunnels.Tests, Aspire.Hosting.Docker.Tests, Aspire.Hosting.Dotnet.Tests, Aspire.Hosting.DotnetTool.Tests, Aspire.Hosting.EntityFrameworkCore.Tests, Aspire.Hosting.Foundry.Tests, Aspire.Hosting.Garnet.Tests, Aspire.Hosting.GitHub.Models.Tests, Aspire.Hosting.Go.Tests, Aspire.Hosting.JavaScript.Tests, Aspire.Hosting.Kafka.Tests, Aspire.Hosting.Keycloak.Tests, Aspire.Hosting.Kubernetes.Tests, Aspire.Hosting.Maui.Tests, Aspire.Hosting.Milvus.Tests, Aspire.Hosting.MongoDB.Tests, Aspire.Hosting.MySql.Tests, Aspire.Hosting.Nats.Tests, Aspire.Hosting.OpenAI.Tests, Aspire.Hosting.Oracle.Tests, Aspire.Hosting.Orleans.Tests, Aspire.Hosting.PostgreSQL.Tests, Aspire.Hosting.Python.Tests, Aspire.Hosting.Qdrant.Tests, Aspire.Hosting.RabbitMQ.Tests, Aspire.Hosting.Radius.Tests, Aspire.Hosting.Redis.Tests, Aspire.Hosting.RemoteHost.Tests, Aspire.Hosting.Seq.Tests, Aspire.Hosting.SqlServer.Tests, Aspire.Hosting.Testing.Tests, Aspire.Hosting.Tests, Aspire.Hosting.Valkey.Tests, Aspire.Hosting.Yarp.Tests, Aspire.Playground.Tests

Selected jobs (5)

cli-starter, deployment-e2e, extension-e2e, polyglot, typescript-api-compat


How these were chosen — grouped by what changed

⚠️ 43 of the 50 selected test projects come from a single change — src/Aspire.Hosting/ApplicationModel/CertificateTrustExecutionConfigurationGatherer.cs.

🔧 src/Aspire.Hosting/ApplicationModel/CertificateTrustExecutionConfigurationGatherer.cs (changed source)
43 via the project graph

show 43

Aspire.Hosting.Analyzers.Tests (2 hops), Aspire.Hosting.Azure.Kubernetes.Tests (2 hops), Aspire.Hosting.Azure.Kusto.Tests (2 hops), Aspire.Hosting.Azure.Tests, Aspire.Hosting.Browsers.Tests (2 hops), Aspire.Hosting.CodeGeneration.Go.Tests, Aspire.Hosting.CodeGeneration.Java.Tests, Aspire.Hosting.CodeGeneration.Python.Tests, Aspire.Hosting.CodeGeneration.Rust.Tests, Aspire.Hosting.CodeGeneration.TypeScript.Tests, Aspire.Hosting.Containers.Tests (2 hops), Aspire.Hosting.DevTunnels.Tests (2 hops), Aspire.Hosting.Docker.Tests (2 hops), Aspire.Hosting.DotnetTool.Tests (2 hops), Aspire.Hosting.EntityFrameworkCore.Tests (2 hops), Aspire.Hosting.Foundry.Tests (2 hops), Aspire.Hosting.Garnet.Tests (2 hops), Aspire.Hosting.GitHub.Models.Tests (2 hops), Aspire.Hosting.Go.Tests (2 hops), Aspire.Hosting.JavaScript.Tests (2 hops), Aspire.Hosting.Kafka.Tests (2 hops), Aspire.Hosting.Keycloak.Tests (2 hops), Aspire.Hosting.Kubernetes.Tests (2 hops), Aspire.Hosting.Maui.Tests, Aspire.Hosting.Milvus.Tests (2 hops), Aspire.Hosting.MongoDB.Tests (2 hops), Aspire.Hosting.MySql.Tests (2 hops), Aspire.Hosting.Nats.Tests (2 hops), Aspire.Hosting.OpenAI.Tests (2 hops), Aspire.Hosting.Oracle.Tests (2 hops), Aspire.Hosting.Orleans.Tests (2 hops), Aspire.Hosting.PostgreSQL.Tests (2 hops), Aspire.Hosting.Python.Tests (2 hops), Aspire.Hosting.Qdrant.Tests (2 hops), Aspire.Hosting.RabbitMQ.Tests (2 hops), Aspire.Hosting.Redis.Tests (2 hops), Aspire.Hosting.RemoteHost.Tests, Aspire.Hosting.Seq.Tests (2 hops), Aspire.Hosting.SqlServer.Tests (2 hops), Aspire.Hosting.Testing.Tests (2 hops), Aspire.Hosting.Valkey.Tests (2 hops), Aspire.Hosting.Yarp.Tests (2 hops), Aspire.Playground.Tests

🧪 tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs (changed test)
1 directly: Aspire.Hosting.Tests
3 via the project graph: Aspire.Hosting.Blazor.Tests, Aspire.Hosting.Dotnet.Tests, Aspire.Hosting.Radius.Tests

📦 affected project Aspire.Cli
1 test: Aspire.Cli.EndToEnd.Tests

📦 affected project Aspire.Hosting
1 test: Aspire.EndToEnd.Tests

🧪 tests/Aspire.Cli.Tests/Certificates/UnixCertificateManagerTests.cs (changed test)
1 directly: Aspire.Cli.Tests

🧪 tests/Aspire.Cli.Tests/TestServices/TestProcessExecutionFactory.cs (changed test)
1 directly: Aspire.Cli.Tests

🧪 tests/Aspire.Cli.Tests/Utils/DevCertsCheckTests.cs (changed test)
1 directly: Aspire.Cli.Tests

Job reasons

Job Triggered by
cli-starter • affected project Aspire.Cli
• selected test Aspire.Cli.Tests
deployment-e2e affected project Aspire.Cli
extension-e2e src/Aspire.Cli/Certificates/CertificateGeneration/UnixCertificateManager.cs, src/Aspire.Cli/Resources/DoctorCommandStrings.Designer.cs, src/Aspire.Cli/Resources/DoctorCommandStrings.resx, src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.cs.xlf, src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.de.xlf, src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.es.xlf, src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.fr.xlf, src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.it.xlf, src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.ja.xlf, src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.ko.xlf, src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.pl.xlf, src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.pt-BR.xlf, src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.ru.xlf, src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.tr.xlf, src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.zh-Hans.xlf, src/Aspire.Cli/Resources/xlf/DoctorCommandStrings.zh-Hant.xlf, src/Aspire.Cli/Utils/EnvironmentChecker/DevCertsCheck.cs, src/Aspire.Hosting/ApplicationModel/CertificateTrustExecutionConfigurationGatherer.cs, src/Aspire.Hosting/Dcp/ExecutableCreator.cs, tests/Aspire.Cli.Tests/Certificates/UnixCertificateManagerTests.cs, tests/Aspire.Cli.Tests/TestServices/TestProcessExecutionFactory.cs, tests/Aspire.Cli.Tests/Utils/DevCertsCheckTests.cs
• affected project Aspire.Cli
polyglot affected project Aspire.Cli
typescript-api-compat affected project Aspire.Cli

Selection computed for commit a655950.

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@danegsta
danegsta merged commit e638801 into main Jul 24, 2026
677 of 680 checks passed
@danegsta
danegsta deleted the danegsta/investigate-ssl-cert-dir branch July 24, 2026 00:05
@microsoft-github-policy-service microsoft-github-policy-service Bot added this to the 13.5 milestone Jul 24, 2026
@aspire-repo-bot

Copy link
Copy Markdown
Contributor

⚠️ Documentation was required for this change, but a docs PR could not be drafted automatically.

Documentation is required for this PR but the draft PR could not be created due to a persistent git patch generation failure in the safe-outputs tooling (Pinned SHA failed to generate patch: ERR_SYSTEM: Git command failed with status 1).

Triggered signals: cli_resource_strings_changed, pr_body_has_user_facing_section, pr_body_has_cli_flag_mention

Intended documentation change: Update src/frontend/src/content/docs/reference/cli/commands/aspire-doctor.mdx in release/13.5 to:

  • Note that Environment checks on Linux now includes an OpenSSL HTTPS development certificate cache check.
  • Add a Linux OpenSSL certificate cache checks subsection explaining the three detected conditions (missing certificate, unreadable files, missing subject-hash links), the aspire certs clean + aspire certs trust repair flow, and the dev-certs-openssl-cache JSON check name.

See the workflow run for details: https://github.com/microsoft/aspire/actions/runs/30055153085

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Outbound HTTPS requests fail on Linux because Aspire replaces SSL_CERT_DIR with temporary certificate directory

3 participants