Files
rollcall/deploy/rollcall-rack-update.sh
T
Ilker ManapandClaude Opus 5.5 703b8f1b7b
CI / agent (push) Successful in 2s
CI / site (push) Successful in 3s
CI / go (push) Successful in 36s
rack update: work directory on /var/lib (/run is noexec)
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>
2026-10-29 18:55:07 +01:00

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