Found by the first signed update on pxe-rack2: the signature verified, then the copy in /run could not be executed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
47 lines
2.5 KiB
Bash
Executable File
47 lines
2.5 KiB
Bash
Executable File
#!/bin/sh
|
|
# Installs a rollcall binary the rack controller staged (rollcall-rack-update.path).
|
|
#
|
|
# The staging directory belongs to the rack's unprivileged user, so nothing in
|
|
# it is trusted: the binary is copied into a directory of root's and installed
|
|
# only if the region's signature over it verifies against the CA certificate
|
|
# written at join (root's, from rack.json ca_file). Then the running binary is
|
|
# kept as .prev, the rack restarted, and the previous binary put back if the
|
|
# rack does not register with the region in 60 s.
|
|
set -u
|
|
D=/var/lib/rollcall/update
|
|
BIN=/usr/local/bin/rollcall
|
|
[ -f "$D/ready" ] || exit 0
|
|
want=$(head -c 64 "$D/ready" | tr -cd 'A-Za-z0-9._+-'); rm -f "$D/ready"
|
|
# root's work directory, on a file system that allows executing (/run is noexec)
|
|
install -d -m 0700 -o root -g root /var/lib/rollcall-update
|
|
W=$(mktemp -d /var/lib/rollcall-update/w.XXXXXX) || exit 1
|
|
trap 'rm -rf "$W"' EXIT
|
|
# last-result is read by the rack (backoff); renamed in, so a link planted there is replaced, not followed
|
|
result() { echo "$(date -Is) $*" > "$W/result" && mv -fT "$W/result" "$D/last-result"; echo "$*"; }
|
|
CA=$(jq -r '.ca_file // "/etc/rollcall/rack/ca.pem"' /etc/rollcall/rack.json 2>/dev/null)
|
|
[ -n "$CA" ] && [ "$(stat -c %u "$CA" 2>/dev/null)" = 0 ] || { result "$want: no root-owned CA certificate ($CA), not installed"; exit 1; }
|
|
cp -- "$D/rollcall.new" "$W/bin" && cp -- "$D/rollcall.new.sig" "$W/sig" || { result "$want: staged files missing, not installed"; exit 1; }
|
|
openssl x509 -in "$CA" -pubkey -noout > "$W/pub" 2>/dev/null &&
|
|
openssl dgst -sha256 -verify "$W/pub" -signature "$W/sig" "$W/bin" >/dev/null 2>&1 ||
|
|
{ result "$want: signature does not verify against $CA, not installed"; exit 1; }
|
|
chmod 0755 "$W/bin"
|
|
got=$("$W/bin" version 2>/dev/null | awk '{print $2}')
|
|
[ "$got" = "$want" ] || { result "$want: binary reports '$got', not installed"; exit 1; }
|
|
cur=$("$BIN" version 2>/dev/null | awk '{print $2}')
|
|
cp -p "$BIN" "$BIN.prev"
|
|
install -m 0755 "$W/bin" "$BIN.tmp" && mv -f "$BIN.tmp" "$BIN"
|
|
rm -f "$D/rollcall.new" "$D/rollcall.new.sha256" "$D/rollcall.new.sig"
|
|
since=$(date +%s)
|
|
systemctl restart rollcall-rack.service
|
|
for _ in $(seq 1 30); do
|
|
sleep 2
|
|
if journalctl -u rollcall-rack.service --since "@$since" -o cat 2>/dev/null | grep -q "^.*registered with"; then
|
|
result "updated $cur -> $want"
|
|
exit 0
|
|
fi
|
|
done
|
|
cp -p "$BIN.prev" "$BIN.tmp" && mv -f "$BIN.tmp" "$BIN"
|
|
systemctl restart rollcall-rack.service
|
|
result "$want did not register within 60s; rolled back to $cur"
|
|
exit 1
|