Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] support Azure Trusted Signing for signing tauri app #9578

Open
anatawa12 opened this issue Apr 26, 2024 · 23 comments · Fixed by #9865
Open

[feat] support Azure Trusted Signing for signing tauri app #9578

anatawa12 opened this issue Apr 26, 2024 · 23 comments · Fixed by #9865

Comments

@anatawa12
Copy link
Contributor

Describe the problem

Many certificate authority sells certificates with high price.
Recently Microsoft released Azure Trusted Signing for code signing and it's relatively cheap.

However, since signing tauri apps are completely integrated inside tauri cli and tauri cli doesn't have option to customize /dlib, /dmdf parameters for signtool, it looks we cannot use Azure Trusted Signing.

Describe the solution you'd like

Add some option to sign with Azure Trusted Signing

Alternatives considered

I think it might also be good to have some option to customize command-line options for signtool or use complately custom command for code signing.

Additional context

https://techcommunity.microsoft.com/t5/security-compliance-and-identity/trusted-signing-is-in-public-preview/ba-p/4103457
https://learn.microsoft.com/en-us/azure/trusted-signing/how-to-signing-integrations

@FabianLars
Copy link
Member

This PR will be converted into something generic before being merged #8718

At least once MS lifts the stupid 3-years tax history requirement, this should have explicit support in tauri imo (so that it's a direct config option that doesn't require the use of the generic option)

@Levminer
Copy link

Levminer commented Jun 3, 2024

As a workaround I created a CLI. It requires the azure cli, but downloads the required dlib for the Trusted Signing. Which gets called in the tauri.conf.json:

{
	"build": {
		"beforeBundleCommand": "trusted-signing-cli -e <url> -a <account name> -c <certificate profile name> path/to/exe/file1.exe",
	},

cc @FabianLars: I could integrate this into Tauri, should I create a PR?

@FabianLars
Copy link
Member

I don't think we'll add any specific solutions into tauri now with the generic solution (not sure). What i would love though is to collect different examples for the generic command (from the aforementioned #9865) in the docs.

@Levminer
Copy link

Levminer commented Jun 3, 2024

I don't think we'll add any specific solutions into tauri now with the generic solution (not sure). What i would love though is to collect different examples for the generic command (from the aforementioned #9865) in the docs.

Thats okay, but signing with the Trusted Signing requires the Azure CLI, a specific dlib, a manifest.json, so its not that simple. Also I think this will be the recommended (or required?) way to sign apps for Windows (accroding to Microsoft) when it leave the private beta. So I think Tauri should have a built-in way to sign with Trusted Signing.

anatawa12 pushed a commit to anatawa12/tauri that referenced this issue Jun 6, 2024
* feat(bundler): support custom sign command on Windows

closes tauri-apps#7188
closes tauri-apps#9578

* fix double quotes

* fix build

* fix build

* clippy

* Update sign.rs

* clippy && replace `winreg` with `windows-registry`

* remove log [skip ci]

* Apply suggestions from code review

* tweak arg so path with spaces work on macOS

* create nsis toolset paths

---------

Co-authored-by: Lucas Nogueira <[email protected]>
@anatawa12
Copy link
Contributor Author

Backport is #9902

@tobyspark
Copy link

tobyspark commented Jun 13, 2024

I don't think we'll add any specific solutions into tauri now with the generic solution (not sure). What i would love though is to collect different examples for the generic command (from the aforementioned #9865) in the docs.

Could I nudge your view a little? As a newcomer to Tauri, it strikes me as odd to be able to supply “platform native” signing credentials for one platform but not another. I get that Azure Trusted Signing is new, and the established-three-year issue certainly sucks, but it’s clearly the direction of travel.

Also as a newcomer, I assumed without direct support I could still e.g. add the MS supplied Github action to my publish workflow as a post-build step. But with a little understanding of what’s happening, of course it needs to happen before the bundling steps in the build process. The reasons why are good, I’m writing this to say there isn’t an escape hatch here. If the custom sign-tool of #9865 allows the provider-specific cloud based flows now pretty much required by EV certificate signing, that’s great, but as @Levminer notes is not adequate for Azure Trusted Signing.

@tobyspark
Copy link

tobyspark commented Jun 19, 2024

FWIW, here is the implementation I arrived for Azure Trusted Signing in Tauri on a GitHub actions workflow.

I add a prior step to the Tauri action that installs @Levminer’s CLI tool, and assembles and inserts the beforeBundleCommand entry to invoke it into tauri.conf.json. You’ll need to know the exact path of the file(s) you want to sign. You then supply the remaining Azure credentials alongside the Apple ones in the Tauri action.

      - name: install dependencies (windows)
        if: matrix.platform == 'windows-latest'
        # The approach here uses the cli tool workaround from the GH issue linked below, with some tauri.conf.json manipulation to add the sign command only for windows and pre-populated with secrets
        # https://github.com/tauri-apps/tauri/issues/9578
        # Note Github hosted runners appear to have `az` pre-installed, which is a requirement of trusted-signing-cli
        shell: bash
        env:
          BEFORE_BUNDLE_COMMAND: trusted-signing-cli -e "${{ secrets.AZURE_ENDPOINT }}" -a "${{ secrets.AZURE_CODE_SIGNING_NAME }}" -c "${{ secrets.AZURE_CERT_PROFILE_NAME }}" "${{ github.workspace }}/src-tauri/target/release/deps/<your app>.exe"
        run: |
          cd "$GITHUB_WORKSPACE"
          cat './src-tauri/tauri.conf.json' | jq '.build += {
              "beforeBundleCommand": env.BEFORE_BUNDLE_COMMAND
            }' > './src-tauri/temp.json' && mv ‘./src-tauri/temp.json' './src-tauri/tauri.conf.json'
          cargo install trusted-signing-cli

      - uses: tauri-apps/tauri-action@v0
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          APPLE_CERTIFICATE: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_CERTIFICATE }}
          APPLE_CERTIFICATE_PASSWORD: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_CERTIFICATE_PASSWORD }}
          APPLE_SIGNING_IDENTITY: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_SIGNING_IDENTITY }}
          APPLE_ID: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_ID }}
          APPLE_PASSWORD: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_PASSWORD }}
          APPLE_TEAM_ID: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_TEAM_ID }}
          AZURE_TENANT_ID: ${{ matrix.platform == 'windows-latest' && secrets.AZURE_TENANT_ID }}
          AZURE_CLIENT_ID: ${{ matrix.platform == 'windows-latest' && secrets.AZURE_CLIENT_ID }}
          AZURE_CLIENT_SECRET: ${{ matrix.platform == 'windows-latest' && secrets.AZURE_CLIENT_SECRET }}
      [...]

Now, have I actually got a signed build? No, but the problem seems to be with the credentials I’ve been supplied with, as the Azure Trusted Signing action fails in the same way.

@tobyspark
Copy link

At least once MS lifts the stupid 3-years tax history requirement, this should have explicit support in tauri imo (so that it's a direct config option that doesn't require the use of the generic option)

Looks like there’s movement here, this is from a week ago (June 2024): "we are on the process of rolling out this feature for individual maintainers and or young corporations, our expectation is that it should be publicly available in the next upcoming weeks.” – Azure/trusted-signing-action#10 (comment)

@bra1n
Copy link

bra1n commented Jun 19, 2024

Looks like there’s movement here, this is from a week ago (June 2024): "we are on the process of rolling out this feature for individual maintainers and or young corporations, our expectation is that it should be publicly available in the next upcoming weeks.” – Azure/trusted-signing-action#10 (comment)

In light of this, can we re-open this ticket to collect all relevant discussions in one issue rather than across multiple new ones, please? @FabianLars @lucasfernog

Edit: for context, I'm in a similar position as OP in that I would like to have a simple way to use the (official) MS / Azure code sign pipeline with Tauri, one that doesn't need extra steps or external tooling.

@Levminer
Copy link

FWIW, here is the implementation I arrived for Azure Trusted Signing in Tauri on a GitHub actions workflow.

I add a prior step to the Tauri action that installs @Levminer’s CLI tool, and assembles and inserts the beforeBundleCommand entry to invoke it into tauri.conf.json. You’ll need to know the exact path of the file(s) you want to sign. You then supply the remaining Azure credentials alongside the Apple ones in the Tauri action.

      - name: install dependencies (windows)
        if: matrix.platform == 'windows-latest'
        # The approach here uses the cli tool workaround from the GH issue linked below, with some tauri.conf.json manipulation to add the sign command only for windows and pre-populated with secrets
        # https://github.com/tauri-apps/tauri/issues/9578
        # Note Github hosted runners appear to have `az` pre-installed, which is a requirement of trusted-signing-cli
        shell: bash
        env:
          BEFORE_BUNDLE_COMMAND: trusted-signing-cli -e "${{ secrets.AZURE_ENDPOINT }}" -a "${{ secrets.AZURE_CODE_SIGNING_NAME }}" -c "${{ secrets.AZURE_CERT_PROFILE_NAME }}" "${{ github.workspace }}/src-tauri/target/release/deps/<your app>.exe"
        run: |
          cd "$GITHUB_WORKSPACE"
          cat './src-tauri/tauri.conf.json' | jq '.build += {
              "beforeBundleCommand": env.BEFORE_BUNDLE_COMMAND
            }' > './src-tauri/temp.json' && mv ‘./src-tauri/temp.json' './src-tauri/tauri.conf.json'
          cargo install trusted-signing-cli

      - uses: tauri-apps/tauri-action@v0
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          APPLE_CERTIFICATE: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_CERTIFICATE }}
          APPLE_CERTIFICATE_PASSWORD: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_CERTIFICATE_PASSWORD }}
          APPLE_SIGNING_IDENTITY: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_SIGNING_IDENTITY }}
          APPLE_ID: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_ID }}
          APPLE_PASSWORD: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_PASSWORD }}
          APPLE_TEAM_ID: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_TEAM_ID }}
          AZURE_TENANT_ID: ${{ matrix.platform == 'windows-latest' && secrets.AZURE_TENANT_ID }}
          AZURE_CLIENT_ID: ${{ matrix.platform == 'windows-latest' && secrets.AZURE_CLIENT_ID }}
          AZURE_CLIENT_SECRET: ${{ matrix.platform == 'windows-latest' && secrets.AZURE_CLIENT_SECRET }}
      [...]

Now, have I actually got a signed build? No, but the problem seems to be with the credentials I’ve been supplied with, as the Azure Trusted Signing action fails in the same way.

Did you set up the permissions? (https://learn.microsoft.com/en-us/azure/trusted-signing/tutorial-assign-roles) If you can't sign with the official action this should be the problem.

@tobyspark
Copy link

tobyspark commented Jun 19, 2024

Did you set up the permissions? (https://learn.microsoft.com/en-us/azure/trusted-signing/tutorial-assign-roles) If you can't sign with the official action this should be the problem.

For the work I’ve got I’m being supplied with them rather than doing it myself. But I’ve read every damn explainer and screenshared with the project owner while they go through it... and yeah, we’ve dotted all the is and crossed all the ts as far as we can see.

@bra1n
Copy link

bra1n commented Jun 19, 2024

@tobyspark In case you haven't seen it, this guide has been extremely helpful with getting the cert up and running: https://melatonin.dev/blog/code-signing-on-windows-with-azure-trusted-signing/ (I'm currently still in the validation step unfortunately)

@FabianLars FabianLars reopened this Jun 19, 2024
@tobyspark
Copy link

tobyspark commented Jun 26, 2024

With good credentials, what I posted above works. But – that’s only signing the app executable, and it is now clear to me that the installer needs to be signed. Does anybody have a working solution for that? e.g.

  • there is no afterBundleCommand equivalent to beforeBundleCommand I could use to invoke the signing within the Tauri build action
  • the Wix documentation mentions signing, but in the context of the .wixproj whatever that is. Empirically, adding the SignMSI part as a Wix fragment in the Tauri conf doesn’t work. I’m assuming this is trivial to someone who is used to Windows and/or Tauri itself.

Failing the above, I started to just brute-force it with a subsequent Github Action step, but even that is troublesome as the installer filename includes the dynamic version number.

Edit to add – I also tried the back-port of #9865 but it is not part of a v1.x release yet, and I couldn’t figure out the correct invocation of npm install to get the v1 branch. https://gitpkg.now.sh/tauri-apps/tauri/tooling/cli/node?1.x appears to work, correctly installs as @tauri-apps/cli but then on use fails on a platform specific version.

@FabianLars
Copy link
Member

Edit to add – I also tried the back-port of #9865 but it is not part of a v1.x release yet, and I couldn’t figure out the correct invocation of npm install to get the v1 branchEdit to add – I also tried the back-port of #9865 but it is not part of a v1.x release yet, and I couldn’t figure out the correct invocation of npm install to get the v1 branch

You can't really do that, only the cargo based cli works from git (because the npm based cli is 2 npm packages and you only get the wrapper one from git). You can however just install the cli in CI with a new step that runs cargo install tauri-cli --git https://github.com/tauri-apps/tauri --branch 1.x and then add tauriScript: cargo tauri to the tauri-action step. (Works locally as well of course)

@Levminer
Copy link

Here's my action: https://github.com/Levminer/authme/blob/dev/.github/workflows/release-artifacts.yml
And here is a helper script I'm using: https://github.com/Levminer/authme/blob/dev/scripts/sign.js

But basically:

  1. Sign the app in the beforeBundleCommand
  2. I get the version from the package.json
  3. Using that version I sign the msi or nsis

@bra1n
Copy link

bra1n commented Jun 26, 2024

Here's my action: https://github.com/Levminer/authme/blob/dev/.github/workflows/release-artifacts.yml And here is a helper script I'm using: https://github.com/Levminer/authme/blob/dev/scripts/sign.js

But basically:

  1. Sign the app in the beforeBundleCommand
  2. I get the version from the package.json
  3. Using that version I sign the msi or nsis

I looked into that - thanks for sharing! However, it's not using the Tauri Github Action, rather it does all the steps manually, right? I would like to use the Github Action because it does all the individual steps, including building, bundling, update sig generation and uploading in one place... 🤔

@bra1n
Copy link

bra1n commented Jun 26, 2024

With good credentials, what I posted above works. But – that’s only signing the app executable, and it is now clear to me that the installer needs to be signed. Does anybody have a working solution for that?

I finally did get it working! However, it requires Tauri v2, as it uses the new config parameter from #9865.

Here's the relevant Github Action block, taken from @Levminer's approach:

      - name: install dependencies (windows only)
        if: matrix.platform == 'windows-latest'
        shell: bash
        env:
          # replace Azure credentials before running
          WINDOWS_SIGN_COMMAND: trusted-signing-cli -e https://weu.codesigning.azure.net/ -a [AZURE_CODE_SIGNING_NAME] -c [AZURE_CERT_PROFILE_NAME] %1
        run: |
          cd "$GITHUB_WORKSPACE"
          cat './src-tauri/tauri.conf.json' | jq '.bundle .windows += {"signCommand": env.WINDOWS_SIGN_COMMAND}' > './src-tauri/temp.json' && mv './src-tauri/temp.json' './src-tauri/tauri.conf.json'
          cargo install trusted-signing-cli

I hard-coded the non-secret AZURE credentials there, but you can also use env vars if you want. The signCommand signs the built executable before bundling and the bundled installers afterwards. Since its run directly from within the Tauri CLI, you can then use the normal Tauri Github Action to take care of the Updater signing and uploading:

      - uses: tauri-apps/tauri-action@v0
        env:
          GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
          TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
          APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
          APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
          APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
          APPLE_ID: ${{ secrets.APPLE_ID }}
          APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
          AZURE_TENANT_ID: ${{ matrix.platform == 'windows-latest' && secrets.AZURE_TENANT_ID }}
          AZURE_CLIENT_ID: ${{ matrix.platform == 'windows-latest' && secrets.AZURE_CLIENT_ID }}
          AZURE_CLIENT_SECRET: ${{ matrix.platform == 'windows-latest' && secrets.AZURE_CLIENT_SECRET }}
        with:
          updaterJsonPreferNsis: true
          tagName: v__VERSION__ # the action automatically replaces \_\_VERSION\_\_ with the app version.
          releaseName: 'v__VERSION__ ${{ env.RELEASE_TITLE }}'
          releaseBody: "${{ env.RELEASE_NOTES }}"
          releaseDraft: true
          prerelease: false
          args: ${{ matrix.args }}

With this workflow I was able to get signed installers and also the installed app itself will run without any warning messages.

Edit: updated the config to remove the need for beforeBundleCommand, as pointed out by @FabianLars below.

@FabianLars
Copy link
Member

If you use the signCommand config you can remove beforeBundleCommand. Tauri will use the signCommand for everything that needs to be signed, including the inner executable.

@bra1n
Copy link

bra1n commented Jun 26, 2024

If you use the signCommand config you can remove beforeBundleCommand. Tauri will use the signCommand for everything that needs to be signed, including the inner executable.

Oh nice, I didn't know that! I'll adjust the above code then.

@jacobgorm
Copy link

One caveat with all the approaches above is that they will expose your Azure secrets to rogue code in any build.rs script you or your dependencies happen to drag via crates.io. I believe that tauri2 supports doing the bundling separately from the build, which would increase security.

@tobyspark
Copy link

Thanks all. In hindsight, why did I spent time trying to wrangle the back-port, when the upgrade to Tauri v2 proved quick and painless =]

@bra1n
Copy link

bra1n commented Jun 27, 2024

One caveat with all the approaches above is that they will expose your Azure secrets to rogue code in any build.rs script you or your dependencies happen to drag via crates.io. I believe that tauri2 supports doing the bundling separately from the build, which would increase security.

Good point! Do you happen to know how one could get an auxiliary build tool integrated into the Tauri build chain the easiest? Just run cargo add trusted-signing-cli in /src-tauri and commit the updated files?

@corentin35000

This comment was marked as resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants