#!/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