Just a few days ago, I've blogged about running E2E tests for an Electron app. But, once tested and verified, the next step is deployment.
If you've ever shipped an application for Windows users, you might be aware of the Windows SmartScreen filter. It's basically a check that might pop up and warn your users in case Microsoft doesn't have any data on your app being trustworthy. That's actually a great feature, since it encourages application developers to sign their programs and therefore increase overall app security. Except for when your build pipeline becomes compromised, as in the recent SolarWinds attack...
However, I'd like to focus on the technical aspects. For code signing certificates, there are two options available: Either a regular certificate, which has quite a low bar for verification and is available for less than 100,- € per year, or an Extended Validation (EV) certificate. The EV ones are much pricier, and do require the certificate issuer to perform a more in-depth check before handing it out. You pay more, but you get more: In contrast to regular certificates, which require a certain number of installs until Windows SmartScreen recognizes them as trustworthy, EV certificates work out of the box and don't show any warnings to your users. So, for anything commercial, you probably want to go with an EV certificate.
But it also comes with a catch: You usually need some hardware device to secure it, often an USB dongle or a Hardware Security Module (HSM). That's just a requirement for the heightened security about EV certificates, but this also means that it's harder to integrate it in automated build workflows. You don't want to be in a position where you need a dedicated build computer in your office where you manually need to insert some USB dongle to generate a release.
Here comes Azure Key Vault: It's a service available in the Azure Cloud which just kind of does everything around securing stuff, hence the name. It's typically used for things like secret management, e.g. to handle configuration for cloud services and the like, or just for plain SSL certificate management, e.g. with Let's Encrypt management via shibayan's key-vault-acme-bot. The part that's interesting for us in this article, however, is that the premium tier of Azure Key Vault allows you to store certificates on a HSM, securely on Microsoft infrastructure.
When going that way, you give up the option of exporting the certificate, e.g. temporary to your build server to sign applications. That would introduce too much of a security risk, so you need to remotely use the signing feature of Azure Key Vault to sign your apps.
And this is where this blog post is headed: When building an Electron app, you've got two steps. First, you build & publish the app, then it's getting packaged. Now, you want to sign both outputs, the actual *.exe files in the bundle as well as the installer itself. So, I'll show you some code that performs the build, calls a hook between the publish and package step to sign everything, and then finally signs the installer itself. All done via remote signing in Azure Key Vault.
I'm a big fan of the NUKE build system, so I'm using that for the actual build automation. The concept should be easily translatable to whatever system you're using. So, let's look at the code finally!
First, we're starting with this build script:
The part above is the target in NUKE that generates the Electron bundle. In this specific example, we're even using Electron.NET, since the app itself is really just a website that some customers want to use as a desktop application. I won't go into too much detail here, since the build process should be pretty straightforward. It's really just a wrapper around electron-builder with something injected here and there.
electronAfterPackHook.js, again, is a simple script. It's really just calling back out to the NUKE build system to run the SignExecutables target and passes the current output directory as an argument. The magic here is that this is now called during your build, after the executable has been created and patched with the icon, but before it's being bundled up by the installer.
Finally, we're using the AzureSign package to sign our executable with the certificate in Azure Key Vault.
After everything's done here, the build continues. At the end, the parent NUKE process just signs the installers and we're done. The next step would be publishing, for us that means generating the documentation and deploying the artifact.
So, you've seen we're really doing something like in the movie Inception: We're calling our build system from a hook from a build process initiated by the build system itself. It feels a bit Rube-Goldberg-y, but it works fine and is pretty simple to setup & understand.