[release/13.2] Backport PR #16053: Fix macOS signing, permissions, cert trust, and CI verification#16215
Conversation
macOS hardened runtime blocks CoreCLR JIT (W^X memory mapping) unless the binary carries com.apple.security.cs.allow-jit and related entitlements. MicroBuild's MacDeveloperHardenWithNotarization signing preserves entitlements from a prior ad-hoc signature, so we codesign with the entitlements plist before Arcade signing. This follows the same pattern used by dotnet/sdk for Roslyn managed binaries (roslyn-entitlements.plist). Fixes #16043 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: joperezr <13854455+joperezr@users.noreply.github.com>
MicroBuild rewrites the binary file during signing, which resets Unix file permissions to the default umask (typically 644). The execute bit must be restored before CreateLayout packs the binary into the CLI archive. Without this, macOS and Linux archives contain a non-executable aspire-managed binary. Fixes #16043 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: joperezr <13854455+joperezr@users.noreply.github.com>
Backport of fbdac8a with conflict resolution: - Added EnsureHttpCertificateExists() to ICertificateToolRunner and NativeCertificateToolRunner - Added ICliHostEnvironment parameter to CertificateService constructor - Updated CliTestHelper default CertificateServiceFactory to pass ICliHostEnvironment - Added IsSuccessfulEnsureResult helper method - Added TestCliHostEnvironment and new non-interactive test cases Co-authored-by: joperezr <13854455+joperezr@users.noreply.github.com>
Add verify-cli-archive.sh (Linux/macOS) and verify-cli-archive.ps1
(Windows) scripts that validate a signed CLI archive by:
1. Extracting the archive to a temp location
2. Running 'aspire --version' to verify the binary executes
3. Running 'aspire new aspire-starter' to test bundle self-extraction
and project creation (exercises aspire-managed)
4. Cleaning up temp state (backs up and restores ~/.aspire)
These scripts will be wired into the CI pipeline to catch signing and
permissions regressions before they reach users.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: joperezr <13854455+joperezr@users.noreply.github.com>
Add post-signing verification steps to both build_sign_native.yml (macOS/Linux) and BuildAndTest.yml (Windows) that run the verification scripts after the CLI archives are built and signed. Verification runs for RIDs that can fully execute on the build agent: - macOS (Apple Silicon): osx-arm64 only - Linux (amd64): linux-x64 only - Windows: win-x64 This ensures that signing/permissions regressions (like the ones fixed in this PR) are caught during the official build before release. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: joperezr <13854455+joperezr@users.noreply.github.com>
- Restore mockable isNonInteractiveTrustSupported parameter in CertificateService for testability (was Func<bool> isWindows) - Fix cert tests to use explicit mocks instead of OS-dependent assertions - Move backup dir into VERIFY_TMPDIR to avoid orphaned temp dirs - Fix BuildAndTest.yml comment to match actual script behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: joperezr <13854455+joperezr@users.noreply.github.com>
The TestCertificateToolRunner in TestServices/ also needs to implement the EnsureHttpCertificateExists method added to ICertificateToolRunner as part of the backport. Co-authored-by: joperezr <13854455+joperezr@users.noreply.github.com>
Keep only macOS entitlements plist and build_sign_native.yml signing changes. Remove CI verification scripts, cert trust changes, and related test updates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 16215Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 16215" |
There was a problem hiding this comment.
Pull request overview
Backports fixes to the release/13.2 branch to address macOS aspire-managed runtime failures after signing (missing hardened-runtime/JIT entitlements and lost execute permissions), and adds CI-side verification of produced CLI archives.
Changes:
- Add a macOS ad-hoc codesign step for
aspire-managedwith CoreCLR JIT entitlements prior to Arcade/MicroBuild signing. - Restore the Unix execute bit on
aspire-managedafter signing so packaged archives contain an executable binary. - Add a post-build step to verify the produced CLI archive can execute and run basic commands (
aspire --version,aspire new) on supported agent/RID combinations.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| eng/pipelines/templates/build_sign_native.yml | Adds macOS ad-hoc signing with entitlements, post-sign chmod, and a signed-archive verification step. |
| eng/aspire-managed-entitlements.plist | Introduces the macOS entitlements required for CoreCLR JIT and library loading under hardened runtime. |
| - script: >- | ||
| codesign --sign - --force | ||
| --entitlements $(Build.SourcesDirectory)/eng/aspire-managed-entitlements.plist | ||
| $(Build.SourcesDirectory)/artifacts/bin/Aspire.Managed/Release/net10.0/${{ targetRid }}/publish/aspire-managed |
There was a problem hiding this comment.
The path used for the ad-hoc codesign step hard-codes both the build configuration (Release) and TFM (net10.0). This makes the pipeline brittle (it will break if _BuildConfig changes or if Aspire.Managed’s TFM changes). Consider locating aspire-managed via $(_BuildConfig) and/or a globbed TFM segment (consistent with the $(ArtifactsBinDir)Aspire.Managed/**/publish/aspire-managed pattern used in eng/Signing.props).
| $(Build.SourcesDirectory)/artifacts/bin/Aspire.Managed/Release/net10.0/${{ targetRid }}/publish/aspire-managed | |
| $(Build.SourcesDirectory)/artifacts/bin/Aspire.Managed/$(_BuildConfig)/*/${{ targetRid }}/publish/aspire-managed |
| # CreateLayout packs the binary into the bundle archive. | ||
| - ${{ if and(eq(parameters.codeSign, true), ne(parameters.agentOs, 'windows')) }}: | ||
| - script: >- | ||
| chmod +x $(Build.SourcesDirectory)/artifacts/bin/Aspire.Managed/Release/net10.0/${{ targetRid }}/publish/aspire-managed |
There was a problem hiding this comment.
This chmod step also hard-codes Release/net10.0 in the publish output path, which can get out of sync with $(_BuildConfig) used earlier and with future TFM changes. Use $(_BuildConfig) (and avoid embedding the TFM in the path, e.g., by globbing the TFM segment) so the step consistently targets the actual publish output.
| chmod +x $(Build.SourcesDirectory)/artifacts/bin/Aspire.Managed/Release/net10.0/${{ targetRid }}/publish/aspire-managed | |
| chmod +x $(Build.SourcesDirectory)/artifacts/bin/Aspire.Managed/$(_BuildConfig)/*/${{ targetRid }}/publish/aspire-managed |
| <!-- Required for CoreCLR JIT compilation under hardened runtime --> | ||
| <key>com.apple.security.cs.allow-jit</key> | ||
| <true/> | ||
| <!-- Required for loading .NET runtime libraries --> |
There was a problem hiding this comment.
The comment for com.apple.security.cs.allow-unsigned-executable-memory appears inaccurate: this entitlement is for allowing unsigned executable memory mappings (often needed alongside JIT), not specifically for loading runtime libraries. Please update the comment to reflect the actual purpose so future maintenance doesn’t rely on misleading documentation.
| <!-- Required for loading .NET runtime libraries --> | |
| <!-- Required for unsigned executable memory mappings used by JIT-generated code --> |
Restore BuildAndTest.yml wiring and verify-cli-archive scripts to validate signed archives work correctly after signing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🎬 CLI E2E Test Recordings — 53 recordings uploaded (commit View recordings
📹 Recordings uploaded automatically from CI run #24480114762 |
Description
Backport of #16053 to the
release/13.2branch.Changes
aspire-managed-entitlements.plistwith CoreCLR JIT and library loading entitlements, and ad-hoc codesign step before Arcade signing.aspire-managedafter MicroBuild signing resets Unix file permissions.verify-cli-archive.sh(Linux/macOS) andverify-cli-archive.ps1(Windows) that validate signed CLI archives by extracting, runningaspire --version, andaspire new.build_sign_native.ymlandBuildAndTest.yml.isNonInteractiveTrustSupportedparameter for testability, fix scripts.Conflict Resolution
The following conflicts were resolved during the backport:
ICertificateToolRunner/NativeCertificateToolRunner: AddedEnsureHttpCertificateExists()method (existed onmainbut notrelease/13.2). This method creates a dev cert without trusting it, used in non-interactive mode.CertificateServiceconstructor: AddedICliHostEnvironmentparameter (already registered in DI onrelease/13.2) and optionalisNonInteractiveTrustSupporteddelegate.CliTestHelper.cs: Updated defaultCertificateServiceFactoryto injectICliHostEnvironment.TestCertificateToolRunner(TestServices/): AddedEnsureHttpCertificateExistsimplementation to satisfy the updated interface.IsSuccessfulEnsureResulthelper method added toCertificateService.Fixes #16043
Checklist
<remarks />and<code />elements on your triple slash comments?aspire.devissue: