Skip to content

ENT-13792: Made Windows MSI assembly fully reproducible#2366

Open
larsewi wants to merge 3 commits into
cfengine:masterfrom
larsewi:reproducible-msi-builds
Open

ENT-13792: Made Windows MSI assembly fully reproducible#2366
larsewi wants to merge 3 commits into
cfengine:masterfrom
larsewi:reproducible-msi-builds

Conversation

@larsewi

@larsewi larsewi commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

The issue was that wixl bakes a random package code, wall-clock summary-info timestamps, and per-file CAB timestamps into the MSI. Further more the wxs file used a random ProductCode. Hence, two builds of identical source produced different MSIs.

Fixed it by deriving the ProductCode and package code deterministically from the version, pinning the CAB file mtimes and summary-info to SOURCE_DATE_EPOCH.

$ ./build-in-container.py --platform ubuntu-24-mingw --project nova --role agent --build-type DEBUG --rebuild-image --output-dir out-a/
$ ./build-in-container.py --platform ubuntu-24-mingw --project nova --role agent --build-type DEBUG --rebuild-image --output-dir out-b/
$ sha256sum out-{a,b}/*.msi
21f7cca0b26cf4164d2c40c019dbdfc9ed842d1750fac9e7126a3d60eced0f85  out-a/cfengine-nova-3.29.0a-1-x86_64.msi
21f7cca0b26cf4164d2c40c019dbdfc9ed842d1750fac9e7126a3d60eced0f85  out-b/cfengine-nova-3.29.0a-1-x86_64.msi
  • Test bootstrap
  PS C:\Users\Administrator> msiexec /i "C:\Users\Administrator\Desktop\cfengine-nova-3.29.0a-1-x86_64.msi" /qn /l*v "C:\Users\Administrator\Desktop\cfengine-install-3.29.log"
PS C:\Users\Administrator> & 'C:\Program Files\Cfengine\bin\cf-agent.exe' -V
CFEngine Core 3.29.0a.e8a605dd9
CFEngine Enterprise 3.29.0a.22cb72acf
PS C:\Users\Administrator> & 'C:\Program Files\Cfengine\bin\cf-agent.exe' -B 172.31.7.249
  notice: Bootstrap mode: implicitly trusting server, use --trust-server=no if server trust is already established
  notice: Trusting new key: SHA=601bd5305ac7c8b70ecaddc64159f2f3dcd392f793edc7906290fbb36fd2a966
R: Bootstrapping from host '172.31.7.249' via built-in policy 'C:\Program Files\Cfengine\inputs\failsafe.cf'
R: This autonomous node assumes the role of voluntary client
R: Updated local policy from policy server
R: Triggered an initial run of the policy
R: Started the scheduler
  notice: Bootstrap to '172.31.7.249' completed successfully!
  • Test upgrade
PS C:\Users\Administrator> & 'C:\Program Files\Cfengine\bin\cf-agent.exe' -V
CFEngine Core 3.29.0
CFEngine Enterprise 3.29.0
PS C:\Users\Administrator> & 'C:\Program Files\Cfengine\bin\cf-agent.exe' -V
CFEngine Core 3.30.0
CFEngine Enterprise 3.30.0 

@larsewi
larsewi force-pushed the reproducible-msi-builds branch from 4a42102 to bb5abf3 Compare July 21, 2026 14:58
larsewi added 2 commits July 21, 2026 17:17
The issue was that wixl bakes a random package code, wall-clock
summary-info timestamps, and per-file CAB timestamps into the MSI.
Furthermore, the wxs file used a random ProductCode. Hence, two builds
of identical source produced different MSIs.

Fixed it by deriving the ProductCode and package code deterministically
from the version, pinning the CAB file mtimes and summary-info to
SOURCE_DATE_EPOCH.

Ticket: ENT-13792
Signed-off-by: Lars Erik Wik <lars.erik.wik@northern.tech>
build-scripts/package-msi now uses uuidgen for deterministic MSI GUIDs.

Ticket: ENT-13792
Signed-off-by: Lars Erik Wik <lars.erik.wik@northern.tech>
@larsewi
larsewi force-pushed the reproducible-msi-builds branch from ab91c5e to 4ee1901 Compare July 21, 2026 15:18
Early placement deleted equal-versioned shared DLLs on upgrade, leaving
cf-agent unable to load. Installing new files before removing the old
product keeps them via refcounting.

Ref: https://learn.microsoft.com/en-us/windows/win32/msi/removeexistingproducts-action
Signed-off-by: Lars Erik Wik <lars.erik.wik@northern.tech>
@larsewi
larsewi marked this pull request as ready for review July 22, 2026 10:48
@larsewi

larsewi commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@larsewi

larsewi commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

^ packages-based tests are unrealted to MinGW only build

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant