diff --git a/.github/workflows/multiOSReleases.yml b/.github/workflows/multiOSReleases.yml index 6e5810d6e2..024cccd441 100644 --- a/.github/workflows/multiOSReleases.yml +++ b/.github/workflows/multiOSReleases.yml @@ -408,6 +408,26 @@ jobs: echo "Generated tauri.windows.conf.json (alias masked):" sed "s/${KEYPAIR_ALIAS}/***/g" ./frontend/src-tauri/tauri.windows.conf.json + - name: Import release GPG signing key (Linux) + if: matrix.platform == 'ubuntu-22.04' + env: + RELEASE_GPG_PRIVATE_KEY: ${{ secrets.RELEASE_GPG_PRIVATE_KEY }} + run: | + echo "$RELEASE_GPG_PRIVATE_KEY" | gpg --batch --import + gpg --list-secret-keys --keyid-format=long + + - name: Make libjvm discoverable for linuxdeploy (Linux AppImage) + if: matrix.platform == 'ubuntu-22.04' + run: | + JAVA_LIBJVM="$JAVA_HOME/lib/server/libjvm.so" + if [ -f "$JAVA_LIBJVM" ]; then + sudo ln -sf "$JAVA_LIBJVM" /usr/lib/libjvm.so + echo "Linked libjvm from $JAVA_LIBJVM -> /usr/lib/libjvm.so" + else + echo "libjvm not found at $JAVA_LIBJVM" + exit 1 + fi + - name: Build Tauri app uses: tauri-apps/tauri-action@51a9f1156b33df106d827c3a78f8f894946c5faa # v0.5.25 env: @@ -418,7 +438,14 @@ jobs: APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} - APPIMAGETOOL_SIGN_PASSPHRASE: ${{ secrets.APPIMAGETOOL_SIGN_PASSPHRASE }} + # AppImage signing — three env vars work together: + # SIGN=1 tells linuxdeploy-plugin-appimage to forward --sign to appimagetool + # APPIMAGETOOL_SIGN_PASSPHRASE appimagetool uses this to unlock the GPG key non-interactively + # SIGN_KEY appimagetool picks the key matching this fingerprint + # Without SIGN=1, the other two are ignored and the AppImage is built unsigned even if a key is present. + SIGN: "1" + APPIMAGETOOL_SIGN_PASSPHRASE: ${{ secrets.RELEASE_GPG_PASSPHRASE }} + SIGN_KEY: ${{ vars.RELEASE_GPG_FINGERPRINT }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY: ${{ secrets.VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY || 'sb_publishable_UHz2SVRF5mvdrPHWkRteyA_yNlZTkYb' }} @@ -432,6 +459,16 @@ jobs: tauriScript: npx tauri args: ${{ matrix.args }} + - name: Clear release GPG key from runner keyring (Linux) + if: always() && matrix.platform == 'ubuntu-22.04' + env: + RELEASE_GPG_FINGERPRINT: ${{ vars.RELEASE_GPG_FINGERPRINT }} + run: | + if [ -n "$RELEASE_GPG_FINGERPRINT" ]; then + gpg --batch --yes --delete-secret-keys "$RELEASE_GPG_FINGERPRINT" || true + gpg --batch --yes --delete-keys "$RELEASE_GPG_FINGERPRINT" || true + fi + # Verify the MSI (outer wrapper users download) AND the inner exe extracted # from it (what actually gets installed and what AV scans). We don't check # target/.../release/stirling-pdf.exe - that's Tauri's intermediate build @@ -525,6 +562,7 @@ jobs: find . -name "*.app" -exec cp -r {} "../../../dist/Stirling-PDF-${{ matrix.name }}.app" \; else find . -name "*.deb" -exec cp {} "../../../dist/Stirling-PDF-${{ matrix.name }}.deb" \; + find . -name "*.rpm" -exec cp {} "../../../dist/Stirling-PDF-${{ matrix.name }}.rpm" \; find . -name "*.AppImage" -exec cp {} "../../../dist/Stirling-PDF-${{ matrix.name }}.AppImage" \; fi @@ -585,6 +623,7 @@ jobs: ./artifacts/**/*.msi ./artifacts/**/*.dmg ./artifacts/**/*.deb + ./artifacts/**/*.rpm ./artifacts/**/*.AppImage draft: false prerelease: false diff --git a/.github/workflows/tauri-build.yml b/.github/workflows/tauri-build.yml index 7e6eb34d5f..b03f5bfee4 100644 --- a/.github/workflows/tauri-build.yml +++ b/.github/workflows/tauri-build.yml @@ -292,6 +292,26 @@ jobs: } EOF + - name: Import release GPG signing key (Linux) + if: matrix.platform == 'ubuntu-22.04' + env: + RELEASE_GPG_PRIVATE_KEY: ${{ secrets.RELEASE_GPG_PRIVATE_KEY }} + run: | + echo "$RELEASE_GPG_PRIVATE_KEY" | gpg --batch --import + gpg --list-secret-keys --keyid-format=long + + - name: Make libjvm discoverable for linuxdeploy (Linux AppImage) + if: matrix.platform == 'ubuntu-22.04' + run: | + JAVA_LIBJVM="$JAVA_HOME/lib/server/libjvm.so" + if [ -f "$JAVA_LIBJVM" ]; then + sudo ln -sf "$JAVA_LIBJVM" /usr/lib/libjvm.so + echo "Linked libjvm from $JAVA_LIBJVM -> /usr/lib/libjvm.so" + else + echo "libjvm not found at $JAVA_LIBJVM" + exit 1 + fi + - name: Build Tauri app uses: tauri-apps/tauri-action@51a9f1156b33df106d827c3a78f8f894946c5faa # v0.5.25 env: @@ -302,7 +322,14 @@ jobs: APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} - APPIMAGETOOL_SIGN_PASSPHRASE: ${{ secrets.APPIMAGETOOL_SIGN_PASSPHRASE }} + # AppImage signing — three env vars work together: + # SIGN=1 tells linuxdeploy-plugin-appimage to forward --sign to appimagetool + # APPIMAGETOOL_SIGN_PASSPHRASE appimagetool uses this to unlock the GPG key non-interactively + # SIGN_KEY appimagetool picks the key matching this fingerprint + # Without SIGN=1, the other two are ignored and the AppImage is built unsigned even if a key is present. + SIGN: "1" + APPIMAGETOOL_SIGN_PASSPHRASE: ${{ secrets.RELEASE_GPG_PASSPHRASE }} + SIGN_KEY: ${{ vars.RELEASE_GPG_FINGERPRINT }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY: ${{ secrets.VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY || 'sb_publishable_UHz2SVRF5mvdrPHWkRteyA_yNlZTkYb' }} @@ -315,6 +342,16 @@ jobs: tauriScript: npx tauri args: ${{ matrix.args }} + - name: Clear release GPG key from runner keyring (Linux) + if: always() && matrix.platform == 'ubuntu-22.04' + env: + RELEASE_GPG_FINGERPRINT: ${{ vars.RELEASE_GPG_FINGERPRINT }} + run: | + if [ -n "$RELEASE_GPG_FINGERPRINT" ]; then + gpg --batch --yes --delete-secret-keys "$RELEASE_GPG_FINGERPRINT" || true + gpg --batch --yes --delete-keys "$RELEASE_GPG_FINGERPRINT" || true + fi + - name: Verify notarization (macOS only) if: matrix.platform == 'macos-15' || matrix.platform == 'macos-15-intel' run: | @@ -345,6 +382,7 @@ jobs: find . -name "*.dmg" -exec cp {} "../../../dist/Stirling-PDF-${{ matrix.name }}.dmg" \; else find . -name "*.deb" -exec cp {} "../../../dist/Stirling-PDF-${{ matrix.name }}.deb" \; + find . -name "*.rpm" -exec cp {} "../../../dist/Stirling-PDF-${{ matrix.name }}.rpm" \; find . -name "*.AppImage" -exec cp {} "../../../dist/Stirling-PDF-${{ matrix.name }}.AppImage" \; fi @@ -442,8 +480,8 @@ jobs: fi else echo "Checking for Linux artifacts..." - find . -name "*.deb" -o -name "*.AppImage" | head -5 - if [ $(find . -name "*.deb" -o -name "*.AppImage" | wc -l) -eq 0 ]; then + find . -name "*.deb" -o -name "*.rpm" -o -name "*.AppImage" | head -5 + if [ $(find . -name "*.deb" -o -name "*.rpm" -o -name "*.AppImage" | wc -l) -eq 0 ]; then echo "❌ No Linux artifacts found" exit 1 fi @@ -456,7 +494,7 @@ jobs: run: | cd ./frontend/src-tauri/target echo "Artifact sizes for ${{ matrix.name }}:" - find . -name "*.exe" -o -name "*.dmg" -o -name "*.deb" -o -name "*.AppImage" -o -name "*.msi" | while read file; do + find . -name "*.exe" -o -name "*.dmg" -o -name "*.deb" -o -name "*.rpm" -o -name "*.AppImage" -o -name "*.msi" | while read file; do if [ -f "$file" ]; then size=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null || echo "unknown") echo "$file: $size bytes" @@ -500,7 +538,7 @@ jobs: 'Stirling-PDF-windows-x86_64': { icon: '🪟', platform: 'Windows x64', files: '.exe, .msi' }, 'Stirling-PDF-macos-aarch64': { icon: '🍎', platform: 'macOS ARM64', files: '.dmg' }, 'Stirling-PDF-macos-x86_64': { icon: '🍎', platform: 'macOS Intel', files: '.dmg' }, - 'Stirling-PDF-linux-x86_64': { icon: '🐧', platform: 'Linux x64', files: '.deb, .AppImage' } + 'Stirling-PDF-linux-x86_64': { icon: '🐧', platform: 'Linux x64', files: '.deb, .rpm, .AppImage' } }; let commentBody = `## 📦 Tauri Desktop Builds Ready!\n\n`; diff --git a/.gitignore b/.gitignore index 1275a0991d..7a632d1a8b 100644 --- a/.gitignore +++ b/.gitignore @@ -217,6 +217,9 @@ id_ecdsa.pub id_ed25519 id_ed25519.pub .ssh/ + +# Allow the published GPG release signing public key (safe to share) +!docs/security/signing-key.pub *ssh # Taskfile checksum cache diff --git a/docs/security/VERIFYING_RELEASES.md b/docs/security/VERIFYING_RELEASES.md new file mode 100644 index 0000000000..2ddb8d905b --- /dev/null +++ b/docs/security/VERIFYING_RELEASES.md @@ -0,0 +1,114 @@ +# Verifying Stirling-PDF Release Artifacts + +Every Linux release artifact (`.AppImage`, `.rpm`, `.deb`) is signed with the +**Stirling-PDF release signing key**. Users are encouraged to verify downloads +before running them, especially when obtaining Stirling-PDF from a mirror, +redistributor, or any source other than the official +[GitHub Releases page](https://github.com/Stirling-Tools/Stirling-PDF/releases). + +## Signing key + +| Field | Value | +|--------------|----------------------------------------------------| +| User ID | `Stirling PDF Inc. ` | +| Fingerprint | `EBB9 258B FEA4 7D92 342F 00DF B8C0 96A5 9BEF 2A8B` | +| Algorithm | RSA-4096 | +| Valid until | 2031-04-16 | + +The public key is committed to this repository at +[`docs/security/signing-key.pub`](signing-key.pub) and is also published on: + +- https://keys.openpgp.org/search?q=EBB9258BFEA47D92342F00DFB8C096A59BEF2A8B +- https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xEBB9258BFEA47D92342F00DFB8C096A59BEF2A8B + +Cross-checking the fingerprint from two independent sources (the repository and +a keyserver) is the recommended way to be sure you've obtained the genuine key. + +## One-time setup — import the public key + +```bash +# Option 1 — from the repo over HTTPS +curl -fsSL https://raw.githubusercontent.com/Stirling-Tools/Stirling-PDF/main/docs/security/signing-key.pub \ + | gpg --import + +# Option 2 — from a keyserver +gpg --keyserver hkps://keys.openpgp.org \ + --recv-keys EBB9258BFEA47D92342F00DFB8C096A59BEF2A8B +``` + +Confirm the fingerprint matches after import: + +```bash +gpg --fingerprint contact@stirlingpdf.com +# Expected: EBB9 258B FEA4 7D92 342F 00DF B8C0 96A5 9BEF 2A8B +``` + +## Verifying an `.AppImage` + +Tauri's AppImage bundler embeds the signature inside the AppImage itself via +`appimagetool --sign`. Extract and verify: + +```bash +# --appimage-signature prints the embedded signature +./Stirling-PDF_*.AppImage --appimage-signature > sig.asc +./Stirling-PDF_*.AppImage --appimage-offset # shows the offset +# Verify the payload signature against the key +gpg --verify sig.asc Stirling-PDF_*.AppImage +``` + +A successful result looks like: + +``` +gpg: Good signature from "Stirling PDF Inc. " [ultimate] +``` + +## Verifying an `.rpm` + +RPM signatures are verified via `rpm --checksig`: + +```bash +# Import the key into rpm's keyring +sudo rpm --import docs/security/signing-key.pub # if working from a clone +# OR +sudo rpm --import https://raw.githubusercontent.com/Stirling-Tools/Stirling-PDF/main/docs/security/signing-key.pub + +# Verify the package +rpm --checksig Stirling-PDF-*.rpm +# Expected output ends with: "digests signatures OK" +``` + +## Verifying a `.deb` + +Debian packages are signed with a detached `.asc` file distributed alongside +the `.deb` on the release page: + +```bash +gpg --verify Stirling-PDF-*.deb.asc Stirling-PDF-*.deb +``` + +## What if verification fails? + +A failed signature check means **do not install the file**. Possible causes: + +- The download was corrupted — try again from the + [official releases](https://github.com/Stirling-Tools/Stirling-PDF/releases). +- You obtained the file from a malicious mirror — get it from the official + source. +- The signing key has rotated — check this document on the latest `main` for + the current fingerprint. + +If none of those explain it, please open a security report at +https://github.com/Stirling-Tools/Stirling-PDF/security/advisories/new. + +## Key rotation policy + +The signing key expires on **2031-04-16**. We will publish a new key at least +six months before expiry. The transition process: + +1. A new key is announced in release notes and this document is updated. +2. The last few releases will be co-signed with both the old and new keys. +3. The old key is published with a revocation notice once the transition is + complete. + +If the signing key is ever compromised, a revocation certificate will be +published immediately to both keyservers and to this document. diff --git a/docs/security/signing-key.pub b/docs/security/signing-key.pub new file mode 100644 index 0000000000..cc47ffe8ca --- /dev/null +++ b/docs/security/signing-key.pub @@ -0,0 +1,52 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +0546Zz4onsw5L5I/NzcaG5HK1YNUNA== +=vzHS +-----END PGP PUBLIC KEY BLOCK----- diff --git a/frontend/src-tauri/stirling-pdf.desktop b/frontend/src-tauri/stirling-pdf.desktop index 45db59c737..e9e26a39c1 100644 --- a/frontend/src-tauri/stirling-pdf.desktop +++ b/frontend/src-tauri/stirling-pdf.desktop @@ -3,7 +3,8 @@ Version=1.0 Type=Application Name=Stirling-PDF Comment=Locally hosted web application that allows you to perform various operations on PDF files -Exec=/usr/bin/stirling-pdf +TryExec={{exec}} +Exec={{exec}} %F Icon={{icon}} Terminal=false MimeType=application/pdf; @@ -12,4 +13,4 @@ Actions=open-file; [Desktop Action open-file] Name=Open PDF File -Exec=/usr/bin/stirling-pdf %F \ No newline at end of file +Exec={{exec}} %F \ No newline at end of file diff --git a/frontend/src-tauri/tauri.conf.json b/frontend/src-tauri/tauri.conf.json index 1f333a0413..117aea592e 100644 --- a/frontend/src-tauri/tauri.conf.json +++ b/frontend/src-tauri/tauri.conf.json @@ -24,7 +24,7 @@ "bundle": { "active": true, "publisher": "Stirling PDF Inc.", - "targets": ["deb", "rpm", "dmg", "msi"], + "targets": ["deb", "rpm", "appimage", "dmg", "msi"], "icon": [ "icons/icon.png", "icons/icon.icns", @@ -47,6 +47,12 @@ "linux": { "deb": { "desktopTemplate": "stirling-pdf.desktop" + }, + "rpm": { + "desktopTemplate": "stirling-pdf.desktop" + }, + "appimage": { + "bundleMediaFramework": false } }, "windows": {