From b75cfc063b04e20bd67dc63fcb98b47c79f1f4ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= <7822554+AlexTMjugador@users.noreply.github.com> Date: Thu, 26 Jun 2025 19:43:20 +0200 Subject: [PATCH] Sign Windows Theseus binaries with DigiCert KeyLocker's cloud HSM (#3838) * feat(ci): sign Windows Theseus bins with DigiCert KeyLocker cloud HSM * perf(ci): speed up Jsign installation * fix(ci): use absolute path to DigiCert client certificate This should avoid errors related to Jsign not being able to find it we've seen on CI. * fix(ci): trim strange characters out from DigiCert credentials * ci: another attempt at fixing Jsign errors * chore: add comment mentioning why `jsign` choco deps are ignored * tweak: move KeyLocker signing config to CI release Tauri config file This prevents casual local builds from attempting to use a signing command they really can't use, improving developer experience. * tweak(ci/windows): do not waste time and signatures with MSIs We aren't distributing these anyway. This should reduce the signing operations required for building the app from 5 (one for the binary, another for the MSI installer, two for WiX extension DLLs and one for the NSIS installer) to 2. * feat(ci): make Windows code signing toggleable, do not sign non-final builds * chore(ci): tweak `sign-windows-binaries` input wording * fix(ci): deal with usual Powershell syntax shenanigans * fix(ci): work around more Powershell syntax shenanigans Who thought it'd be a good idea to make a comma a synonymous of a space for separating command line arguments? Why have to characters for the same thing? * perf(ci): do not run app build workflow on Labrinth changes Labrinth is not related to the app at all, so this is just a waste of CI minutes. * ci(theseus): enable Windows code signing by default for manual triggers These are expected to be not that common, so defaulting to what causes the least human errors when it comes to publishing a release makes most sense. --- .github/workflows/theseus-release.yml | 42 ++++++++++++++++++++++----- apps/app/tauri-release.conf.json | 20 ++++++++++++- apps/app/tauri.conf.json | 3 -- 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/.github/workflows/theseus-release.yml b/.github/workflows/theseus-release.yml index 1cbf4e884..4628ca117 100644 --- a/.github/workflows/theseus-release.yml +++ b/.github/workflows/theseus-release.yml @@ -9,14 +9,18 @@ on: - .github/workflows/theseus-release.yml - 'apps/app/**' - 'apps/app-frontend/**' - - 'apps/labrinth/src/common/**' - - 'apps/labrinth/Cargo.toml' - 'packages/app-lib/**' - 'packages/app-macros/**' - 'packages/assets/**' - 'packages/ui/**' - 'packages/utils/**' workflow_dispatch: + inputs: + sign-windows-binaries: + description: Sign Windows binaries + type: boolean + default: true + required: false jobs: build: @@ -103,11 +107,21 @@ jobs: sudo apt-get update sudo apt-get install -y libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev pkg-config libayatana-appindicator3-dev librsvg2-dev + - name: Install code signing client (Windows only) + if: startsWith(matrix.platform, 'windows') + run: choco install jsign --ignore-dependencies # GitHub runners come with a global Java installation already + - name: Install frontend dependencies run: pnpm install + - name: Disable Windows code signing for non-final release builds + if: ${{ startsWith(matrix.platform, 'windows') && !startsWith(github.ref, 'refs/tags/v') && !inputs.sign-windows-binaries }} + run: | + jq 'del(.bundle.windows.signCommand)' apps/app/tauri-release.conf.json > apps/app/tauri-release.conf.json.new + Move-Item -Path apps/app/tauri-release.conf.json.new -Destination apps/app/tauri-release.conf.json -Force + - name: build app (macos) - run: pnpm --filter=@modrinth/app run tauri build --target universal-apple-darwin --config "tauri-release.conf.json" + run: pnpm --filter=@modrinth/app run tauri build --target universal-apple-darwin --config tauri-release.conf.json if: startsWith(matrix.platform, 'macos') env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -121,15 +135,29 @@ jobs: TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} - - name: build app - run: pnpm --filter=@modrinth/app run tauri build --config "tauri-release.conf.json" - id: build_os - if: "!startsWith(matrix.platform, 'macos')" + - name: build app (Linux) + run: pnpm --filter=@modrinth/app run tauri build --config tauri-release.conf.json + if: startsWith(matrix.platform, 'ubuntu') env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + - name: build app (Windows) + run: | + [System.Convert]::FromBase64String("$env:DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_BASE64") | Set-Content -Path signer-client-cert.p12 -AsByteStream + $env:DIGICERT_ONE_SIGNER_CREDENTIALS = "$env:DIGICERT_ONE_SIGNER_API_KEY|$PWD\signer-client-cert.p12|$env:DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_PASSWORD" + pnpm --filter=@modrinth/app run tauri build --config tauri-release.conf.json --verbose --bundles 'nsis,updater' + Remove-Item -Path signer-client-cert.p12 + if: startsWith(matrix.platform, 'windows') + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + DIGICERT_ONE_SIGNER_API_KEY: ${{ secrets.DIGICERT_ONE_SIGNER_API_KEY }} + DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_BASE64: ${{ secrets.DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_BASE64 }} + DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_PASSWORD: ${{ secrets.DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_PASSWORD }} + - name: upload ${{ matrix.platform }} uses: actions/upload-artifact@v4 with: diff --git a/apps/app/tauri-release.conf.json b/apps/app/tauri-release.conf.json index 32b8a3489..47b3705a3 100644 --- a/apps/app/tauri-release.conf.json +++ b/apps/app/tauri-release.conf.json @@ -1,6 +1,24 @@ { "bundle": { - "createUpdaterArtifacts": "v1Compatible" + "createUpdaterArtifacts": "v1Compatible", + "windows": { + "signCommand": { + "cmd": "jsign", + "args": [ + "sign", + "--verbose", + "--storetype", + "DIGICERTONE", + "--keystore", + "https://clientauth.one.digicert.com", + "--storepass", + "env:DIGICERT_ONE_SIGNER_CREDENTIALS", + "--tsaurl", + "https://timestamp.sectigo.com,http://timestamp.digicert.com", + "%1" + ] + } + } }, "build": { "features": ["updater"] diff --git a/apps/app/tauri.conf.json b/apps/app/tauri.conf.json index 8223c313a..a166ad079 100644 --- a/apps/app/tauri.conf.json +++ b/apps/app/tauri.conf.json @@ -14,9 +14,6 @@ "externalBin": [], "icon": ["icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"], "windows": { - "certificateThumbprint": null, - "digestAlgorithm": "sha256", - "timestampUrl": "http://timestamp.digicert.com", "nsis": { "installMode": "perMachine", "installerHooks": "./nsis/hooks.nsi"