[bug] Fail to sign protoc sidecar with Azure Trusted Signing
[!IMPORTANT]
The issue was that the sidecar binary was read-only but Tauri silenced the permission error. See https://github.com/tauri-apps/tauri/issues/11778#issuecomment-2495504198 for more info.
Describe the bug
I'm trying to get Azure Trusted Signing working for my app github.com/mountain-loop/yaak. It signs the main .exe correctly, and correctly skips the already-signed NodeJS sidecar. However, it seems to fail on the unsigned protoc sidecar.
Here is the output from https://github.com/mountain-loop/yaak/actions/runs/11976760384/job/33393142512
Finished `release` profile [optimized] target(s) in 11m 48s
warning: the following packages contain code that will be rejected by a future version of Rust: iso8601 v0.3.0, nom v4.2.3
note: to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id 1`
Built application at: D:\a\yaak\yaak\src-tauri\target\release\yaak-app.exe
Signing D:\a\yaak\yaak\src-tauri\target\release\yaak-app.exe
Signing D:\a\yaak\yaak\src-tauri\target\release\yaak-app.exe with a custom signing command
Info "[\r\n {\r\n \"cloudName\": \"AzureCloud\",\r\n \"homeTenantId\": \"***\",\r\n \"id\": \"b045e283-89f9-42ff-bd9a-95f6e7a9b035\",\r\n \"isDefault\": true,\r\n \"managedByTenants\": [],\r\n \"name\": \"Yaak Subscription\",\r\n \"state\": \"Enabled\",\r\n \"tenantId\": \"***\",\r\n \"user\": {\r\n \"name\": \"***\",\r\n \"type\": \"servicePrincipal\"\r\n }\r\n }\r\n]\r\n\r\nTrusted Signing\r\n\r\nVersion: 1.0.60\r\n\r\n\"Metadata\": {\r\n \"Endpoint\": \"[https://eus.codesigning.azure.net/\](https://eus.codesigning.azure.net//)",\r\n \"CodeSigningAccountName\": \"Yaak\",\r\n \"CertificateProfileName\": \"yaakapp\",\r\n \"ExcludeCredentials\": []\r\n}\r\n\r\nSubmitting digest for signing...\r\n\r\nOperationId 1eb5fd2a-01d6-45e8-b43a-bacda9cd5f54: InProgress\r\n\r\nSigning completed with status 'Succeeded' in 1.6176595s\r\n\r\nSuccessfully signed: D:\\a\\yaak\\yaak\\src-tauri\\target\\release\\yaak-app.exe\r\r\n\r\nNumber of files successfully Signed: 1\r\r\nNumber of warnings: 0\r\r\nNumber of errors: 0\r\r\n"
File: vendored\node\yaaknode-x86_64-pc-windows-msvc.exe
Index Algorithm Timestamp
========================================
0 sha256 RFC3161
Successfully verified: vendored\node\yaaknode-x86_64-pc-windows-msvc.exe
Info sidecar at "vendored\node\yaaknode-x86_64-pc-windows-msvc.exe" already signed. Skipping...
File: vendored\protoc\yaakprotoc-x86_64-pc-windows-msvc.exe
Index Algorithm Timestamp
========================================
SignTool Error: No signature found.
Number of errors: 1
Signing vendored\protoc\yaakprotoc-x86_64-pc-windows-msvc.exe
Signing vendored\protoc\yaakprotoc-x86_64-pc-windows-msvc.exe with a custom signing command
failed to bundle project: `failed to run trusted-signing-cli`
Error failed to bundle project: `failed to run trusted-signing-cli`
Error: Command failed with exit code 1: npm run tauri build
Reproduction
As seen in the tauri.conf.json#L82, the sign command I'm using is:
"windows": {
"signCommand": "trusted-signing-cli -e https://eus.codesigning.azure.net/ -a Yaak -c yaakapp %1"
}
To debug this, I created a new workflow to simply run this command on the protoc binary (committed it directly to the repo for simplicity), and it succeeded: https://github.com/mountain-loop/yaak/actions/runs/11975020946/job/33387429516
- name: Sign files
run: trusted-signing-cli -e https://eus.codesigning.azure.net/ -a Yaak -c yaakapp yaakprotoc-x86_64-pc-windows-msvc.exe
env:
AZURE_CLIENT_ID: ${{ matrix.platform == 'windows-latest' && secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ matrix.platform == 'windows-latest' && secrets.AZURE_CLIENT_SECRET }}
AZURE_TENANT_ID: ${{ matrix.platform == 'windows-latest' && secrets.AZURE_TENANT_ID }}
Expected behavior
protoc binary should sign successfully during tauri-action build
Full tauri info output
[✔] Environment
- OS: Mac OS 15.1.1 arm64 (X64)
✔ Xcode Command Line Tools: installed
✔ rustc: 1.82.0 (f6e511eec 2024-10-15)
✔ cargo: 1.82.0 (8f40fc59f 2024-08-21)
✔ rustup: 1.27.1 (54dd3d00f 2024-04-24)
✔ Rust toolchain: stable-aarch64-apple-darwin (default)
- node: 22.8.0
- npm: 10.8.2
- deno: deno 1.44.0
[-] Packages
- tauri 🦀: 2.1.1
- tauri-build 🦀: 2.0.3
- wry 🦀: 0.47.0
- tao 🦀: 0.30.8
- @tauri-apps/api : 2.0.2 (outdated, latest: 2.1.1)
- @tauri-apps/cli : 2.1.0
[-] Plugins
- tauri-plugin-dialog 🦀: 2.0.3
- @tauri-apps/plugin-dialog : 2.0.0 (outdated, latest: 2.0.1)
- tauri-plugin-updater 🦀: 2.0.2
- @tauri-apps/plugin-updater : not installed!
- tauri-plugin-log 🦀: 2.0.1
- @tauri-apps/plugin-log : 2.0.0
- tauri-plugin-window-state 🦀: 2.0.1
- @tauri-apps/plugin-window-state : not installed!
- tauri-plugin-os 🦀: 2.0.1
- @tauri-apps/plugin-os : 2.0.0
- tauri-plugin-fs 🦀: 2.0.3
- @tauri-apps/plugin-fs : 2.0.0 (outdated, latest: 2.0.2)
- tauri-plugin-shell 🦀: 2.0.2
- @tauri-apps/plugin-shell : 2.0.0 (outdated, latest: 2.0.1)
[-] App
- build-type: bundle
- CSP: unset
- frontendDist: ../dist
- devUrl: http://localhost:1420/
- framework: React
Stack trace
No response
Additional context
No response
Okay, it looks like this was indeed my fault. However, it seems that Tauri did not surface the error that would have helped me fix it.
Running the sign command directly worked, because I committed the binary directly to the repo. I did this on macOS.
However, signing the full app downloads and extracts the protoc binary from a GitHub release. The issue was that it extracts with a read-only permission.
I adjusted the test workflow to use the download script and got this helpful error: https://github.com/mountain-loop/yaak/actions/runs/11987805359/job/33422398760
[
{
"cloudName": "AzureCloud",
"homeTenantId": "***",
"id": "b045e283-89f9-42ff-bd9a-95f6e7a9b035",
"isDefault": true,
"managedByTenants": [],
"name": "Yaak Subscription",
"state": "Enabled",
"tenantId": "***",
"user": {
"name": "***",
"type": "servicePrincipal"
}
}
]
SignTool Error: Access is denied.
Number of files successfully Signed: 0
SignTool Error: An error occurred while attempting to sign: src-tauri/vendored/protoc/yaakprotoc-x86_64-pc-windows-msvc.exe
Number of warnings: 0
Number of errors: 1
thread 'main' panicked at C:\Users\runneradmin/.cargo\registry\src\index.crates.io-6f17d22bba15001f\trusted-signing-cli-0.3.0\src/main.rs:157:10:
called `Result::unwrap()` on an `Err` value: Custom { kind: Other, error: "command [\"C:\\\\Program Files (x86)\\\\Windows Kits\\\\10\\\\bin\\\\10.0.22000.0\\\\x64\\\\signtool.exe\", \"sign\", \"/v\", \"/fd\", \"SHA256\", \"/tr\", \"[http://timestamp.acs.microsoft.com\](http://timestamp.acs.microsoft.com/)", \"/td\", \"SHA256\", \"/dlib\", \"C:\\\\Users\\\\runneradmin\\\\.trusted-signing-cli\\\\lib\\\\bin\\\\x64\\\\Azure.CodeSigning.Dlib.dll\", \"/dmdf\", \"C:\\\\Users\\\\runneradmin\\\\.trusted-signing-cli\\\\metadata.json\", \"src-tauri/vendored/protoc/yaakprotoc-x86_64-pc-windows-msvc.exe\"] exited with code 1" }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Error: Process completed with exit code 1.
So it seems that Tauri is silencing stderr of the custom sign command?
So it seems that Tauri is silencing stderr of the custom sign command?
It may be hidden behind the --verbose flag (or -vv / -vvv, not sure if the verbosity level matters here) 🤔
Which command would that flag be on? Tauri should be running the same command as my test case
tauri build --verbose, or in tauri-action args: --verbose. The signCommand will be executed the same way, we'd just check if the tauri cli is hiding some command output from us.
Ah great, I will give this a try, thanks!