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

Using Azure Trusted Signing on Docker #8649

Open
arantes555 opened this issue Oct 31, 2024 · 8 comments
Open

Using Azure Trusted Signing on Docker #8649

arantes555 opened this issue Oct 31, 2024 · 8 comments

Comments

@arantes555
Copy link

arantes555 commented Oct 31, 2024

  • Electron-Builder Version: 25.1.8 & 26.0.0-alpha.4
  • Node Version: v20.15.1 in docker electronuserland/builder:wine-mono
  • Electron Version: all
  • Electron Type (current, beta, nightly): all
  • Target: Windows

I am trying to use the new Azure Trusted Signing on a (linux) docker container, using the electronuserland/builder:wine or electronuserland/builder:wine-mono image.

This fails with the below errors.

On 25.1.8:
 • signing with Azure Trusted Signing (beta)  path=releases/win-ia32-unpacked/SealdLimbo.exe
  • Above command failed, retrying 3 more times
[13:27:57] [release]: Error!
[13:27:57] Tasks failed after 36.25 s
Error: Exit code: ENOENT. spawn prlctl ENOENT
    at /project/node_modules/builder-util/src/util.ts:138:18
    at exithandler (node:child_process:430:5)
    at ChildProcess.errorhandler (node:child_process:442:5)
    at ChildProcess.emit (node:events:519:28)
    at Process.ChildProcess._handle.onexit (node:internal/child_process:292:12)
    at onErrorNT (node:internal/child_process:484:16)
    at processTicksAndRejections (node:internal/process/task_queues:82:21)
From previous event:
    at processImmediate (node:internal/timers:478:21)
From previous event:
    at WinPackager.signApp (/project/node_modules/app-builder-lib/src/winPackager.ts:270:27)
    at WinPackager.doSignAfterPack (/project/node_modules/app-builder-lib/src/platformPackager.ts:346:32)
    at WinPackager.doPack (/project/node_modules/app-builder-lib/src/platformPackager.ts:331:7)
    at WinPackager.pack (/project/node_modules/app-builder-lib/src/platformPackager.ts:138:5)
    at Packager.doBuild (/project/node_modules/app-builder-lib/src/packager.ts:459:9)
    at executeFinally (/project/node_modules/builder-util/src/promise.ts:12:14)
    at Packager.build (/project/node_modules/app-builder-lib/src/packager.ts:393:31)
    at executeFinally (/project/node_modules/builder-util/src/promise.ts:12:14)
    at /project/node_modules/centimaitre/centimaitre.js:121:14
    at executeTask (/project/node_modules/centimaitre/centimaitre.js:132:3)
    at async Promise.all (index 0) {
On 26.0.0-alpha.4:
  • signing with Azure Trusted Signing (beta)  path=releases/win-ia32-unpacked/SealdLimbo.exe
[13:33:36] [release]: Error!
[13:33:36] Tasks failed after 28.83 s
Error: Exit code: ENOENT. spawn prlctl ENOENT
    at /project/node_modules/builder-util/src/util.ts:138:18
    at exithandler (node:child_process:430:5)
    at ChildProcess.errorhandler (node:child_process:442:5)
    at ChildProcess.emit (node:events:519:28)
    at Process.ChildProcess._handle.onexit (node:internal/child_process:292:12)
    at onErrorNT (node:internal/child_process:484:16)
    at processTicksAndRejections (node:internal/process/task_queues:82:21) {

Digging into the relevant code, it looks like it's trying to boot a Parallels VM.

Is it currently impossible to use Azure Trusted Signing on the Docker build image, or any unix system ? I did not find any warning about it in the documentation.

(For anyone interested, I did manage to find a -convoluted- workaround using JSign)

@mmaietta
Copy link
Collaborator

mmaietta commented Oct 31, 2024

Is it currently impossible to use Azure Trusted Signing on the Docker build image, or any unix system ? I did not find any warning about it in the documentation.

Correct, it's trying to boot a parallels VM as it's configured to use powershell and that's not in the default electron-builder-provided docker image. This has always been the logic though for accessing powershell commands

What does your JSign solution look like? Do you have a gist or public repo you could link?

@arantes555
Copy link
Author

arantes555 commented Oct 31, 2024

@mmaietta Thanks for your answer. I guess my workflow never used powershell commands previously, so I never hit that bit of logic. Now that powershell is available for Linux, is there any chance that that could be used ?

My solution with JSign is pretty simple (though I had to use the snapshot build of JSign 7.0 as 6.0 does not support Azure Trusted Signing, and 7.0 is not out yet)

(The relevant part of) my build config:

{
      /*...*/
      win: {
        signtoolOptions: {
          publisherName: 'My Publisher',
          signingHashAlgorithms: ['sha256'], // only sign sha256, no need to sign sha1 that was for windows 7 which we do not support anyway
          sign: (process.env.AZURE_CODESIGNING_ACCESS_TOKEN && process.env.AZURE_CODESIGNING_PROFILE) ? './scripts/sign-win.js' : null
        }
      }
}

With sign-win.js:

exports.default = async function (configuration) {
  require('child_process').execSync(
    `jsign --storetype TRUSTEDSIGNING --keystore weu.codesigning.azure.net --storepass ${process.env.AZURE_CODESIGNING_ACCESS_TOKEN} --alias ${process.env.AZURE_CODESIGNING_PROFILE} "${configuration.path}"`,
    {
      stdio: 'inherit'
    }
  )
}

The script to run the build with signature:

az login --service-principal --tenant ${AZURE_TENANT} -u ${AZURE_SERVICE_PRINCIPAL_ID} -p ${AZURE_SERVICE_PRINCIPAL_SECRET}
export AZURE_CODESIGNING_ACCESS_TOKEN=$(az account get-access-token --resource https://codesigning.azure.net | jq -r '.accessToken')
npm run release:limbo -- --targetPlatform=win32 --targetType=nsis

All this on a custom docker image to have all tools:

FROM electronuserland/builder:20-wine-07.24
RUN apt-get update
RUN apt-get install -y azure-cli jq
# From https://github.com/ebourg/jsign/actions/runs/11514023277
ADD jsign_7.0~SNAPSHOT_all.deb /tmp/
RUN apt-get install -y /tmp/jsign_7.0~SNAPSHOT_all.deb
RUN apt-get clean autoclean
RUN apt-get autoremove -y
RUN rm -rf /var/lib/{apt,dpkg,cache,log}/ /tmp/jsign_7.0~SNAPSHOT_all.deb

@mmaietta
Copy link
Collaborator

mmaietta commented Oct 31, 2024

AFAICT, there are only Microsoft-provided docker images w/ powershell available. I haven't been able to locate any docker image scripts or dockerfiles that install powershell core themselves.

@arantes555
Copy link
Author

@mmaietta I just did it without problem, on electronuserland/builder:wine, following https://learn.microsoft.com/en-us/powershell/scripting/install/install-ubuntu?view=powershell-7.4#installation-via-package-repository-the-package-repository

apt-get update
apt-get install -y apt-transport-https software-properties-common

wget -q https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb

dpkg -i packages-microsoft-prod.deb

rm packages-microsoft-prod.deb

apt-get update
apt-get install -y powershell

@mmaietta
Copy link
Collaborator

Nice find! Okay, now that we have powershell installed, we need these commands to successfully execute

Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser
Install-Module -Name TrustedSigning -RequiredVersion 0.4.1 -Force -Repository PSGallery -Scope CurrentUser

Using command line:

pwsh -NoProfile -NonInteractive -Command <cmd>

It seems to run successfully for me on a local image, would love for you to try it out from your side as well. Just need to figure out how to discern we're in a docker container and not needing to execute on a parallels VM

@arantes555
Copy link
Author

First command fails for me :

root@fa5fee24658f:/project# pwsh -NoProfile -NonInteractive -Command "Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser"
Install-PackageProvider: No match was found for the specified search criteria for the provider 'NuGet'. The package provider requires 'PackageManagement' and 'Provider' tags. Please check if the specified package has the tags.

Second one succeeds though.

Microsoft's doc seems to also require Microsoft.Windows.SDK.BuildTools according to https://learn.microsoft.com/en-us/azure/trusted-signing/how-to-signing-integrations#download-and-install-signtool . Maybe it's already in your docker image ?

I am a complete novice in this powershell / microsoft build tools stuff ^^

@mmaietta
Copy link
Collaborator

mmaietta commented Nov 1, 2024

@arantes555 I think I was able to set it up correctly, but I'm hesitant on adding powershell to the default wine image as it adds 186mb to the image 😅 (and it's only used for Azure Trusted Signing)

I'm thinking of implementing the compatibility for Trusted Signing, but requiring that you provide the docker image with powershell installed. All electron-builder does internally is detect if it's in a docker container and then uses the pwsh command instead of pwsh.exe or powershell.exe

readonly powershellCommand = new Lazy<string>(async () => {
log.info(null, "identified inside docker container, checking for `pwsh` for powershell")
try {
await this.exec("pwsh", ["--version"])
return Promise.resolve("pwsh")
} catch (error: any) {
const errorMessage = "unable to find `pwsh` within docker container, please install per https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-linux"
log.error({ executable: "pwsh", message: error.message ?? error.stack }, errorMessage)
throw new Error(error.message ?? error.stack)
}
})

We already take a similar approach with brew install rpm for instance

What are your thoughts?

@arantes555
Copy link
Author

@arantes555 I think I was able to set it up correctly, but I'm hesitant on adding powershell to the default wine image as it adds 186mb to the image 😅 (and it's only used for Azure Trusted Signing)

Great ! About adding powershell, maybe you could try adding it to the wine-mono image ? Or create a new image wine-powershell ?

I'm thinking of implementing the compatibility for Trusted Signing, but requiring that you provide the docker image with powershell installed. All electron-builder does internally is detect if it's in a docker container and then uses the pwsh command instead of pwsh.exe or powershell.exe

readonly powershellCommand = new Lazy<string>(async () => {
log.info(null, "identified inside docker container, checking for `pwsh` for powershell")
try {
await this.exec("pwsh", ["--version"])
return Promise.resolve("pwsh")
} catch (error: any) {
const errorMessage = "unable to find `pwsh` within docker container, please install per https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-linux"
log.error({ executable: "pwsh", message: error.message ?? error.stack }, errorMessage)
throw new Error(error.message ?? error.stack)
}
})

We already take a similar approach with brew install rpm for instance

What are your thoughts?

Not a fan of basing the logic on detecting if we are in a docker container .. What if I am on a desktop linux (or mac) which has powershell locally ? It could use it, but it's gonna try booting a VM.

My suggestion: if not on windows, try detecting if pwsh is available with something like:

function isAvailable (name) {
  try {
    exec('which', [name])
    return true
  } catch {
    return false
  }
}

If it's available, use local pwsh, if not boot VM.

However, you are much more familiar that I am with the internals of electron-builder, so if your approach is easier to implement, let's go for it, it would still be much better !

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

No branches or pull requests

2 participants