Fix stale README signature key and guard against future drift

The README's secure-install snippet has pinned the wrong ed25519 key
since commit a60fa03d7 (April 22, 2026), so v6 rc.2 through rc.5 all
shipped with a documented verification step that does not work.

I downloaded the published rc.5 install.sh + install.sh.sshsig and
ran ssh-keygen -Y verify with both candidate keys:
  Ds21c5... (README's pinned key) -> Could not verify signature
  MZd/DaH... (key embedded in install.sh and pulse-auto-update.sh) -> OK

Customers who actually followed the README's secure-install path saw
"Could not verify signature" and aborted. Most users curl-pipe the
script unverified so the drift went unreported.

Replace the stale key in README.md and docs/INSTALL.md with the actual
pipeline signing key (MZd/...).

Add a validate-release.sh smoke that extracts the README's pinned key
and runs the exact ssh-keygen -Y verify command against the signed
install.sh.sshsig. Any future drift between documented key and actual
signing key fails the release before publish.

Lock both the correct-key presence and the stale-key absence in
build_release_assets_test.go for README and docs/INSTALL.md so a manual
edit cannot regress the docs back to the broken state.
This commit is contained in:
rcourtman 2026-05-12 10:30:42 +01:00
parent 49412357af
commit ce7d7c1956
4 changed files with 81 additions and 3 deletions

View file

@ -84,7 +84,7 @@ export PULSE_VERSION=vX.Y.Z
curl -fsSLO "https://github.com/rcourtman/Pulse/releases/download/${PULSE_VERSION}/install.sh"
curl -fsSLO "https://github.com/rcourtman/Pulse/releases/download/${PULSE_VERSION}/install.sh.sshsig"
ssh-keygen -Y verify \
-f <(printf '%s\n' 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDs21c5oPk2khrdHlsw1aZ9EJKoTsyalGzhb0hdwJrkV pulse-installer') \
-f <(printf '%s\n' 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMZd/DaH+BldzOkq1A8KVTcFk73nAyrE8aJOyf7i00jm pulse-installer') \
-I pulse-installer \
-n pulse-install \
-s install.sh.sshsig < install.sh

View file

@ -23,7 +23,7 @@ export PULSE_VERSION=vX.Y.Z
curl -fsSLO "https://github.com/rcourtman/Pulse/releases/download/${PULSE_VERSION}/install.sh"
curl -fsSLO "https://github.com/rcourtman/Pulse/releases/download/${PULSE_VERSION}/install.sh.sshsig"
ssh-keygen -Y verify \
-f <(printf '%s\n' 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDs21c5oPk2khrdHlsw1aZ9EJKoTsyalGzhb0hdwJrkV pulse-installer') \
-f <(printf '%s\n' 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMZd/DaH+BldzOkq1A8KVTcFk73nAyrE8aJOyf7i00jm pulse-installer') \
-I pulse-installer \
-n pulse-install \
-s install.sh.sshsig < install.sh
@ -98,7 +98,7 @@ export PULSE_VERSION=vX.Y.Z
curl -fsSLO "https://github.com/rcourtman/Pulse/releases/download/${PULSE_VERSION}/install.sh"
curl -fsSLO "https://github.com/rcourtman/Pulse/releases/download/${PULSE_VERSION}/install.sh.sshsig"
ssh-keygen -Y verify \
-f <(printf '%s\n' 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDs21c5oPk2khrdHlsw1aZ9EJKoTsyalGzhb0hdwJrkV pulse-installer') \
-f <(printf '%s\n' 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMZd/DaH+BldzOkq1A8KVTcFk73nAyrE8aJOyf7i00jm pulse-installer') \
-I pulse-installer \
-n pulse-install \
-s install.sh.sshsig < install.sh

View file

@ -283,6 +283,45 @@ func TestReleaseValidationRequiresSignedSidecars(t *testing.T) {
`Pulse Unified Agent Installer`,
`bash "$install_sh_path" --help`,
`Install specific version (e.g.`,
// README key drift guard — across v6 rc.2 → rc.5 the README pinned a
// stale ed25519 key that did not verify install.sh.sshsig, so anyone
// following the secure-install path saw "Could not verify signature".
// validate-release.sh must extract the README's pinned key and actually
// run ssh-keygen -Y verify against the signed installer.
`Validating README pinned signature key matches install.sh.sshsig`,
`grep -oE "ssh-ed25519 [A-Za-z0-9+/=]+ pulse-installer" "$readme_path"`,
`ssh-keygen -Y verify \`,
`README's pinned signature key does not verify install.sh.sshsig`,
}
readmeBytes, err := os.ReadFile(repoFile("README.md"))
if err != nil {
t.Fatalf("read README.md: %v", err)
}
readme := string(readmeBytes)
// Lock in the actual signing key documented to customers. This is the public
// counterpart of PULSE_UPDATE_SIGNING_KEY and matches what install.sh and
// scripts/pulse-auto-update.sh have embedded. A future edit cannot silently
// regress to the stale Ds21c5 key without tripping this assertion.
const correctReadmeKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMZd/DaH+BldzOkq1A8KVTcFk73nAyrE8aJOyf7i00jm pulse-installer"
if !strings.Contains(readme, correctReadmeKey) {
t.Fatalf("README.md must pin the correct pulse-installer ed25519 key for install.sh signature verification")
}
const staleReadmeKey = "Ds21c5oPk2khrdHlsw1aZ9EJKoTsyalGzhb0hdwJrkV"
if strings.Contains(readme, staleReadmeKey) {
t.Fatalf("README.md still references the stale pulse-installer key Ds21c5...; rc.2 → rc.5 shipped this drift")
}
installDocsBytes, err := os.ReadFile(repoFile("docs", "INSTALL.md"))
if err != nil {
t.Fatalf("read docs/INSTALL.md: %v", err)
}
installDocs := string(installDocsBytes)
if !strings.Contains(installDocs, correctReadmeKey) {
t.Fatalf("docs/INSTALL.md must pin the correct pulse-installer ed25519 key")
}
if strings.Contains(installDocs, staleReadmeKey) {
t.Fatalf("docs/INSTALL.md still references the stale pulse-installer key Ds21c5...")
}
for _, needle := range localRequired {
if !strings.Contains(localValidator, needle) {

View file

@ -517,6 +517,45 @@ while IFS= read -r line; do
done < checksums.txt
success "SSH signature sidecars validated"
# Actually run the README's documented verification step against install.sh.sshsig.
# The README ships a hardcoded ed25519 pubkey and tells customers to verify
# install.sh with it before running. Across v6 rc.2 → rc.5 (~20 days) the README
# pinned a stale key (Ds21c5...) that didn't match the actual pipeline signing
# key (MZd/...), so any customer who followed the secure-install path got
# "Could not verify signature" and aborted. This check extracts the README's
# pinned key and runs the exact verification command, so any drift between
# documented key and actual signing key fails the release.
info "Validating README pinned signature key matches install.sh.sshsig..."
readme_path="$(cd "$(dirname "$0")/.." && pwd)/README.md"
if [ ! -f "$readme_path" ]; then
error "README.md not found at $readme_path — cannot validate documented signature key"
exit 1
fi
readme_signing_key=$(grep -oE "ssh-ed25519 [A-Za-z0-9+/=]+ pulse-installer" "$readme_path" | head -1)
if [ -z "$readme_signing_key" ]; then
error "Could not extract ed25519 pulse-installer key from README.md secure-install snippet"
exit 1
fi
if ! command -v ssh-keygen >/dev/null 2>&1; then
error "ssh-keygen not found — required to validate the README-documented signature path"
exit 1
fi
readme_allowed_signers=$(mktemp)
printf 'pulse-installer %s\n' "$readme_signing_key" > "$readme_allowed_signers"
if ! ssh-keygen -Y verify \
-f "$readme_allowed_signers" \
-I pulse-installer \
-n pulse-install \
-s install.sh.sshsig < install.sh >/dev/null 2>&1; then
rm -f "$readme_allowed_signers"
error "README's pinned signature key does not verify install.sh.sshsig"
error "Customers who follow the README's secure-install ssh-keygen step will see 'Could not verify signature' and abort"
error "Either update README.md/docs/INSTALL.md with the correct pulse-installer pubkey, or fix the release signing key"
exit 1
fi
rm -f "$readme_allowed_signers"
success "README pinned signature key verifies install.sh.sshsig"
# Validate individual .sha256 files exist and match checksums.txt
info "Validating individual .sha256 files..."
while IFS= read -r line; do