From cf04f7b2bb0b4cb0d2d9de47a22e1f1fc9eb06c0 Mon Sep 17 00:00:00 2001 From: Freddy Kristiansen Date: Mon, 28 Oct 2024 14:10:35 +0100 Subject: [PATCH] Enable trusted Signing (#1280) ## Support for Azure Trusted Signing Read https://learn.microsoft.com/en-us/azure/trusted-signing/ for more information about Trusted Signing and how to set it up. After setting up your trusted signing account and certificate profile, you need to create a setting called [trustedSigning](https://aka.ms/algosettings#trustedSigning) for AL-Go to sign your apps using Azure Trusted Signing. New setting example: "trustedSigning": { "Endpoint": "https://neu.codesigning.azure.net", "Account": "BCCodeSigningTest", "CertificateProfile": "BCCodeSigningPublic" } These values are defined from your trusted signing account in Azure. Note that your Azure_Credentials secret (Entra ID App or Managed identity) still needs to provide access to your azure subscription and be assigned the `Trusted Signing Certificate Profile Signer` role in the Trusted Signing Account. --------- Co-authored-by: freddydk --- Actions/AL-Go-Helper.ps1 | 5 ++ Actions/Sign/Sign.ps1 | 43 ++++++----- Actions/Sign/Sign.psm1 | 72 ++++++++++++------- RELEASENOTES.md | 20 ++++-- Scenarios/settings.md | 1 + .../.github/workflows/_BuildALGoProject.yaml | 4 +- .../.github/workflows/_BuildALGoProject.yaml | 4 +- 7 files changed, 98 insertions(+), 51 deletions(-) diff --git a/Actions/AL-Go-Helper.ps1 b/Actions/AL-Go-Helper.ps1 index fee6f3258..81bbd41c2 100644 --- a/Actions/AL-Go-Helper.ps1 +++ b/Actions/AL-Go-Helper.ps1 @@ -661,6 +661,11 @@ function ReadSettings { "defaultReleaseMD" = "## Release reference documentation\n\nThis is the generated reference documentation for [{REPOSITORY}](https://github.com/{REPOSITORY}).\n\nYou can use the navigation bar at the top and the table of contents to the left to navigate your documentation.\n\nYou can change this content by creating/editing the **{INDEXTEMPLATERELATIVEPATH}** file in your repository or use the alDoc:defaultReleaseMD setting in your repository settings file (.github/AL-Go-Settings.json)\n\n{RELEASENOTES}" } "trustMicrosoftNuGetFeeds" = $true + "trustedSigning" = [ordered]@{ + "Endpoint" = "" + "Account" = "" + "CertificateProfile" = "" + } } # Read settings from files and merge them into the settings object diff --git a/Actions/Sign/Sign.ps1 b/Actions/Sign/Sign.ps1 index 4e3a0f3b7..6e123f6a8 100644 --- a/Actions/Sign/Sign.ps1 +++ b/Actions/Sign/Sign.ps1 @@ -28,23 +28,35 @@ Write-Host "::endgroup::" # Get parameters for signing $AzureCredentials = ConvertFrom-Json ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($AzureCredentialsJson))) $settings = $env:Settings | ConvertFrom-Json -if ($settings.keyVaultName) { - $AzureKeyVaultName = $settings.keyVaultName -} -elseif ($AzureCredentials.PSobject.Properties.name -eq "keyVaultName") { - $AzureKeyVaultName = $AzureCredentials.keyVaultName + +if ($settings.TrustedSigning.Endpoint -and $settings.TrustedSigning.Account -and $settings.TrustedSigning.CertificateProfile) { + $SigningParams = @{ + "SigningEndpoint" = $settings.TrustedSigning.Endpoint + "SigningAccount" = $settings.TrustedSigning.Account + "SigningCertificateProfile" = $settings.TrustedSigning.CertificateProfile + } } else { - throw "KeyVaultName is not specified in AzureCredentials nor in settings. Please specify it in one of them." -} + if ($settings.keyVaultName) { + $AzureKeyVaultName = $settings.keyVaultName + } + elseif ($AzureCredentials.PSobject.Properties.name -eq "keyVaultName") { + $AzureKeyVaultName = $AzureCredentials.keyVaultName + } + else { + throw "KeyVaultName is not specified in AzureCredentials nor in settings. Please specify it in one of them." + } -$AzureCredentialParams = @{ - "ClientId" = $AzureCredentials.clientId - "TenantId" = $AzureCredentials.tenantId -} -if ($AzureCredentials.PSobject.Properties.name -eq "clientSecret") { - $AzureCredentialParams += @{ - "ClientSecret" = $AzureCredentials.clientSecret + $SigningParams = @{ + "ClientId" = $AzureCredentials.clientId + "TenantId" = $AzureCredentials.tenantId + "KeyVaultName" = $AzureKeyVaultName + "CertificateName" = $settings.keyVaultCodesignCertificateName + } + if ($AzureCredentials.PSobject.Properties.name -eq "clientSecret") { + $SigningParams += @{ + "ClientSecret" = $AzureCredentials.clientSecret + } } } InstallAzModuleIfNeeded -name 'Az.Accounts' @@ -54,8 +66,7 @@ $description = "Signed with AL-Go for GitHub" $descriptionUrl = "$ENV:GITHUB_SERVER_URL/$ENV:GITHUB_REPOSITORY" Write-Host "::group::Signing files" -Invoke-SigningTool @AzureCredentialParams -KeyVaultName $AzureKeyVaultName ` - -CertificateName $settings.keyVaultCodesignCertificateName ` +Invoke-SigningTool @SigningParams ` -FilesToSign $PathToFiles ` -Description $description ` -DescriptionUrl $descriptionUrl ` diff --git a/Actions/Sign/Sign.psm1 b/Actions/Sign/Sign.psm1 index 3d8d9fce9..439b2c711 100644 --- a/Actions/Sign/Sign.psm1 +++ b/Actions/Sign/Sign.psm1 @@ -78,16 +78,22 @@ function Install-SigningTool() { #> function Invoke-SigningTool() { param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory = $true, ParameterSetName="KeyVaultSigning")] [string] $KeyVaultName, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory = $true, ParameterSetName="KeyVaultSigning")] [string] $CertificateName, - [Parameter(Mandatory = $false)] + [Parameter(Mandatory = $false, ParameterSetName="KeyVaultSigning")] [string] $ClientId, - [Parameter(Mandatory = $false)] + [Parameter(Mandatory = $false, ParameterSetName="KeyVaultSigning")] [string] $ClientSecret, - [Parameter(Mandatory = $false)] + [Parameter(Mandatory = $false, ParameterSetName="KeyVaultSigning")] [string] $TenantId, + [Parameter(Mandatory = $true, ParameterSetName="TrustedSigning")] + [string] $SigningEndpoint, + [Parameter(Mandatory = $true, ParameterSetName="TrustedSigning")] + [string] $SigningAccount, + [Parameter(Mandatory = $true, ParameterSetName="TrustedSigning")] + [string] $SigningCertificateProfile, [Parameter(Mandatory = $true)] [string] $FilesToSign, [Parameter(Mandatory = $true)] @@ -105,14 +111,12 @@ function Invoke-SigningTool() { $signingToolExe = Install-SigningTool # Sign files - if ($ClientId -and $ClientSecret -and $TenantId) { - Write-Host "Invoking signing tool using clientId/clientSecret" - . $signingToolExe code azure-key-vault ` - --azure-key-vault-url "https://$KeyVaultName.vault.azure.net/" ` - --azure-key-vault-certificate $CertificateName ` - --azure-key-vault-client-id $ClientId ` - --azure-key-vault-client-secret $ClientSecret ` - --azure-key-vault-tenant-id $TenantId ` + if ($PsCmdlet.ParameterSetName -eq "TrustedSigning") { + Write-Host "Invoking signing tool using trusted signing" + . $signingToolExe code trusted-signing ` + --trusted-signing-endpoint $SigningEndpoint ` + --trusted-signing-account $SigningAccount ` + --trusted-signing-certificate-profile $SigningCertificateProfile ` --description $Description ` --description-url $DescriptionUrl ` --file-digest $DigestAlgorithm ` @@ -122,18 +126,36 @@ function Invoke-SigningTool() { $FilesToSign } else { - Write-Host "Invoking signing tool using managed identity" - . $signingToolExe code azure-key-vault ` - --azure-key-vault-url "https://$KeyVaultName.vault.azure.net/" ` - --azure-key-vault-certificate $CertificateName ` - --azure-key-vault-managed-identity $true ` - --description $Description ` - --description-url $DescriptionUrl ` - --file-digest $DigestAlgorithm ` - --timestamp-digest $DigestAlgorithm ` - --timestamp-url $TimestampService ` - --verbosity $Verbosity ` - $FilesToSign + if ($ClientId -and $ClientSecret -and $TenantId) { + Write-Host "Invoking signing tool using clientId/clientSecret" + . $signingToolExe code azure-key-vault ` + --azure-key-vault-url "https://$KeyVaultName.vault.azure.net/" ` + --azure-key-vault-certificate $CertificateName ` + --azure-key-vault-client-id $ClientId ` + --azure-key-vault-client-secret $ClientSecret ` + --azure-key-vault-tenant-id $TenantId ` + --description $Description ` + --description-url $DescriptionUrl ` + --file-digest $DigestAlgorithm ` + --timestamp-digest $DigestAlgorithm ` + --timestamp-url $TimestampService ` + --verbosity $Verbosity ` + $FilesToSign + } + else { + Write-Host "Invoking signing tool using managed identity" + . $signingToolExe code azure-key-vault ` + --azure-key-vault-url "https://$KeyVaultName.vault.azure.net/" ` + --azure-key-vault-certificate $CertificateName ` + --azure-key-vault-managed-identity $true ` + --description $Description ` + --description-url $DescriptionUrl ` + --file-digest $DigestAlgorithm ` + --timestamp-digest $DigestAlgorithm ` + --timestamp-url $TimestampService ` + --verbosity $Verbosity ` + $FilesToSign + } } } diff --git a/RELEASENOTES.md b/RELEASENOTES.md index aee778dbc..f54abcdff 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,6 +2,20 @@ - Issue 1241 Increment Version Number might produce wrong app.json +### New Project Settings + +- `pageScriptingTests` should be an array of page scripting test file specifications, relative to the AL-Go project. Examples of file specifications: `recordings/my*.yml` (for all yaml files in the recordings subfolder matching my\*.yml), `recordings` (for all \*.yml files in the recordings subfolder) or `recordings/test.yml` (for a single yml file) +- `doNotRunPageScriptingTests` can force the pipeline to NOT run the page scripting tests specified in pageScriptingTests. Note this setting can be set in a [workflow specific settings file](#where-are-the-settings-located) to only apply to that workflow +- `restoreDatabases` should be an array of events, indicating when you want to start with clean databases in the container. Possible events are: `BeforeBcpTests`, `BeforePageScriptingTests`, `BeforeEachTestApp`, `BeforeEachBcptTestApp`, `BeforeEachPageScriptingTest` + +### New Repository Settings + +- `trustedSigning` is a structure defining `Account`, `EndPoint` and `CertificateProfile` if you want to use trusted signing. Note that your Azure_Credentials secret (Microsoft Entra ID App or Managed identity) still needs to provide access to your azure subscription and be assigned the `Trusted Signing Certificate Profile Signer` role in the Trusted Signing Account. + +### Support for Azure Trusted Signing + +Read https://learn.microsoft.com/en-us/azure/trusted-signing/ for more information about Trusted Signing and how to set it up. After setting up your trusted signing account and certificate profile, you need to create a setting called [trustedSigning](https://aka.ms/algosettings#trustedSigning) for AL-Go to sign your apps using Azure Trusted Signing. + ### Support for Page Scripting Tests Page Scripting tests are now supported as part of CI/CD. By specifying pageScriptingTests in your project settings file, AL-Go for GitHub will automatically run these page scripting tests as part of your CI/CD workflow, generating the following build artifacts: @@ -9,12 +23,6 @@ Page Scripting tests are now supported as part of CI/CD. By specifying pageScrip - `PageScriptingTestResults` is a JUnit test results file with all results combined. - `PageScriptingTestResultDetails` are the detailed test results (including videos) when any of the page scripting tests have failures. If the page scripting tests succeed - the details are not published. -### New Project Settings - -- `pageScriptingTests` should be an array of page scripting test file specifications, relative to the AL-Go project. Examples of file specifications: `recordings/my*.yml` (for all yaml files in the recordings subfolder matching my\*.yml), `recordings` (for all \*.yml files in the recordings subfolder) or `recordings/test.yml` (for a single yml file) -- `doNotRunPageScriptingTests` can force the pipeline to NOT run the page scripting tests specified in pageScriptingTests. Note this setting can be set in a [workflow specific settings file](#where-are-the-settings-located) to only apply to that workflow -- `restoreDatabases` should be an array of events, indicating when you want to start with clean databases in the container. Possible events are: `BeforeBcpTests`, `BeforePageScriptingTests`, `BeforeEachTestApp`, `BeforeEachBcptTestApp`, `BeforeEachPageScriptingTest` - ## v6.0 ### Issues diff --git a/Scenarios/settings.md b/Scenarios/settings.md index 677f2b9f2..7b7fae0bf 100644 --- a/Scenarios/settings.md +++ b/Scenarios/settings.md @@ -121,6 +121,7 @@ The repository settings are only read from the repository settings file (.github | excludeEnvironments | excludeEnvironments can be an array of GitHub Environments, which should be excluded from the list of environments considered for deployment. github-pages is automatically added to this array and cannot be used as environment for deployment of AL-Go for GitHub projects. | \[ \] | | trustMicrosoftNuGetFeeds | Unless this setting is set to false, AL-Go for GitHub will trust the NuGet feeds provided by Microsoft. The feeds provided by Microsoft contains all Microsoft apps, all Microsoft symbols and symbols for all AppSource apps. | true | | trustedNuGetFeeds | trustedNuGetFeeds can be an array of NuGet feed specifications, which AL-Go for GitHub will use for dependency resolution. Every feed specification must include a URL property and can optionally include a few other properties:
**url** = The URL of the feed (examples: https://pkgs.dev.azure.com/myorg/apps/\_packaging/myrepo/nuget/v3/index.json or https://nuget.pkg.github.com/mygithuborg/index.json").
**authTokenSecret** = If the NuGet feed specified by URL is private, the authTokenSecret must be the name of a secret containing the authentication token with permissions to search and read packages from the NuGet feed.
**patterns** = AL-Go for GitHub will only trust packages, where the ID matches this pattern. Default is all packages (\*).
**fingerprints** = If specified, AL-Go for GitHub will only trust packages signed with a certificate with a fingerprint matching one of the fingerprints in this array. | \[ \] | +| trustedSigning | Structure defining the properties needed for enabling trusted Signing. Please read [this](https://learn.microsoft.com/en-us/azure/trusted-signing/) to setup your Azure Trusted Signing Account and Certificate Profile and then provide these properties in this setting:
**Account** must be the name of your trusted signing account.
**Endpoint** must point to the endpoint of your trusted signing account (ex. https://weu.codesigning.azure.net).
**CertificateProfile** must be the CertificateProfile in your trusted signing account you want to use for signing.
Please note that your Azure_Credentials secret (Microsoft Entra ID App or Managed identity) needs to provide access to your azure subscription and be assigned the `Trusted Signing Certificate Profile Signer` role in the Trusted Signing Account. | ## AppSource specific advanced settings diff --git a/Templates/AppSource App/.github/workflows/_BuildALGoProject.yaml b/Templates/AppSource App/.github/workflows/_BuildALGoProject.yaml index fb7cc20ec..c66027d23 100644 --- a/Templates/AppSource App/.github/workflows/_BuildALGoProject.yaml +++ b/Templates/AppSource App/.github/workflows/_BuildALGoProject.yaml @@ -97,7 +97,7 @@ jobs: with: shell: ${{ inputs.shell }} project: ${{ inputs.project }} - get: useCompilerFolder,keyVaultCodesignCertificateName,doNotSignApps,doNotRunTests,artifact,generateDependencyArtifact + get: useCompilerFolder,keyVaultCodesignCertificateName,doNotSignApps,doNotRunTests,artifact,generateDependencyArtifact,trustedSigning - name: Read secrets id: ReadSecrets @@ -148,7 +148,7 @@ jobs: installTestAppsJson: ${{ steps.DownloadProjectDependencies.outputs.DownloadedTestApps }} - name: Sign - if: inputs.signArtifacts && env.doNotSignApps == 'False' && env.keyVaultCodesignCertificateName != '' + if: inputs.signArtifacts && env.doNotSignApps == 'False' && (env.keyVaultCodesignCertificateName != '' || (fromJson(env.trustedSigning).Endpoint != '' && fromJson(env.trustedSigning).Account != '' && fromJson(env.trustedSigning).CertificateProfile != '')) id: sign uses: microsoft/AL-Go-Actions/Sign@main with: diff --git a/Templates/Per Tenant Extension/.github/workflows/_BuildALGoProject.yaml b/Templates/Per Tenant Extension/.github/workflows/_BuildALGoProject.yaml index fb7cc20ec..c66027d23 100644 --- a/Templates/Per Tenant Extension/.github/workflows/_BuildALGoProject.yaml +++ b/Templates/Per Tenant Extension/.github/workflows/_BuildALGoProject.yaml @@ -97,7 +97,7 @@ jobs: with: shell: ${{ inputs.shell }} project: ${{ inputs.project }} - get: useCompilerFolder,keyVaultCodesignCertificateName,doNotSignApps,doNotRunTests,artifact,generateDependencyArtifact + get: useCompilerFolder,keyVaultCodesignCertificateName,doNotSignApps,doNotRunTests,artifact,generateDependencyArtifact,trustedSigning - name: Read secrets id: ReadSecrets @@ -148,7 +148,7 @@ jobs: installTestAppsJson: ${{ steps.DownloadProjectDependencies.outputs.DownloadedTestApps }} - name: Sign - if: inputs.signArtifacts && env.doNotSignApps == 'False' && env.keyVaultCodesignCertificateName != '' + if: inputs.signArtifacts && env.doNotSignApps == 'False' && (env.keyVaultCodesignCertificateName != '' || (fromJson(env.trustedSigning).Endpoint != '' && fromJson(env.trustedSigning).Account != '' && fromJson(env.trustedSigning).CertificateProfile != '')) id: sign uses: microsoft/AL-Go-Actions/Sign@main with: