From 0e3a824fd3c2ccb9471f0c48383e1d7d2e1ed8ef Mon Sep 17 00:00:00 2001 From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com> Date: Wed, 22 Apr 2026 12:32:40 -0700 Subject: [PATCH 1/2] Add macOS binary code signing and package notarization We still need to apply the template signing so that Guardian tasks pass and so that script files are signed. After doing what's essentially Windows signing, we sign and harden the binaries for macOS. Then we do the same for the PKG installer, and finally notarize it. The ESRP signing service requires a zip of files for Apple signing at all stages. Now that we can use it via the OneBranch signing task we no longer need the service connection or variable group that was trying to set it up. Notarization requires the BundleId from Get-MacOSPackageIdentifierInfo. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .pipelines/templates/mac-package-build.yml | 65 ++++++++++++++++++---- .pipelines/templates/mac.yml | 32 +++++++++++ tools/packaging/packaging.psd1 | 1 + 3 files changed, 86 insertions(+), 12 deletions(-) diff --git a/.pipelines/templates/mac-package-build.yml b/.pipelines/templates/mac-package-build.yml index 6585773c743..154cb6c0257 100644 --- a/.pipelines/templates/mac-package-build.yml +++ b/.pipelines/templates/mac-package-build.yml @@ -76,6 +76,14 @@ jobs: # Diagnostics is not critical it passes every time it runs continueOnError: true + - pwsh: | + $signedDir = "$(Pipeline.Workspace)/CoOrdinatedBuildPipeline/drop_macos_sign_${{ parameters.buildArchitecture }}/Signed-${{ parameters.buildArchitecture }}" + Get-ChildItem $signedDir -Recurse -Include 'pwsh', '*.dylib' | ForEach-Object { + codesign --verify --deep --strict --verbose=4 $_.FullName + if ($LASTEXITCODE -ne 0) { throw "codesign verification failed for $($_.FullName)" } + } + displayName: 'Verify Apple codesign on signed binaries' + - pwsh: | # Add -SkipReleaseChecks as a mitigation to unblock release. # macos-10.15 does not allow creating a folder under root. Hence, moving the folder. @@ -158,7 +166,12 @@ jobs: Write-Host "##vso[artifact.upload containerfolder=macos-pkgs;artifactname=macos-pkgs]$file" } + $packageInfo = Get-MacOSPackageIdentifierInfo -Version '$(Version)' -LTS:$LTS + Write-Verbose -Verbose "BundleId: $($packageInfo.PackageIdentifier)" + Write-Host "##vso[task.setvariable variable=BundleId;isOutput=true]$($packageInfo.PackageIdentifier)" + displayName: 'Package ${{ parameters.buildArchitecture}}' + name: packageStep env: __DOTNET_RUNTIME_FEED_KEY: $(RUNTIME_SOURCEFEED_KEY) @@ -178,7 +191,8 @@ jobs: value: $(Build.SourcesDirectory)/PowerShell/.config/suppress.json - name: BuildArch value: ${{ parameters.buildArchitecture }} - - group: mscodehub-macos-package-signing + - name: BundleId + value: $[ dependencies.package_macOS_${{ parameters.buildArchitecture }}.outputs['packageStep.BundleId'] ] steps: - download: current @@ -216,32 +230,59 @@ jobs: inline_operation: | [ { - "KeyCode": "$(KeyCode)", + "KeyCode": "CP-401337-Apple", "OperationCode": "MacAppDeveloperSign", "ToolName": "sign", "ToolVersion": "1.0", "Parameters": { - "Hardening": "Enable", - "OpusInfo": "http://microsoft.com" + "Hardening": "--options=runtime" } } ] + - task: onebranch.pipeline.signing@1 + displayName: 'OneBranch Notarize Package' + inputs: + command: 'sign' + files_to_sign: '**/*-osx-*.zip' + search_root: '$(Pipeline.Workspace)' + inline_operation: | + [ + { + "KeyCode": "CP-401337-Apple", + "OperationCode": "MacAppNotarize", + "ToolName": "sign", + "ToolVersion": "1.0", + "Parameters": { + "BundleId": "$(BundleId)" + } + } + ] + timeoutInMinutes: 120 + - pwsh: | $signedPkg = Get-ChildItem -Path $(Pipeline.Workspace) -Filter "*osx*.zip" -File + if (-not (Test-Path $(ob_outputDirectory))) { + $null = New-Item -Path $(ob_outputDirectory) -ItemType Directory + } + + $expandDir = "$(Pipeline.Workspace)/pkgExpand" + $null = New-Item -Path $expandDir -ItemType Directory -Force + $signedPkg | ForEach-Object { Write-Verbose -Verbose "Signed package zip: $_" + Expand-Archive -Path $_ -DestinationPath $expandDir -Verbose + } - if (-not (Test-Path $_)) { - throw "Package not found: $_" - } - - if (-not (Test-Path $(ob_outputDirectory))) { - $null = New-Item -Path $(ob_outputDirectory) -ItemType Directory - } + # ESRP's signing pipeline nests the PKG inside a '.zip.unzipped' subfolder + $pkgFile = Get-ChildItem -Path $expandDir -Filter '*.pkg' -Recurse -File + if (-not $pkgFile) { + throw "Package not found in: $signedPkg" + } - Expand-Archive -Path $_ -DestinationPath $(ob_outputDirectory) -Verbose + $pkgFile | ForEach-Object { + Move-Item -Path $_ -Destination $(ob_outputDirectory) -Verbose } Write-Verbose -Verbose "Expanded pkg file:" diff --git a/.pipelines/templates/mac.yml b/.pipelines/templates/mac.yml index 1699207c657..38b83423057 100644 --- a/.pipelines/templates/mac.yml +++ b/.pipelines/templates/mac.yml @@ -144,4 +144,36 @@ jobs: binPath: $(DropRootPath) OfficialBuild: $(ps_official_build) + # Apple-sign the Mach-O binaries inside the signed output. + - pwsh: | + $signedDir = "$(ob_outputDirectory)/Signed-$(Runtime)" + $zipFile = "$(Pipeline.Workspace)/macho-$(BuildArchitecture).zip" + Compress-Archive -Path "$signedDir/*" -DestinationPath $zipFile -Force + displayName: Compress signed folder for Apple signing + + - task: onebranch.pipeline.signing@1 + displayName: Apple CodeSign Mach-O binaries + inputs: + command: 'sign' + files_to_sign: 'macho-$(BuildArchitecture).zip' + search_root: '$(Pipeline.Workspace)' + inline_operation: | + [ + { + "KeyCode": "CP-401337-Apple", + "OperationCode": "MacAppDeveloperSign", + "ToolName": "sign", + "ToolVersion": "1.0", + "Parameters": { + "Hardening": "--options=runtime" + } + } + ] + + - pwsh: | + $signedDir = "$(ob_outputDirectory)/Signed-$(Runtime)" + $zipFile = "$(Pipeline.Workspace)/macho-$(BuildArchitecture).zip" + Expand-Archive -Path $zipFile -DestinationPath $signedDir -Force -Verbose + displayName: Expand Apple-signed Mach-O binaries into signed output + - template: /.pipelines/templates/step/finalize.yml@self diff --git a/tools/packaging/packaging.psd1 b/tools/packaging/packaging.psd1 index 0053428a481..e5b7fb84dfc 100644 --- a/tools/packaging/packaging.psd1 +++ b/tools/packaging/packaging.psd1 @@ -26,6 +26,7 @@ 'Test-PackageManifest' 'Update-PSSignedBuildFolder' 'Test-Bom' + 'Get-MacOSPackageIdentifierInfo' ) RootModule = "packaging.psm1" RequiredModules = @("build") From 8827d8e56d54b8d701949eb326d69fa629d870c6 Mon Sep 17 00:00:00 2001 From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com> Date: Thu, 23 Apr 2026 13:11:13 -0700 Subject: [PATCH 2/2] Apply macOS entitlements to pwsh Uses codesign in the macOS build step to apply entitlements from a plist. This is required for the hardened runtime (which is required for notarization). See: https://learn.microsoft.com/en-us/dotnet/core/install/macos-notarization-issues#default-entitlements Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .pipelines/templates/mac.yml | 8 ++++++++ assets/macos-entitlements.plist | 14 ++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 assets/macos-entitlements.plist diff --git a/.pipelines/templates/mac.yml b/.pipelines/templates/mac.yml index 38b83423057..b22149d5e46 100644 --- a/.pipelines/templates/mac.yml +++ b/.pipelines/templates/mac.yml @@ -69,6 +69,14 @@ jobs: $psOptPath = "$(OB_OUTPUTDIRECTORY)/psoptions.json" Save-PSOptions -PSOptionsPath $psOptPath + $entitlements = "$(PowerShellRoot)/assets/macos-entitlements.plist" + $pwshBin = "$(OB_OUTPUTDIRECTORY)/pwsh" + Write-Verbose -Verbose "Applying entitlements to $pwshBin" + codesign --sign - --force --options runtime --entitlements $entitlements $pwshBin + if ($LASTEXITCODE -ne 0) { + throw "codesign failed with exit code $LASTEXITCODE" + } + # Since we are using custom pool for macOS, we need to use artifact.upload to publish the artifacts Write-Host "##vso[artifact.upload containerfolder=$artifactName;artifactname=$artifactName]$(OB_OUTPUTDIRECTORY)" diff --git a/assets/macos-entitlements.plist b/assets/macos-entitlements.plist new file mode 100644 index 00000000000..9d534f4f4bf --- /dev/null +++ b/assets/macos-entitlements.plist @@ -0,0 +1,14 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.cs.disable-library-validation + + +