Sign Windows releases with MrTrust certificate
Some checks failed
Codex Template Compliance / template-compliance (push) Successful in 8s
Build / build-windows (push) Failing after 10m58s

This commit is contained in:
2026-05-16 01:15:02 +02:00
parent 312dee9f24
commit e66aa3d128
8 changed files with 82 additions and 14 deletions

View File

@@ -19,10 +19,13 @@ jobs:
MODRINTH_SOCKET_URL: wss://api.modrinth.com/ MODRINTH_SOCKET_URL: wss://api.modrinth.com/
MODRINTH_LAUNCHER_META_URL: https://launcher-meta.modrinth.com/ MODRINTH_LAUNCHER_META_URL: https://launcher-meta.modrinth.com/
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
MRTRUST_CODE_SIGNING_PFX_BASE64: ${{ secrets.MRTRUST_CODE_SIGNING_PFX_BASE64 }}
MRTRUST_PFX_PASSWORD: ${{ secrets.MRTRUST_PFX_PASSWORD }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
TAURI_SIGNING_PUBLIC_KEY: ${{ secrets.TAURI_SIGNING_PUBLIC_KEY }} TAURI_SIGNING_PUBLIC_KEY: ${{ secrets.TAURI_SIGNING_PUBLIC_KEY }}
XWIN_CACHE_DIR: .xwin-cache XWIN_CACHE_DIR: .xwin-cache
JSIGN_VERSION: "7.4"
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -66,6 +69,25 @@ jobs:
- name: Install Windows Rust target - name: Install Windows Rust target
run: rustup target add x86_64-pc-windows-msvc run: rustup target add x86_64-pc-windows-msvc
- name: Prepare MrTrust Windows code signing
shell: bash
run: |
if [ -z "${MRTRUST_CODE_SIGNING_PFX_BASE64}" ] || [ -z "${MRTRUST_PFX_PASSWORD}" ]; then
echo "::error::MRTRUST_CODE_SIGNING_PFX_BASE64 and MRTRUST_PFX_PASSWORD are required so MrTrust-installed users can trust Modrinth Plus."
exit 1
fi
mkdir -p .signing
printf '%s' "${MRTRUST_CODE_SIGNING_PFX_BASE64}" | base64 --decode > .signing/MrSphay-CodeSigning.pfx
chmod 600 .signing/MrSphay-CodeSigning.pfx
curl --fail-with-body --location \
--output .signing/jsign.jar \
"https://github.com/ebourg/jsign/releases/download/${JSIGN_VERSION}/jsign-${JSIGN_VERSION}.jar"
echo "MRTRUST_PFX_PATH=${GITHUB_WORKSPACE}/.signing/MrSphay-CodeSigning.pfx" >> "${GITHUB_ENV}"
echo "JSIGN_JAR=${GITHUB_WORKSPACE}/.signing/jsign.jar" >> "${GITHUB_ENV}"
- name: Prepare Modrinth Plus update metadata - name: Prepare Modrinth Plus update metadata
shell: bash shell: bash
run: | run: |
@@ -74,12 +96,12 @@ jobs:
if [ -n "${TAURI_SIGNING_PRIVATE_KEY}" ] && [ -n "${TAURI_SIGNING_PUBLIC_KEY}" ]; then if [ -n "${TAURI_SIGNING_PRIVATE_KEY}" ] && [ -n "${TAURI_SIGNING_PUBLIC_KEY}" ]; then
node -e "const fs=require('fs'); const path='apps/app/tauri-release.conf.json'; const config=JSON.parse(fs.readFileSync(path,'utf8')); config.plugins.updater.pubkey=process.env.TAURI_SIGNING_PUBLIC_KEY; fs.writeFileSync(path, JSON.stringify(config,null,'\\t')+'\\n');" node -e "const fs=require('fs'); const path='apps/app/tauri-release.conf.json'; const config=JSON.parse(fs.readFileSync(path,'utf8')); config.plugins.updater.pubkey=process.env.TAURI_SIGNING_PUBLIC_KEY; fs.writeFileSync(path, JSON.stringify(config,null,'\\t')+'\\n');"
else else
echo "::warning::TAURI_SIGNING_PRIVATE_KEY and TAURI_SIGNING_PUBLIC_KEY are not set. Building installer without publishing self-update metadata." echo "::error::TAURI_SIGNING_PRIVATE_KEY and TAURI_SIGNING_PUBLIC_KEY are required for release/update builds."
node -e "const fs=require('fs'); const path='apps/app/tauri-release.conf.json'; const config=JSON.parse(fs.readFileSync(path,'utf8')); config.plugins.updater.pubkey='dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDIwMzM5QkE0M0FCOERBMzkKUldRNTJyZzZwSnN6SUdPRGdZREtUUGxMblZqeG9OVHYxRUlRTzJBc2U3MUNJaDMvZDQ1UytZZmYK'; fs.writeFileSync(path, JSON.stringify(config,null,'\\t')+'\\n');" exit 1
fi fi
- name: Build Windows desktop client - name: Build Windows desktop client
run: pnpm --filter @modrinth/app exec tauri build --runner cargo-xwin --target x86_64-pc-windows-msvc run: pnpm --filter @modrinth/app exec tauri build --config tauri-release.conf.json --runner cargo-xwin --target x86_64-pc-windows-msvc --bundles "nsis,updater"
- name: Upload Windows desktop client - name: Upload Windows desktop client
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
@@ -159,3 +181,8 @@ jobs:
--upload-file "${package_dir}/latest/latest.json" \ --upload-file "${package_dir}/latest/latest.json" \
"${latest_url}/latest.json" "${latest_url}/latest.json"
fi fi
- name: Clean signing material
if: always()
shell: bash
run: rm -rf .signing

View File

@@ -8,6 +8,7 @@ All notable Modrinth Plus changes are documented here.
- Added Gitea Actions verification for the Modrinth Plus fork. - Added Gitea Actions verification for the Modrinth Plus fork.
- Added Windows installer publishing to the Gitea generic package registry. - Added Windows installer publishing to the Gitea generic package registry.
- Added signed Windows self-update metadata publishing for Modrinth Plus release builds. - Added signed Windows self-update metadata publishing for Modrinth Plus release builds.
- Added MrTrust-compatible Authenticode signing for Windows release artifacts.
- Added Codex repository context and release/security documentation. - Added Codex repository context and release/security documentation.
- Fixed startup compatibility with existing official Modrinth App databases that recorded different checksums for historical SQLite migrations. - Fixed startup compatibility with existing official Modrinth App databases that recorded different checksums for historical SQLite migrations.
- Fixed Connected Library Git repository URL resolution for repositories that use `master` instead of `main`. - Fixed Connected Library Git repository URL resolution for repositories that use `master` instead of `main`.

View File

@@ -0,0 +1,31 @@
#!/usr/bin/env bash
set -euo pipefail
artifact_path="${1:-}"
if [ -z "${artifact_path}" ]; then
echo "No artifact path was provided for signing." >&2
exit 1
fi
if [ -z "${JSIGN_JAR:-}" ] || [ ! -f "${JSIGN_JAR}" ]; then
echo "JSIGN_JAR must point to the downloaded jsign jar." >&2
exit 1
fi
if [ -z "${MRTRUST_PFX_PATH:-}" ] || [ ! -f "${MRTRUST_PFX_PATH}" ]; then
echo "MRTRUST_PFX_PATH must point to the MrTrust code-signing PFX." >&2
exit 1
fi
if [ -z "${MRTRUST_PFX_PASSWORD:-}" ]; then
echo "MRTRUST_PFX_PASSWORD must be set." >&2
exit 1
fi
java -jar "${JSIGN_JAR}" sign \
--verbose \
--storetype PKCS12 \
--keystore "${MRTRUST_PFX_PATH}" \
--storepass env:MRTRUST_PFX_PASSWORD \
--tsaurl "https://timestamp.sectigo.com,http://timestamp.digicert.com" \
"${artifact_path}"

View File

@@ -3,18 +3,9 @@
"createUpdaterArtifacts": "v1Compatible", "createUpdaterArtifacts": "v1Compatible",
"windows": { "windows": {
"signCommand": { "signCommand": {
"cmd": "jsign", "cmd": "bash",
"args": [ "args": [
"sign", "scripts/sign-windows-artifact.sh",
"--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" "%1"
] ]
} }

View File

@@ -18,6 +18,8 @@
- [ ] Security review is current. - [ ] Security review is current.
- [ ] No secrets are committed. - [ ] No secrets are committed.
- [ ] Windows artifacts are signed with the MrTrust code-signing certificate.
- [ ] Signature validates on a machine where MrTrust is installed.
- [ ] Connected Library external URLs are documented. - [ ] Connected Library external URLs are documented.
- [ ] Private repo authentication remains disabled unless explicitly designed. - [ ] Private repo authentication remains disabled unless explicitly designed.

View File

@@ -8,6 +8,7 @@ Release artifacts are not published yet.
- Connected Library can track public Git-hosted modpack manifests. - Connected Library can track public Git-hosted modpack manifests.
- Per-pack auto-update can be enabled after a pack is connected. - Per-pack auto-update can be enabled after a pack is connected.
- Windows release artifacts are signed with the MrTrust code-signing certificate.
- Gitea Actions are used as the verification runner. - Gitea Actions are used as the verification runner.
## Security ## Security
@@ -15,6 +16,7 @@ Release artifacts are not published yet.
- Dependency audit: pending runner/toolchain confirmation. - Dependency audit: pending runner/toolchain confirmation.
- Secret handling: no tokens are stored by Connected Library v1. - Secret handling: no tokens are stored by Connected Library v1.
- External network calls: public HTTPS manifest and `.mrpack` downloads. - External network calls: public HTTPS manifest and `.mrpack` downloads.
- Windows trust: MrTrust-installed users can trust Modrinth Plus only when artifacts are signed with the matching MrSphay certificate chain.
## Verification ## Verification
@@ -23,6 +25,7 @@ Release artifacts are not published yet.
| Gitea Actions build | Must pass before release | | Gitea Actions build | Must pass before release |
| Frontend lint | Covered by Gitea build workflow | | Frontend lint | Covered by Gitea build workflow |
| Rust clippy | Covered by Gitea build workflow | | Rust clippy | Covered by Gitea build workflow |
| MrTrust signing | Required by Gitea build workflow |
| Artifact download | Pending release packaging | | Artifact download | Pending release packaging |
## Notes ## Notes

View File

@@ -23,6 +23,7 @@ main
- [x] External network calls documented for Connected Library. - [x] External network calls documented for Connected Library.
- [x] No private Connected Library credentials are persisted in v1. - [x] No private Connected Library credentials are persisted in v1.
- [x] Connected Library requires HTTPS manifest and `.mrpack` URLs. - [x] Connected Library requires HTTPS manifest and `.mrpack` URLs.
- [x] MrTrust signing secrets are expected only as Gitea Actions secrets.
## Dependency Review ## Dependency Review
@@ -44,6 +45,7 @@ Pending successful Gitea Actions run.
- [x] Connected Library manifests are stored locally in SQLite. - [x] Connected Library manifests are stored locally in SQLite.
- [x] Connected Library auto-update is disabled by default. - [x] Connected Library auto-update is disabled by default.
- [x] `GITEA_TOKEN` is only for local agent API checks, not runtime app use. - [x] `GITEA_TOKEN` is only for local agent API checks, not runtime app use.
- [x] MrTrust signing does not bypass Defender, SmartScreen, UAC, or enterprise policy.
- [ ] Full Tauri runtime permission review pending. - [ ] Full Tauri runtime permission review pending.
## Release Notes ## Release Notes
@@ -52,4 +54,5 @@ Known residual risks:
```text ```text
Connected Library update behavior is conservative and does not yet implement strict removed-file sync. Connected Library update behavior is conservative and does not yet implement strict removed-file sync.
Windows trust depends on publishing artifacts signed with the same certificate chain installed by MrTrust.
``` ```

View File

@@ -4,6 +4,8 @@ Modrinth Plus uses the existing Tauri updater flow from the upstream Modrinth Ap
The updater requires signing. Tauri does not allow unsigned updater installs, so the Gitea repository must provide these Actions secrets: The updater requires signing. Tauri does not allow unsigned updater installs, so the Gitea repository must provide these Actions secrets:
- `MRTRUST_CODE_SIGNING_PFX_BASE64`: base64-encoded `MrSphay-CodeSigning.pfx` from MrTrust.
- `MRTRUST_PFX_PASSWORD`: password for the MrTrust code-signing PFX.
- `TAURI_SIGNING_PRIVATE_KEY`: private key generated by `tauri signer generate`. - `TAURI_SIGNING_PRIVATE_KEY`: private key generated by `tauri signer generate`.
- `TAURI_SIGNING_PRIVATE_KEY_PASSWORD`: optional key password. - `TAURI_SIGNING_PRIVATE_KEY_PASSWORD`: optional key password.
- `TAURI_SIGNING_PUBLIC_KEY`: public key generated next to the private key. - `TAURI_SIGNING_PUBLIC_KEY`: public key generated next to the private key.
@@ -17,8 +19,16 @@ pnpm --filter @modrinth/app exec tauri signer generate -- -w "$env:USERPROFILE\.
Use the `.key` file content as `TAURI_SIGNING_PRIVATE_KEY` and the `.key.pub` file content as `TAURI_SIGNING_PUBLIC_KEY`. Use the `.key` file content as `TAURI_SIGNING_PRIVATE_KEY` and the `.key.pub` file content as `TAURI_SIGNING_PUBLIC_KEY`.
Encode the MrTrust PFX for the `MRTRUST_CODE_SIGNING_PFX_BASE64` secret:
```powershell
[Convert]::ToBase64String([IO.File]::ReadAllBytes(".\private\MrSphay-CodeSigning.pfx"))
```
The Gitea workflow patches the public key into `apps/app/tauri-release.conf.json` at build time, builds a signed Windows updater bundle, uploads the installer and updater bundle to the package registry, and publishes `latest.json` at: The Gitea workflow patches the public key into `apps/app/tauri-release.conf.json` at build time, builds a signed Windows updater bundle, uploads the installer and updater bundle to the package registry, and publishes `latest.json` at:
```text ```text
https://git.wilkensxl.de/api/packages/MrSphay/generic/modrinth-plus/latest/latest.json https://git.wilkensxl.de/api/packages/MrSphay/generic/modrinth-plus/latest/latest.json
``` ```
The Windows installer and bundled executables are Authenticode-signed with the MrTrust code-signing certificate. Users who installed MrTrust before installing Modrinth Plus should see the artifacts as signed by the trusted MrSphay certificate chain. MrTrust does not disable Defender, SmartScreen, UAC, or enterprise policy.