Improve Linux dev certificate trust handling#18851
Conversation
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
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 18851Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 18851" |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
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
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
This comment has been minimized.
This comment has been minimized.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 677e0f4a-653a-4d97-b690-f86d67107226
There was a problem hiding this comment.
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-*.pementry. 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 cleancurrently 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);
This comment has been minimized.
This comment has been minimized.
PR Testing ReportPR Information
Artifact Version Verification
Changes AnalyzedFiles Changed
Change Categories
Test Scenarios ExecutedScenario 1: PR dogfood CLI artifact verificationObjective: Install the dogfood CLI for PR #18851 in a Linux container workspace and verify it matches the PR head commit. Steps:
Evidence:
Observations:
Scenario 2: Focused Linux source regression testsObjective: Verify the PR's Linux certificate handling changes with the focused test coverage added by the PR. Steps:
Evidence:
Observations:
Scenario 3: Generated AppHost smoke with executable SSL_CERT_DIR probeObjective: Verify the PR dogfood CLI can create and run a fresh generated AppHost, and that a local executable resource receives an appended Linux Steps:
Evidence:
Observations:
Scenario 4:
|
| 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.
JamesNK
left a comment
There was a problem hiding this comment.
Reviewed with 1 correctness issue: the recommended corrupt-cache repair does not succeed when openssl is unavailable.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 677e0f4a-653a-4d97-b690-f86d67107226
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
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 -hashprobe 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);
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 677e0f4a-653a-4d97-b690-f86d67107226
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 677e0f4a-653a-4d97-b690-f86d67107226
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
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 x509probe is treated as proof that the subject-hash entry is missing. For example, the new start-failure test creates a valid12345678.0entry, 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 doctorpasses only its user-cancellation token, so anopensslexecutable that hangs will block this check—and therefore all later doctor checks—indefinitely. Add a short linked timeout, asDeprecatedWorkloadCheckdoes, and treat timeout as an unavailable probe while ensuring the child process is terminated.
var exitCode = await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false);
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
|
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
There was a problem hiding this comment.
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 x509probe 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), soaspire doctorwill 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);
This comment has been minimized.
This comment has been minimized.
PR Testing ReportPR Information
Artifact Version Verification
Changes AnalyzedFiles Changed
Change Categories
Test Scenarios ExecutedScenario 1: Current-head Linux source regression testsObjective: Verify the changed Linux certificate trust behavior and new doctor diagnostics from source at the current PR head. Steps:
Evidence:
Observations:
Unhappy-path coverage included:
Scenario 2: Dogfood CLI empty AppHost smokeObjective: Verify the PR dogfood CLI can create and run a generated Aspire app in the Linux container. Steps:
Evidence:
Observations:
Summary
Overall Result✅ PR source verified at current head. The Linux regression coverage passed in the repo container at |
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 677e0f4a-653a-4d97-b690-f86d67107226
There was a problem hiding this comment.
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
opensslprobe is treated as proof that the subject-hash entry is missing. If the executable exists but cannot start or exits unsuccessfully,HasMatchingHashEntryis 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
opensslcan blockaspire doctorindefinitely. 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);
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)
Selected jobs (5)
How these were chosen — grouped by what changed
🔧 show 43
🧪 📦 affected project 📦 affected project 🧪 🧪 🧪 Job reasons
Selection computed for commit |
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
|
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 ( Triggered signals: Intended documentation change: Update
See the workflow run for details: https://github.com/microsoft/aspire/actions/runs/30055153085 |
Description
Linux AppHosts launched with
dotnet runor an IDE can bypass theaspire runsetup that materializesSSL_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_DIRwhen it is set, or by materializing well-known system certificate directories that actually exist when it is unset. ExplicitSSL_CERT_DIRvalues, including an empty value, are preserved as user configuration.It also expands
aspire doctorto detect stale, missing, or corrupt OpenSSL development certificate cache entries under the dev-certs trust directory and recommendaspire certs cleanfollowed byaspire 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:
Validation:
Fixes #18066
Checklist
<remarks />and<code />elements on your triple slash comments?