@@ -277,7 +277,7 @@ jobs:
277277 echo "rule_id=$RULE_ID" >> "$GITHUB_OUTPUT"
278278 echo "::notice::SG rule $RULE_ID added (22/tcp from $MY_IP/32)"
279279
280- - name : SCP artifact + SSH-driven binary swap
280+ - name : SCP artifact + SSH-driven binary swap (verify PID + SHA256 changed)
281281 env :
282282 IP : ${{ steps.ec2.outputs.runner_ip }}
283283 KEY : ${{ steps.keypush.outputs.key_path }}
@@ -286,20 +286,65 @@ jobs:
286286 ARCHIVE=$(ls /tmp/runner-artifact/boxlite-runner-*-linux-amd64.tar.gz | head -1)
287287 [ -n "$ARCHIVE" ] || { echo "::error::No runner artifact found"; exit 1; }
288288
289+ # Compute expected binary sha256 from the artifact tarball — the
290+ # remote `sha256sum /usr/local/bin/boxlite-runner` after swap must
291+ # match this, otherwise the file on disk isn't what we shipped.
292+ EXPECTED_SHA=$(tar -xzOf "$ARCHIVE" boxlite-runner 2>/dev/null | sha256sum | awk '{print $1}') \
293+ || EXPECTED_SHA=$(tar -tzf "$ARCHIVE" | grep -E 'boxlite-runner$' | head -1 \
294+ | xargs -I{} sh -c "tar -xzOf '$ARCHIVE' {} | sha256sum | awk '{print \$1}'")
295+ [ -n "$EXPECTED_SHA" ] || { echo "::error::Could not compute expected sha256 from $ARCHIVE"; exit 1; }
296+ echo "::notice::expected sha256: $EXPECTED_SHA"
297+
289298 SSH_OPTS="-i $KEY -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=15"
290299
291300 # SCP the tarball
292301 scp $SSH_OPTS "$ARCHIVE" "ubuntu@${IP}:/tmp/boxlite-runner.tar.gz"
293302
294- # In-place swap + restart + smoke-check
295- ssh $SSH_OPTS "ubuntu@${IP}" 'bash -s' <<'REMOTE'
303+ # In-place swap + restart + verification. The remote script
304+ # captures MainPID + ActiveEnterTimestamp BEFORE the swap, then
305+ # asserts after restart: (a) new MainPID != old MainPID
306+ # (proves a real process replacement, not the old one still
307+ # running), (b) ActiveEnterTimestampMonotonic strictly
308+ # advanced (proves systemd marked it active after our start),
309+ # and (c) /usr/local/bin/boxlite-runner sha256 matches
310+ # EXPECTED_SHA passed from the GHA runner.
311+ ssh $SSH_OPTS "ubuntu@${IP}" "EXPECTED_SHA='$EXPECTED_SHA' bash -s" <<'REMOTE'
296312 set -euxo pipefail
313+
314+ BEFORE_PID=$(systemctl show -p MainPID --value boxlite-runner 2>/dev/null || echo 0)
315+ BEFORE_TS=$(systemctl show -p ActiveEnterTimestampMonotonic --value boxlite-runner 2>/dev/null || echo 0)
316+ echo "BEFORE: MainPID=$BEFORE_PID ActiveEnterTimestampMonotonic=$BEFORE_TS"
317+
297318 sudo systemctl stop boxlite-runner
298319 sudo tar xzf /tmp/boxlite-runner.tar.gz -C /usr/local/bin/
299320 sudo chmod +x /usr/local/bin/boxlite-runner
300321 sudo systemctl start boxlite-runner
301322 sleep 5
302323 sudo systemctl is-active --quiet boxlite-runner || { sudo journalctl -u boxlite-runner -n 50 --no-pager; exit 1; }
324+
325+ AFTER_PID=$(systemctl show -p MainPID --value boxlite-runner)
326+ AFTER_TS=$(systemctl show -p ActiveEnterTimestampMonotonic --value boxlite-runner)
327+ INSTALLED_SHA=$(sudo sha256sum /usr/local/bin/boxlite-runner | awk '{print $1}')
328+ echo "AFTER: MainPID=$AFTER_PID ActiveEnterTimestampMonotonic=$AFTER_TS"
329+ echo "AFTER: sha256=$INSTALLED_SHA"
330+ echo "EXPECT: sha256=$EXPECTED_SHA"
331+
332+ # (a) PID must have changed (proves real restart)
333+ if [ "$AFTER_PID" = "$BEFORE_PID" ] && [ "$BEFORE_PID" != "0" ]; then
334+ echo "::error::MainPID did not change ($AFTER_PID) — service did not actually restart"
335+ exit 1
336+ fi
337+ # (b) systemd's monotonic timestamp for last active-enter must have advanced
338+ if [ "$AFTER_TS" -le "$BEFORE_TS" ]; then
339+ echo "::error::ActiveEnterTimestampMonotonic did not advance ($BEFORE_TS -> $AFTER_TS)"
340+ exit 1
341+ fi
342+ # (c) installed binary sha256 must match the artifact we uploaded
343+ if [ "$INSTALLED_SHA" != "$EXPECTED_SHA" ]; then
344+ echo "::error::Installed binary sha256 mismatch — got $INSTALLED_SHA expected $EXPECTED_SHA"
345+ exit 1
346+ fi
347+ echo "Verified: PID swap + monotonic ts advance + sha256 match"
303348 /usr/local/bin/boxlite-runner --version 2>&1 || true
304349 REMOTE
305350 echo "::notice::Runner binary swap succeeded"
0 commit comments