Build every template in smoke-gate order, index the full repository and sign packages plus metadata with the RSA4096 key, in the pinned Void container via podman. Fail-closed without a key: no key file, no repo. Ticket #29 (needs #27, #28).
130 lines
4.7 KiB
Bash
Executable File
130 lines
4.7 KiB
Bash
Executable File
#!/bin/sh
|
|
# Repository half of packaging/void/repo.sh (ticket #29). Runs inside the
|
|
# pinned Void container as root: system setup, the void-packages checkout
|
|
# at the pinned commit, then every template built in smoke-gate order as
|
|
# the unprivileged builder user (xbps-src refuses root), then index, sign,
|
|
# verify and stage as root again.
|
|
#
|
|
# Not executed directly: repo.sh mounts this file and runs it. VOID_COMMIT,
|
|
# PKGS and SIGNEDBY arrive via the environment; the RSA4096 private key is
|
|
# mounted read-only at /run/xbps-sign-key.pem (repo.sh validated it before
|
|
# any container ran).
|
|
#
|
|
# Keeping this a real file (instead of an inline `sh -c` string) means
|
|
# normal quoting, heredocs, and shellcheck apply.
|
|
set -eu
|
|
|
|
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
|
|
KEY=/run/xbps-sign-key.pem
|
|
OUT=/mnt/out
|
|
REPO="$OUT/repo"
|
|
KEYS="$OUT/keys"
|
|
|
|
[ -r "$KEY" ] || {
|
|
echo "container-repo.sh: signing key not mounted at $KEY" >&2
|
|
exit 1
|
|
}
|
|
|
|
xbps-install -Suy xbps >/dev/null
|
|
xbps-install -y git base-devel xtools binutils zstd openssl >/dev/null
|
|
git clone --quiet --depth 1 https://github.com/void-linux/void-packages /void-packages
|
|
git -C /void-packages fetch --quiet --depth 1 origin "$VOID_COMMIT"
|
|
git -C /void-packages checkout --quiet "$VOID_COMMIT"
|
|
cp -r /overlay/. /void-packages/srcpkgs/
|
|
id builder >/dev/null 2>&1 || useradd -m builder
|
|
chown builder:builder /void-packages
|
|
|
|
echo "== build every template in order: $PKGS"
|
|
cd /void-packages
|
|
# shellcheck disable=SC2086
|
|
for PKG in $PKGS; do
|
|
echo "== xlint $PKG"
|
|
su -s /bin/sh builder -c "cd /void-packages && xlint 'srcpkgs/$PKG/template'"
|
|
echo "== xbps-src pkg $PKG"
|
|
su -s /bin/sh builder -c "cd /void-packages && ./xbps-src pkg '$PKG'"
|
|
done
|
|
|
|
echo "== collect built packages"
|
|
mkdir -p "$REPO" "$KEYS"
|
|
# shellcheck disable=SC2086
|
|
for PKG in $PKGS; do
|
|
PKGFILE=$(ls /void-packages/hostdir/binpkgs/"$PKG"-*.x86_64.xbps 2>/dev/null | head -n1)
|
|
[ -n "$PKGFILE" ] || {
|
|
echo "FAIL: $PKG (built package missing from hostdir/binpkgs)" >&2
|
|
exit 1
|
|
}
|
|
echo "collected: $(basename "$PKGFILE")"
|
|
cp "$PKGFILE" "$REPO/"
|
|
done
|
|
|
|
echo "== index the full repository"
|
|
# shellcheck disable=SC2086
|
|
xbps-rindex -a "$REPO"/*.x86_64.xbps
|
|
|
|
echo "== sign every binary package"
|
|
xbps-rindex -S --privkey "$KEY" --signedby "$SIGNEDBY" "$REPO"/*.x86_64.xbps
|
|
|
|
echo "== sign the repository metadata"
|
|
xbps-rindex -s --privkey "$KEY" --signedby "$SIGNEDBY" "$REPO"
|
|
|
|
echo "== prove an unsigned install is refused"
|
|
set +e
|
|
PROBE=$(xbps-install -r /tmp/keyprobe --repository="$REPO" -Sy voculinux </dev/null 2>&1)
|
|
PROBE_RC=$?
|
|
set -e
|
|
if [ "$PROBE_RC" -eq 0 ] || ! printf '%s' "$PROBE" | grep -q "RSA signed"; then
|
|
echo "FAIL: unsigned install was not refused — signatures are not enforced" >&2
|
|
printf '%s\n' "$PROBE" >&2
|
|
exit 1
|
|
fi
|
|
echo "unsigned install refused as required"
|
|
|
|
FP=$(printf '%s' "$PROBE" | grep -i Fingerprint | awk '{print $2}' | head -n1)
|
|
[ -n "$FP" ] || {
|
|
echo "FAIL: could not read the key fingerprint from the refusal" >&2
|
|
exit 1
|
|
}
|
|
echo "key fingerprint: $FP"
|
|
|
|
echo "== stage the client key"
|
|
openssl rsa -in "$KEY" -pubout -out /tmp/repo-pub.pem 2>/dev/null
|
|
PUB64=$(base64 -w0 /tmp/repo-pub.pem)
|
|
# XML-escape the signer identity: xbps writes </> itself on import
|
|
# and rejects a plist carrying raw angle brackets.
|
|
ESCAPED_SIGNEDBY=$(printf '%s' "$SIGNEDBY" | sed -e 's/&/\&/g' -e 's/</\</g' -e 's/>/\>/g')
|
|
{
|
|
printf '%s\n' '<?xml version="1.0" encoding="UTF-8"?>'
|
|
printf '%s\n' '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">'
|
|
printf '%s\n' '<plist version="1.0">' '<dict>' ' <key>public-key</key>'
|
|
printf ' <data>%s</data>\n' "$PUB64"
|
|
printf '%s\n' ' <key>public-key-size</key>' ' <integer>4096</integer>' ' <key>signature-by</key>'
|
|
printf ' <string>%s</string>\n' "$ESCAPED_SIGNEDBY"
|
|
printf '%s\n' '</dict>' '</plist>'
|
|
} > "$KEYS/$FP.plist"
|
|
{
|
|
printf 'signed-by: %s\n' "$SIGNEDBY"
|
|
printf 'fingerprint: %s\n' "$FP"
|
|
} > "$KEYS/FINGERPRINT"
|
|
echo "staged: keys/$FP.plist"
|
|
|
|
echo "== verify: install the app from the signed repo in a clean rootdir"
|
|
VERIFY=/tmp/verify-install
|
|
mkdir -p "$VERIFY/var/db/xbps/keys"
|
|
# The app's full dependency tree resolves from the official remote, so the
|
|
# verify rootdir trusts the official keys too; our repo stays enforced
|
|
# because its packages verify only against the staged key below.
|
|
cp /var/db/xbps/keys/*.plist "$VERIFY/var/db/xbps/keys/"
|
|
cp "$KEYS/$FP.plist" "$VERIFY/var/db/xbps/keys/"
|
|
xbps-install -y -r "$VERIFY" \
|
|
--repository="$REPO" \
|
|
--repository=https://repo-default.voidlinux.org/current \
|
|
-Sy voculinux
|
|
xbps-query -r "$VERIFY" -l | grep -q "^ii voculinux-" || {
|
|
echo "FAIL: voculinux not installed in the verify rootdir" >&2
|
|
exit 1
|
|
}
|
|
echo "verified: voculinux installs from the signed repository"
|
|
|
|
ls "$REPO"
|
|
echo "SIGNED REPO OK: fingerprint $FP"
|