LibrePortal/scripts/release/make_release.sh
librelad 5700f78c6b feat(release): minisign signature signing + verification
The sha256 only proves a download is intact; a compromised host could swap the
tarball + its checksum. Add minisign signatures, which prove authenticity (the host
can't forge them without the offline secret key). Ships INACTIVE behind a REPLACE_ME
placeholder, so installs work until a real key is generated; then it's REQUIRED.

- make_release.sh: signs the tarball when LP_MINISIGN_SECKEY is set -> <tarball>.minisig.
- libreportal.pub: the public key (placeholder), ships in the tarball and is installed
  to the ROOT-OWNED footprint (/usr/local/lib/libreportal/libreportal.pub) by init.sh
  -> the manager can't swap it to accept forged updates. footprint_version -> 2.
- install.sh: LP_MINISIGN_PUBKEY constant; once non-placeholder, downloads + verifies
  the .minisig (minisign -P) and REFUSES on invalid/missing (auto-installs minisign if
  needed). --no-verify-signature is a dev-only escape hatch.
- fetch.sh (update path): verifies against the footprint .pub (minisign -p), refuses on
  invalid/missing.
- docs/DEVELOPMENT.md: keygen (minisign -G), paste pubkey into libreportal.pub +
  install.sh, keep the secret key offline, sign builds via LP_MINISIGN_SECKEY, bump
  footprint_version on key rotation.

Verified end-to-end with a real throwaway key: good signature accepted; tampered,
wrong-key, and missing-signature all refused; placeholder skips (sha256 still enforced).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 19:40:30 +01:00

72 lines
2.8 KiB
Bash
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
#
# Build a versioned, checksum-verified LibrePortal release artifact.
#
# The stable install fetches a release tarball over plain HTTPS (no git, no auth),
# so it's reproducible and version-pinned. This builds that artifact from the
# COMMITTED tree via `git archive`, which honours the `export-ignore` rules in
# .gitattributes — so dev-only trees (scripts/unused, site, .claude, …) never ship.
#
# No infrastructure needed: output lands in dist/<channel>/ laid out exactly like
# the hosting will serve it, so you can point an install at it with:
# ( cd dist && python3 -m http.server 8000 )
# LP_RELEASE_BASE_URL=http://localhost:8000 ./install.sh ...
#
# Usage: scripts/release/make_release.sh [channel] [git-ref]
# channel stable (default) | edge
# git-ref HEAD (default) | a tag/commit to build from
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$REPO_ROOT"
CHANNEL="${1:-stable}"
REF="${2:-HEAD}"
VERSION="$(tr -d ' \t\n\r' < VERSION 2>/dev/null || true)"
[[ -n "$VERSION" ]] || { echo "make_release: VERSION file is empty or missing" >&2; exit 1; }
# Root-owned-footprint version (helpers/wrapper/unit/sudoers). Published in the
# manifest so the updater can detect when an update needs a root re-install.
FOOTPRINT_VERSION="$(grep -oE '^footprint_version=[0-9]+' init.sh | head -1 | cut -d= -f2)"
[[ -n "$FOOTPRINT_VERSION" ]] || FOOTPRINT_VERSION=0
TARBALL="libreportal-${VERSION}.tar.gz"
PREFIX="libreportal-${VERSION}/" # top-level dir inside the tarball
OUT="$REPO_ROOT/dist/$CHANNEL"
mkdir -p "$OUT"
echo "Building $TARBALL (channel=$CHANNEL, ref=$REF) ..."
git archive --format=tar.gz --prefix="$PREFIX" -o "$OUT/$TARBALL" "$REF"
( cd "$OUT" && sha256sum "$TARBALL" > "$TARBALL.sha256" )
SHA="$(cut -d' ' -f1 < "$OUT/$TARBALL.sha256")"
cat > "$OUT/latest.json" <<EOF
{
"version": "$VERSION",
"channel": "$CHANNEL",
"url": "$TARBALL",
"sha256": "$SHA",
"footprint_version": $FOOTPRINT_VERSION,
"notes": ""
}
EOF
# Sign the tarball with minisign if a secret key is configured. Keep that key
# OFFLINE — set LP_MINISIGN_SECKEY to its path on the release machine only. The
# public half lives in libreportal.pub + install.sh; verification activates once
# you replace their REPLACE_ME placeholder. Produces <tarball>.minisig.
if [[ -n "${LP_MINISIGN_SECKEY:-}" ]]; then
command -v minisign >/dev/null 2>&1 || { echo "make_release: LP_MINISIGN_SECKEY set but 'minisign' isn't installed" >&2; exit 1; }
minisign -Sm "$OUT/$TARBALL" -s "$LP_MINISIGN_SECKEY" -t "libreportal $VERSION ($CHANNEL)" >/dev/null
SIGNED="$OUT/$TARBALL.minisig (signed)"
else
SIGNED=" unsigned (set LP_MINISIGN_SECKEY to sign)"
fi
echo "$OUT/$TARBALL"
echo "$OUT/$TARBALL.sha256 ($SHA)"
echo "$OUT/latest.json"
echo "$SIGNED"