From 635cfe56a84873485f86710ac7d7e5f8e32038a7 Mon Sep 17 00:00:00 2001 From: mr-shekhar <85621953+mr-shekhar@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:28:07 +0530 Subject: [PATCH] NXDRIVE-2941: Update the release process to sign windows exe to limit signature usage (#5010) NXDRIVE-2941: Update the release process to sign windows exe to limit signature usage --------- Co-authored-by: Sushil Chaudhary Co-authored-by: sushildeep --- .github/workflows/release.yml | 75 +++++++++++++-- docs/changes/5.5.0.md | 1 + tools/windows/deploy_ci_agent.ps1 | 149 +++++++++++++++++++++--------- 3 files changed, 172 insertions(+), 53 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 184277234e..dbd23b748a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,6 +14,10 @@ on: description: 'Set to "release" for a beta release.' required: false default: "alpha" + signExe: + description: 'Set to "true" to generate sign .exe on Windows.' + required: false + default: "false" env: GITHUB_USERNAME: "nuxeodrive" @@ -125,6 +129,7 @@ jobs: NOTARIZATION_PASSWORD: ${{ secrets.NOTARIZATION_PASSWORD }} NOTARIZATION_TEAMID: ${{ secrets.NOTARIZATION_TEAMID }} SIGNING_ID: "NUXEO CORP" + SIGNING_ID_NEW: "Hyland Software, Inc." SYSTEM_VERSION_COMPAT: 0 run: bash tools/osx/deploy_ci_agent.sh --check-upgrade @@ -148,13 +153,6 @@ jobs: # # Windows # - - - name: "[Windows] Setup certificate" - if: matrix.os == 'windows-latest' - run: | - echo "${{ secrets.CERT_APP_WINDOWS }}" > certificate.b64 - certutil -decode certificate.b64 certificate.pfx - - name: "[Windows] Unlock PowerShell" if: matrix.os == 'windows-latest' run: powershell Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope LocalMachine @@ -163,11 +161,60 @@ jobs: if: matrix.os == 'windows-latest' run: powershell ".\\tools\\windows\\deploy_ci_agent.ps1" -install_release + - name: Setup Certificate + if: matrix.os == 'windows-latest' + run: | + echo "${{ secrets.SM_CLIENT_CERT_FILE_B64 }}" | base64 --decode > /d/Certificate_pkcs12.p12 + cat /d/Certificate_pkcs12.p12 + shell: bash + + - name: Set variables + if: matrix.os == 'windows-latest' + id: variables + run: | + dir + echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT + echo "KEYPAIR_NAME=gt-standard-keypair" >> $GITHUB_OUTPUT + echo "CERTIFICATE_NAME=gt-certificate" >> $GITHUB_OUTPUT + echo "SM_HOST=${{ secrets.SM_HOST }}" >> "$GITHUB_ENV" + echo "SM_API_KEY=${{ secrets.SM_API_KEY }}" >> "$GITHUB_ENV" + echo "SM_CLIENT_CERT_FILE=D:\\Certificate_pkcs12.p12" >> "$GITHUB_ENV" + echo "SM_CLIENT_CERT_PASSWORD=${{ secrets.SM_CLIENT_CERT_PASSWORD }}" >> "$GITHUB_ENV" + echo "SM_KEYPAIR_ALIAS=${{ secrets.SM_KEYPAIR_ALIAS }}" >> "$GITHUB_ENV" + echo "SM_CODE_SIGNING_CERT_SHA1_HASH=${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH }}" >> "$GITHUB_ENV" + echo "C:\Program Files (x86)\Windows Kits\10\App Certification Kit" >> $GITHUB_PATH + echo "C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools" >> $GITHUB_PATH + echo "C:\Program Files\DigiCert\DigiCert Keylocker Tools" >> $GITHUB_PATH + shell: bash + + - name: Setup Keylocker KSP on windows + if: matrix.os == 'windows-latest' + run: | + curl -X GET https://one.digicert.com/signingmanager/api-ui/v1/releases/Keylockertools-windows-x64.msi/download -H "x-api-key:%SM_API_KEY%" -o Keylockertools-windows-x64.msi + msiexec /i Keylockertools-windows-x64.msi /quiet /qn + smksp_registrar.exe list + smctl.exe keypair ls + C:\Windows\System32\certutil.exe -csp "DigiCert Signing Manager KSP" -key -user + shell: cmd + + - name: Certificates Sync + if: matrix.os == 'windows-latest' + run: | + smctl windows certsync --keypair-alias=${{ secrets.SM_KEYPAIR_ALIAS }} + shell: cmd + + - name: Health status + if: matrix.os == 'windows-latest' + run: | + smctl healthcheck + shell: cmd + + - name: "[Windows] Generate the .exe and validate against 2021" timeout-minutes: 15 if: matrix.os == 'windows-latest' env: - KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} + KEYCHAIN_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }} NXDRIVE_TEST_NUXEO_URL: "https://drive-2021.beta.nuxeocloud.com/nuxeo" SIGNING_ID: "Nuxeo" SIGNTOOL_PATH: 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.20348.0\x86' @@ -177,14 +224,22 @@ jobs: timeout-minutes: 15 if: matrix.os == 'windows-latest' env: - KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} NXDRIVE_TEST_NUXEO_URL: "https://drive-2023.beta.nuxeocloud.com/nuxeo" NXDRIVE_TEST_USERNAME: ${{ secrets.NXDRIVE_2023_TEST_USERNAME }} NXDRIVE_TEST_PASSWORD: ${{ secrets.NXDRIVE_2023_TEST_PASSWORD }} SIGNING_ID: "Nuxeo" SIGNTOOL_PATH: 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.20348.0\x86' run: powershell ".\\tools\\windows\\deploy_ci_agent.ps1" -check_upgrade - + + - name: "[Windows] Generate and sign the .exe" + timeout-minutes: 15 + if: matrix.os == 'windows-latest' && github.event.inputs.signExe == 'true' + env: + KEYCHAIN_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }} + SIGNING_ID_NEW: "Hyland Software, Inc." + SIGNTOOL_PATH: 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.20348.0\x86' + run: powershell ".\\tools\\windows\\deploy_ci_agent.ps1" -build_installer_and_sign + - name: "Upload artifacts" uses: actions/upload-artifact@v4 with: diff --git a/docs/changes/5.5.0.md b/docs/changes/5.5.0.md index b167def8dd..ee50c586fa 100644 --- a/docs/changes/5.5.0.md +++ b/docs/changes/5.5.0.md @@ -27,6 +27,7 @@ Release date: `2024-xx-xx` - [NXDRIVE-2926] (https://jira.nuxeo.com/browse/NXDRIVE-2926): Update github Action Runner to use mac-latest - [NXDRIVE-2932] (https://jira.nuxeo.com/browse/NXDRIVE-2932): Fix Microsoft Visual Studio issue - [NXDRIVE-2938] (https://jira.nuxeo.com/browse/NXDRIVE-2938): Update token for codecov +- [NXDRIVE-2941] (https://jira.nuxeo.com/browse/NXDRIVE-2941): Update the release process to sign Windows exe to limit signature usage ## Tests diff --git a/tools/windows/deploy_ci_agent.ps1 b/tools/windows/deploy_ci_agent.ps1 index 8d17547ec0..e99c93625f 100644 --- a/tools/windows/deploy_ci_agent.ps1 +++ b/tools/windows/deploy_ci_agent.ps1 @@ -21,6 +21,7 @@ param ( [switch]$build = $false, [switch]$build_dlls = $false, [switch]$check_upgrade = $false, + [switch]$build_installer_and_sign = $false, [switch]$install = $false, [switch]$install_release = $false, [switch]$start = $false, @@ -86,7 +87,7 @@ function build_installer { build_overlays } - sign_dlls + #sign_dlls Write-Output ">>> [$app_version] Freezing the application" freeze_pyinstaller @@ -95,10 +96,10 @@ function build_installer { & $Env:STORAGE_DIR\Scripts\python.exe $global:PYTHON_OPT tools\cleanup_application_tree.py "dist\ndrive" # Remove compiled QML files - Get-ChildItem -Path "dist\ndrive" -Recurse -File -Include *.qmlc | Foreach ($_) {Remove-Item -Verbose $_.Fullname} + Get-ChildItem -Path "dist\ndrive" -Recurse -File -Include *.qmlc | Foreach ($_) { Remove-Item -Verbose $_.Fullname } add_missing_ddls - sign "dist\ndrive\ndrive.exe" + #sign "dist\ndrive\ndrive.exe" # Stop now if we only want the application to be frozen (for integration tests) if ($Env:FREEZE_ONLY) { @@ -111,14 +112,14 @@ function build_installer { if (-Not ($Env:SKIP_ADDONS)) { build "$app_version" "tools\windows\setup-addons.iss" - sign "dist\nuxeo-drive-addons.exe" + #sign "dist\nuxeo-drive-addons.exe" } build "$app_version" "tools\windows\setup.iss" - sign "dist\nuxeo-drive-$app_version.exe" + #sign "dist\nuxeo-drive-$app_version.exe" build "$app_version" "tools\windows\setup-admin.iss" - sign "dist\nuxeo-drive-$app_version-admin.exe" + #sign "dist\nuxeo-drive-$app_version-admin.exe" } function build_overlays { @@ -129,17 +130,17 @@ function build_overlays { # Remove old DLLs on GitHub-CI to prevent such errors: # Rename-Item : Cannot create a file when that file already exists. if ($Env:GITHUB_WORKSPACE) { - Get-ChildItem -Path $folder -Recurse -File -Include *.dll | Foreach ($_) {Remove-Item $_.Fullname} + Get-ChildItem -Path $folder -Recurse -File -Include *.dll | Foreach ($_) { Remove-Item $_.Fullname } } # List of DLLs to build $overlays = @( - @{Name='NuxeoDriveSynced'; Id='1'; Icon='badge_synced'}, - @{Name='NuxeoDriveSyncing'; Id='2'; Icon='badge_syncing'}, - @{Name='NuxeoDriveConflicted'; Id='3'; Icon='badge_conflicted'}, - @{Name='NuxeoDriveError'; Id='4'; Icon='badge_error'}, - @{Name='NuxeoDriveLocked'; Id='5'; Icon='badge_locked'}, - @{Name='NuxeoDriveUnsynced'; Id='6'; Icon='badge_unsynced'} + @{Name = 'NuxeoDriveSynced'; Id = '1'; Icon = 'badge_synced' }, + @{Name = 'NuxeoDriveSyncing'; Id = '2'; Icon = 'badge_syncing' }, + @{Name = 'NuxeoDriveConflicted'; Id = '3'; Icon = 'badge_conflicted' }, + @{Name = 'NuxeoDriveError'; Id = '4'; Icon = 'badge_error' }, + @{Name = 'NuxeoDriveLocked'; Id = '5'; Icon = 'badge_locked' }, + @{Name = 'NuxeoDriveUnsynced'; Id = '6'; Icon = 'badge_unsynced' } ) $x64Path = "%name%_x64.dll" @@ -186,7 +187,7 @@ function build_overlays { } # Delete everything that is not a DLL - Get-ChildItem -Path $folder\Release -Recurse -File -Exclude *.dll | Foreach ($_) {Remove-Item $_.Fullname} + Get-ChildItem -Path $folder\Release -Recurse -File -Exclude *.dll | Foreach ($_) { Remove-Item $_.Fullname } } function check_import($import) { @@ -202,7 +203,7 @@ function check_import($import) { function check_upgrade { # Ensure a new version can be released by checking the auto-update process. - & $Env:STORAGE_DIR\Scripts\python.exe $global:PYTHON_OPT tools\scripts\check_update_process.py + & $Env:STORAGE_DIR\Scripts\python.exe $global:PYTHON_OPT tools\scripts\check_update_process.py if ($lastExitCode -ne 0) { ExitWithCode $lastExitCode } @@ -217,7 +218,8 @@ function check_vars { if ($Env:GITHUB_WORKSPACE) { # Running from GitHub Actions $Env:WORKSPACE = (Get-Item $Env:GITHUB_WORKSPACE).parent.FullName - } else { + } + else { Write-Output ">>> WORKSPACE not defined. Aborting." ExitWithCode 1 } @@ -225,9 +227,11 @@ function check_vars { if (-Not ($Env:WORKSPACE_DRIVE)) { if (Test-Path "$($Env:WORKSPACE)\sources") { $Env:WORKSPACE_DRIVE = "$($Env:WORKSPACE)\sources" - } elseif (Test-Path "$($Env:WORKSPACE)\nuxeo-drive") { + } + elseif (Test-Path "$($Env:WORKSPACE)\nuxeo-drive") { $Env:WORKSPACE_DRIVE = "$($Env:WORKSPACE)\nuxeo-drive" - } else { + } + else { $Env:WORKSPACE_DRIVE = $Env:WORKSPACE } } @@ -255,14 +259,16 @@ function check_vars { if (-Not ($Env:SPECIFIC_TEST) -Or ($Env:SPECIFIC_TEST -eq "")) { $Env:SPECIFIC_TEST = "tests" - } else { + } + else { Write-Output " SPECIFIC_TEST = $Env:SPECIFIC_TEST" $Env:SPECIFIC_TEST = "tests\$Env:SPECIFIC_TEST" } if (-Not ($Env:SKIP)) { $Env:SKIP = "" - } else { + } + else { Write-Output " SKIP = $Env:SKIP" } } @@ -284,10 +290,12 @@ function download($url, $output) { if ($Env:GITHUB_WORKSPACE) { $client = New-Object System.Net.WebClient $client.DownloadFile($url, $output) - } else { + } + else { Start-BitsTransfer -Source $url -Destination $output } - } Catch {} + } + Catch {} $try += 1 Start-Sleep -s 5 } @@ -407,7 +415,7 @@ function junit_arg($path, $run) { if ($run) { $run = ".$run" } - return "--junitxml=$junit\$path$run.xml" + return "--junitxml=$junit\$path$run.xml" } function launch_test($path, $pytest_args) { @@ -506,33 +514,38 @@ function sign($file) { $Env:SIGNING_ID = "Nuxeo" Write-Output ">>> SIGNING_ID is not set, using '$Env:SIGNING_ID'" } + + if (-Not ($Env:SIGNING_ID_NEW)) { + $Env:SIGNING_ID_NEW = "Hyland Software, Inc." + Write-Output ">>> SIGNING_ID_NEW is not set, using '$Env:SIGNING_ID_NEW'" + } if (-Not ($Env:APP_NAME)) { $Env:APP_NAME = "Nuxeo Drive" Write-Output ">>> APP_NAME is not set, using '$Env:APP_NAME'" } - if ($Env:GITHUB_WORKSPACE) { - $cert = "certificate.pfx" - if (Test-Path $cert) { - Write-Output ">>> Importing the code signing certificate" - $password = ConvertTo-SecureString -String $Env:KEYCHAIN_PASSWORD -AsPlainText -Force - Import-PfxCertificate -FilePath $cert -CertStoreLocation "Cert:\LocalMachine\My" -Password $password - - # Remove the file to not import it again the next run - Remove-Item -Path $cert -Verbose - } - } + #if ($Env:GITHUB_WORKSPACE) { + # $cert = "certificate.pfx" + # if (Test-Path $cert) { + # Write-Output ">>> Importing the code signing certificate" + #$password = ConvertTo-SecureString -String $Env:KEYCHAIN_PASSWORD -AsPlainText -Force + # Import-PfxCertificate -FilePath $cert -CertStoreLocation "Cert:\LocalMachine\My" -Password $password + # Remove the file to not import it again the next run + # Remove-Item -Path $cert -Verbose + # } + #} + Write-Output ">>> $Env:SM_CODE_SIGNING_CERT_SHA1_HASH" Write-Output ">>> Signing $file" & $Env:SIGNTOOL_PATH\signtool.exe sign ` - /a ` - /sm ` - /n "$Env:SIGNING_ID" ` + /sha1 "$ENV:SM_CODE_SIGNING_CERT_SHA1_HASH" ` + /n "$Env:SIGNING_ID_NEW" ` /d "$Env:APP_NAME" ` - /fd sha256 ` + /td SHA256 /fd sha256 ` /tr http://timestamp.digicert.com/sha256/timestamp ` /v ` "$file" + if ($lastExitCode -ne 0) { ExitWithCode $lastExitCode } @@ -543,7 +556,49 @@ function sign($file) { ExitWithCode $lastExitCode } } +function build_installer_and_sign { + # Build the installer + $app_version = (Get-Content nxdrive/__init__.py) -match "__version__" -replace '"', "" -replace "__version__ = ", "" + + # Build DDLs only on GitHub-CI, no need to loose time on the local dev machine + if ($Env:GITHUB_WORKSPACE) { + build_overlays + } + + sign_dlls + + Write-Output ">>> [$app_version] Freezing the application" + freeze_pyinstaller + + # Do some clean-up + & $Env:STORAGE_DIR\Scripts\python.exe $global:PYTHON_OPT tools\cleanup_application_tree.py "dist\ndrive" + # Remove compiled QML files + Get-ChildItem -Path "dist\ndrive" -Recurse -File -Include *.qmlc | Foreach ($_) { Remove-Item -Verbose $_.Fullname } + + add_missing_ddls + sign "dist\ndrive\ndrive.exe" + + # Stop now if we only want the application to be frozen (for integration tests) + if ($Env:FREEZE_ONLY) { + return 0 + } + + if ($Env:ZIP_NEEDED) { + zip_files "dist\nuxeo-drive-windows-$app_version.zip" "dist\ndrive" + } + + if (-Not ($Env:SKIP_ADDONS)) { + build "$app_version" "tools\windows\setup-addons.iss" + sign "dist\nuxeo-drive-addons.exe" + } + + build "$app_version" "tools\windows\setup.iss" + sign "dist\nuxeo-drive-$app_version.exe" + + build "$app_version" "tools\windows\setup-admin.iss" + sign "dist\nuxeo-drive-$app_version-admin.exe" +} function sign_dlls { $folder = "$Env:WORKSPACE_DRIVE\tools\windows\dll" Get-ChildItem $folder -Recurse -Include *.dll | Foreach-Object { @@ -579,19 +634,27 @@ function main { if ($build) { build_installer - } elseif ($build_dlls) { + } + elseif ($build_dlls) { build_overlays - } elseif ($check_upgrade) { + } + elseif ($build_installer_and_sign) { + build_installer_and_sign + } + elseif ($check_upgrade) { check_upgrade - } elseif ($install -or $install_release) { + } + elseif ($install -or $install_release) { install_deps if ((check_import "import PyQt5") -ne 1) { Write-Output ">>> No PyQt5. Installation failed." ExitWithCode 1 } - } elseif ($start) { + } + elseif ($start) { start_nxdrive - } elseif ($tests) { + } + elseif ($tests) { launch_tests } }