Skip to content

Add Azure Artifact Signing as a Windows signer mode - #227

Merged
NeuroKoder3 merged 1 commit into
mainfrom
feat/azure-artifact-signing
Aug 2, 2026
Merged

Add Azure Artifact Signing as a Windows signer mode#227
NeuroKoder3 merged 1 commit into
mainfrom
feat/azure-artifact-signing

Conversation

@NeuroKoder3

Copy link
Copy Markdown
Owner

Summary

Adds TRANSTRACK_SIGN_MODE=azure — Azure Artifact Signing, Microsoft's cloud signing service. Roughly $10/month against several hundred a year for a CA certificate, with no certificate to buy, no hardware token, and no annual re-issue.

signtool loads Azure.CodeSigning.Dlib.dll, which authenticates to Azure and has the signature produced server-side. No private key is ever on the build machine, which bounds what an attacker gains by compromising one: they can request signatures while their access lasts, but they cannot take the key.

Two behaviours are handled in the signer rather than left to be discovered in production:

  • Artifact Signing certificates are valid for three days. A signature outlives them only because a timestamp proves it was made while the certificate was live. The signer always timestamps against Microsoft's authority. Skip it and the installer verifies for three days and then begins failing on customer machines with nothing about the file having changed.
  • DefaultAzureCredential walks a chain that includes a browser prompt, which hangs a headless build rather than failing it. With a service principal in the environment the signer narrows the chain to that one credential; with federated identity (GitHub OIDC, managed identity) it keeps the chain but excludes the browser.

The two common Azure failures both surface from signtool as a generic SignerSign() error, which says nothing about which of several settings is wrong. Each now gets a hint: a 403 is almost always a region mismatch between AZURE_SIGNING_ENDPOINT and the account, or a missing Certificate Profile Signer role; a dlib load failure is almost always a missing .NET 8 runtime or an x64/x86 mismatch between signtool and the dlib.

The release workflow's preflight job detects the mode using the same precedence as the signer's own auto-detect, so the two cannot disagree about which mode is in play, and installs the client tools on the runner. The release gate reports azure mode without requiring AZURE_SIGNING_DLIB — that is a path on the Windows machine that signed, and the gate runs on Linux, where requiring it would fail a correctly configured release.

Test plan

  • npm test — 50/50 suites; signWin.test.cjs now 31 cases
  • npm run lint clean, release.yml parses
  • node scripts/check-compliance-docs.mjs — 83 requirements, 83 matrix rows, 73 OQ cases, 28 risks
  • Release gate reports azure mode (Artifact Signing) with only the three non-path variables set, confirming it does not demand a Windows path on Linux
  • Auto-detect precedence asserted, including that a half-removed eSigner config still wins rather than silently changing modes
  • Metadata generation covered: endpoint/account/profile, credential-chain narrowing for both service principal and federated identity, correlation id from GITHUB_RUN_ID, and the timestamp authority
  • End-to-end signing needs an Azure account; everything ahead of the network call is covered

Follow-up for the account owner

Before enrolling, check one thing: Artifact Signing requires the organisation to have been verifiable for three years or more. Newer organisations can enrol but the certificate shows an unverified identity. If that blocks you, ssl_esigner with an OV certificate remains the fallback and is unchanged.

Setup is four steps, written out in docs/CODE_SIGNING.md: create the account and a Public Trust certificate profile, create a service principal with the Certificate Profile Signer role, install the client tools, set six secrets.

Roughly $10/month against several hundred a year for a CA certificate, with no
hardware token and no annual re-issue. signtool loads Azure.CodeSigning.Dlib.dll,
which authenticates to Azure and has the signature produced server-side, so no
private key is ever on the build machine -- a compromised build host can request
signatures while its access lasts but cannot take the key.

Two behaviours are handled rather than left to be discovered:

Artifact Signing certificates are valid for three days. A signature outlives
them only because a timestamp proves it was made while the certificate was live,
so the signer always timestamps against Microsoft's authority. Skip it and the
installer verifies for three days and then begins failing on customer machines
with nothing about the file having changed.

DefaultAzureCredential walks a chain of credential sources, one of which opens a
browser -- on a headless build that hangs rather than fails. With a service
principal in the environment the signer narrows the chain to it; with federated
identity it keeps the chain but excludes the browser.

The two common Azure failures both surface from signtool as a generic
SignerSign() error, so each gets a hint: a 403 is almost always a region
mismatch or a missing signer role assignment, and a dlib load failure is almost
always a missing .NET 8 runtime or an x64/x86 mismatch.

The release workflow's preflight now detects the mode with the same precedence
as the signer's own auto-detect, so the two cannot disagree, and installs the
client tools on the runner. The release gate reports the mode without requiring
AZURE_SIGNING_DLIB, which is a path on the Windows machine that signed and
cannot exist on the Linux host running the gate.

Co-authored-by: Cursor <cursoragent@cursor.com>
@strix-security

strix-security Bot commented Aug 2, 2026

Copy link
Copy Markdown

Strix is installed on this repository, but we couldn't run this PR security review because this workspace's trial has ended. Add a card to resume code reviews here.

@NeuroKoder3
NeuroKoder3 merged commit 92f0bfd into main Aug 2, 2026
16 checks passed
@NeuroKoder3
NeuroKoder3 deleted the feat/azure-artifact-signing branch August 2, 2026 01:32
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

Successfully merging this pull request may close these issues.

1 participant