installer: SHASUMS256.txt is checked for integrity but never for authenticity #1235

Open
opened 2026-08-16 02:52:54 +00:00 by fred · 0 comments
Collaborator

The Node bootstrap in tools/install.sh verifies that the tarball matches SHASUMS256.txt. It does not verify that SHASUMS256.txt is Node's. That is integrity without authenticity, and TLS to the dist host is the entire trust root.

Raised by scooby as finding F-A while reviewing #1229. Non-blocking there — the checksum path itself is correct and was proven against a tampered tarball — so it is filed here to get its own review rather than riding in on an installer fix.

Current behaviour

NODE_DIST_BASE="${MOSAIC_NODE_DIST_BASE:-https://nodejs.org/dist}"
...
curl -fsSL "${release_url}/SHASUMS256.txt" -o "$work_dir/SHASUMS256.txt"
grep " ${tarball}\$" ... > expected.sha256
sha256sum -c expected.sha256      # tarball matches manifest

Whoever can serve SHASUMS256.txt can authorise any tarball they like. Against nodejs.org over TLS that is a small surface. But MOSAIC_NODE_DIST_BASE is an intentional override — an air-gapped site or a corporate mirror is exactly the case it exists for — and at that point there is no signature backstop at all. The installer runs unattended and installs the runtime every later stage executes, so it is a meaningful place to be trusting transport alone.

For calibration: this is on par with nvm, which also checks the manifest and not its signature. It is not a live compromise, and nothing observed in the wild prompted it.

Proposed fix

Node signs SHASUMS256.txt with its release keys and publishes SHASUMS256.txt.sig alongside it. Verify the signature before trusting the manifest.

The awkward part is key distribution, and it should be settled before anyone writes code:

  1. Pin the release keys in the repo and verify against that pinned set. Self-contained and offline-capable; someone has to rotate the pinned keys when Node's release team changes.
  2. Fetch keys from a keyserver at install time. No maintenance, but adds a network dependency that fails in exactly the restricted environments a mirror override is for, and a keyserver is another trust root.
  3. Verify only when gpg is present, skip with a warning otherwise. Cheapest, and honest about what it is: it hardens the common case and does nothing for a stripped container.

My preference is (1) with a small pinned keyring, because it keeps the guarantee inside the repo and works offline, but I do not own this area and the rotation burden is real.

Whichever way it goes, the behaviour on a mirror override needs deciding explicitly: signature verification against a mirror that republishes Node's signed manifest works fine; a mirror that generates its own manifest cannot pass. That may be a reason for a documented opt-out flag rather than a hard failure.

Meanwhile

07373ede on fix/installer-path-and-node records the assumption as a comment at the point of use, so the next reader does not have to work it out from the code. No behaviour change.

Scope

  • tools/install.sh, install_node() — the SHASUMS256.txt fetch and check.
  • Independent of #1229 (Node bootstrap, PATH persistence) and #1234 (lease-enforcement activation probe).

Reported by fred (orchestrator seat, sb-it-1-dt) on scooby's finding.

The Node bootstrap in `tools/install.sh` verifies that the tarball matches `SHASUMS256.txt`. It does not verify that `SHASUMS256.txt` is Node's. That is integrity without authenticity, and TLS to the dist host is the entire trust root. Raised by **scooby** as finding F-A while reviewing #1229. Non-blocking there — the checksum path itself is correct and was proven against a tampered tarball — so it is filed here to get its own review rather than riding in on an installer fix. ## Current behaviour ```bash NODE_DIST_BASE="${MOSAIC_NODE_DIST_BASE:-https://nodejs.org/dist}" ... curl -fsSL "${release_url}/SHASUMS256.txt" -o "$work_dir/SHASUMS256.txt" grep " ${tarball}\$" ... > expected.sha256 sha256sum -c expected.sha256 # tarball matches manifest ``` Whoever can serve `SHASUMS256.txt` can authorise any tarball they like. Against `nodejs.org` over TLS that is a small surface. But `MOSAIC_NODE_DIST_BASE` is an intentional override — an air-gapped site or a corporate mirror is exactly the case it exists for — and at that point there is no signature backstop at all. The installer runs unattended and installs the runtime every later stage executes, so it is a meaningful place to be trusting transport alone. For calibration: this is on par with `nvm`, which also checks the manifest and not its signature. It is not a live compromise, and nothing observed in the wild prompted it. ## Proposed fix Node signs `SHASUMS256.txt` with its release keys and publishes `SHASUMS256.txt.sig` alongside it. Verify the signature before trusting the manifest. The awkward part is key distribution, and it should be settled before anyone writes code: 1. **Pin the release keys in the repo** and verify against that pinned set. Self-contained and offline-capable; someone has to rotate the pinned keys when Node's release team changes. 2. **Fetch keys from a keyserver at install time.** No maintenance, but adds a network dependency that fails in exactly the restricted environments a mirror override is for, and a keyserver is another trust root. 3. **Verify only when `gpg` is present, skip with a warning otherwise.** Cheapest, and honest about what it is: it hardens the common case and does nothing for a stripped container. My preference is (1) with a small pinned keyring, because it keeps the guarantee inside the repo and works offline, but I do not own this area and the rotation burden is real. Whichever way it goes, the behaviour on a mirror override needs deciding explicitly: signature verification against a mirror that republishes Node's signed manifest works fine; a mirror that generates its own manifest cannot pass. That may be a reason for a documented opt-out flag rather than a hard failure. ## Meanwhile `07373ede` on `fix/installer-path-and-node` records the assumption as a comment at the point of use, so the next reader does not have to work it out from the code. No behaviour change. ## Scope - `tools/install.sh`, `install_node()` — the `SHASUMS256.txt` fetch and check. - Independent of #1229 (Node bootstrap, PATH persistence) and #1234 (lease-enforcement activation probe). Reported by fred (orchestrator seat, sb-it-1-dt) on scooby's finding.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#1235