Skip to content

Commit b3e7d7d

Browse files
authored
fix(installer): verify keyring signature and remove insecure bypass (#323)
Closes #314 (inert on squash->develop; close manually after merge). ## What - **#1** \`--apt\` path now downloads \`glyndor-archive-keyring.deb.sig\` and verifies the Ed25519 signature against the embedded release key **before** \`dpkg -i\` (fail-closed). - **#2** Removed \`PODUP_INSECURE_SKIP_VERIFY\` opt-out from \`install.sh\` + \`install.ps1\`. A strong cryptographic proof is now mandatory. ## Coordination The \`--apt\` verification requires the apt repo (\`Glyndor/apt\`) to publish \`glyndor-archive-keyring.deb.sig\`. A companion apt PR signs the keyring; this must land before the next release ships the new install.sh. ## Test \`bash -n install.sh\` OK. shellcheck/PSScriptAnalyzer run in CI (lint-shell, lint-powershell). Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
1 parent ca05b0a commit b3e7d7d

3 files changed

Lines changed: 32 additions & 24 deletions

File tree

docs/self-update.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ The one-line installer applies the same fail-closed policy. It requires **at
4444
least one** strong proof to succeed — the Ed25519 signature (when `python3` +
4545
`cryptography` and a configured public key are present) **or** the GitHub
4646
build-provenance attestation (`gh attestation verify`). If neither verifier is
47-
available it refuses to install. `PODUP_INSECURE_SKIP_VERIFY=1` is the explicit,
48-
documented opt-out (checksum only) for constrained environments.
47+
available it refuses to install. There is **no opt-out**: a checksum alone is not
48+
a trust anchor, so a verifier (`gh` or `python3` + `cryptography`) must be present
49+
at install time. The `--apt` path likewise verifies the keyring package's Ed25519
50+
signature before installing it as root.
4951

5052
## The embedded public keys
5153

install.ps1

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
# PODUP_VERSION Release tag to install (e.g. v0.3.0). Default: latest.
1111
# PODUP_INSTALL_DIR Installation directory. Default: %LOCALAPPDATA%\Programs\podup.
1212
# PODUP_RELEASE_PUBKEY_B64 Override the baked-in Ed25519 release public key (for forks).
13-
# PODUP_INSECURE_SKIP_VERIFY Set to 1 to accept checksum-only verification.
1413

1514
Set-StrictMode -Version Latest
1615
$ErrorActionPreference = 'Stop'
@@ -81,8 +80,7 @@ try {
8180
# SHA256SUMS. The binary is trusted only after at least one cryptographic proof
8281
# tied to the release key or the repository's build identity succeeds — the
8382
# Ed25519 signature over SHA256SUMS, or the GitHub build-provenance attestation.
84-
# If neither verifier can run, the install fails closed. Set
85-
# PODUP_INSECURE_SKIP_VERIFY=1 to explicitly opt out (checksum only).
83+
# If neither verifier can run, the install fails closed.
8684

8785
# Baked-in base64 (unpadded) raw Ed25519 public keys (32 bytes each) matching
8886
# the release signing key (RELEASE_SIGN_KEY). Up to two are accepted: the
@@ -166,13 +164,11 @@ sys.exit(1)
166164
Write-LogInfo 'GitHub CLI with attestation support not found — cannot check attestation'
167165
}
168166

169-
# Fail closed unless a strong proof succeeded or the user explicitly opts out.
167+
# Fail closed: a strong cryptographic proof is mandatory. A checksum alone is not
168+
# a trust anchor, and there is no opt-out — hardened environments require
169+
# verifiable supply-chain integrity at install time.
170170
if (-not $verified) {
171-
if ($env:PODUP_INSECURE_SKIP_VERIFY -eq '1') {
172-
Write-LogInfo 'PODUP_INSECURE_SKIP_VERIFY=1 — proceeding with checksum verification only'
173-
} else {
174-
Fail "No signature or attestation verifier available. Install 'gh' (>= 2.49) or python3 with the 'cryptography' package, set PODUP_RELEASE_PUBKEY_B64, or re-run with PODUP_INSECURE_SKIP_VERIFY=1 to accept checksum-only verification."
175-
}
171+
Fail "No signature or attestation verifier available. Install 'gh' (>= 2.49) or python3 with the 'cryptography' package, or set PODUP_RELEASE_PUBKEY_B64, then re-run."
176172
}
177173

178174
Write-LogInfo 'Verifying SHA-256 checksum ...'

install.sh

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -159,13 +159,27 @@ install_apt() {
159159
command -v dpkg >/dev/null 2>&1 || fail "--apt requires dpkg"
160160

161161
# Bootstrap the repository over HTTPS by installing the glyndor-archive-keyring
162-
# package, which registers the signing key and source list. This curl step has
163-
# the same trust as running this installer; from then on apt verifies every
164-
# package against the repository's GPG signature, and key renewals arrive
162+
# package, which registers the signing key and source list. The package is
163+
# verified against the embedded Ed25519 release key before it is installed, so
164+
# a tampered CDN or MITM cannot inject a rogue keyring. From then on apt verifies
165+
# every package against the repository's GPG signature, and key renewals arrive
165166
# automatically through apt upgrade.
166167
local kr="glyndor-archive-keyring.deb"
167168
log_info "Setting up the Glyndor apt repository ..."
168169
download "https://apt.glyndor.net/${kr}" "${TMP_DIR}/${kr}"
170+
download "https://apt.glyndor.net/${kr}.sig" "${TMP_DIR}/${kr}.sig"
171+
172+
# Fail closed: the keyring package is installed as root, so it must carry a
173+
# valid Ed25519 signature from the release key. No checksum-only fallback.
174+
log_info "Verifying keyring package signature ..."
175+
local kr_rc=0
176+
ed25519_verify "${TMP_DIR}/${kr}.sig" "${TMP_DIR}/${kr}" || kr_rc=$?
177+
case "$kr_rc" in
178+
0) log_ok "Keyring signature verified" ;;
179+
1) fail "Keyring signature verification failed — aborting (package may be tampered)" ;;
180+
2) fail "Cannot verify keyring signature: install python3 with the 'cryptography' package (and a configured release key) — aborting" ;;
181+
esac
182+
169183
run_root dpkg -i "${TMP_DIR}/${kr}"
170184
run_root apt-get update
171185
log_info "Installing podup ..."
@@ -187,8 +201,7 @@ install_binary() {
187201
# SHA256SUMS. The binary is trusted only after at least one cryptographic proof
188202
# tied to the release key or the repository's build identity succeeds — the
189203
# Ed25519 signature over SHA256SUMS, or the GitHub build-provenance attestation.
190-
# If neither verifier can run, the install fails closed. Set
191-
# PODUP_INSECURE_SKIP_VERIFY=1 to explicitly opt out (checksum only).
204+
# If neither verifier can run, the install fails closed.
192205
local verified=0 rc=0
193206

194207
log_info "Verifying SHA256SUMS signature ..."
@@ -217,15 +230,12 @@ install_binary() {
217230
log_info "GitHub CLI with attestation support not found — cannot check attestation"
218231
fi
219232

220-
# Fail closed unless a strong proof succeeded or the user explicitly opts out.
233+
# Fail closed: a strong cryptographic proof is mandatory. A checksum alone is
234+
# not a trust anchor, and there is no opt-out — government and hardened
235+
# environments require verifiable supply-chain integrity at install time.
221236
if [[ "$verified" -ne 1 ]]; then
222-
if [[ "${PODUP_INSECURE_SKIP_VERIFY:-0}" == "1" ]]; then
223-
log_info "PODUP_INSECURE_SKIP_VERIFY=1 — proceeding with checksum verification only"
224-
else
225-
fail "No signature or attestation verifier available. Install 'gh' (>= 2.49) \
226-
or python3 with the 'cryptography' package, set PODUP_RELEASE_PUBKEY_B64, or re-run \
227-
with PODUP_INSECURE_SKIP_VERIFY=1 to accept checksum-only verification."
228-
fi
237+
fail "No signature or attestation verifier available. Install 'gh' (>= 2.49) \
238+
or python3 with the 'cryptography' package, or set PODUP_RELEASE_PUBKEY_B64, then re-run."
229239
fi
230240

231241
verify_checksum "$ARTIFACT"

0 commit comments

Comments
 (0)