feat(wake): W7 A10 idempotent installer + mosaic-wake.service (component-manifest, Gate-A, blank-reset retire, snapshot-guard, fail-closed install-validate) (#911)
Co-authored-by: jason.woltje <jason@diversecanvas.com> Co-committed-by: jason.woltje <jason@diversecanvas.com>
This commit was merged in pull request #911.
This commit is contained in:
@@ -271,6 +271,70 @@ echo "== B10: fresh beacon within SLO -> ALIVE (exit 0, no alarm) =="
|
||||
[ ! -s "$ALARM_OUT" ] || fail_msg "B10: a fresh beacon must NOT fire an alarm"
|
||||
) && ok
|
||||
|
||||
# ── W7 monitor-integration hardening (folded-in W6 observations, #910 review) ──
|
||||
|
||||
echo "== B11: staleness from monitor ingested_ts -> a far-future emit_ts STILL goes stale =="
|
||||
(
|
||||
H="$(fresh_home b11)"
|
||||
export ALARM_OUT="$H/alarm.json"
|
||||
export WAKE_BEACON_RECEIVED="$H/received.json"
|
||||
export WAKE_ALARM_SINK_CMD="$CAPTURE_ALARM"
|
||||
now="$(date +%s)"
|
||||
# A host ships a FAR-FUTURE emit_ts (now + 100000s) to try to defer staleness.
|
||||
# record stamps the monitor's own ingested_ts; check must use THAT, not emit_ts.
|
||||
jq -cn --argjson ts "$((now + 100000))" \
|
||||
'{kind:"wake-beacon", beacon_seq:9, emit_ts:$ts, host_id:"h", independence:"off-host", degraded:false}' \
|
||||
| "$BEACON" record
|
||||
# Simulate the monitor having received it 100s ago (ingested_ts backdated) while
|
||||
# the host's emit_ts stays far in the future. Under emit_ts-based staleness this
|
||||
# would read as ALIVE (age hugely negative); under receive-time it is STALE.
|
||||
jq -c --argjson ing "$((now - 100))" '.ingested_ts = $ing' "$WAKE_BEACON_RECEIVED" >"$H/tmp" && mv "$H/tmp" "$WAKE_BEACON_RECEIVED"
|
||||
out="$("$BEACON" check --slo-seconds 5 2>&1)"; rc=$?
|
||||
[ "$rc" -eq 1 ] || fail_msg "B11: a far-future emit_ts must NOT defer staleness — receive-time governs (expected absence exit 1); got $rc [$out]"
|
||||
echo "$out" | grep -qi 'ALARM FIRED' || fail_msg "B11: the receive-time-stale beacon must fire the absence alarm [$out]"
|
||||
jq -e '.age_seconds >= 100' "$ALARM_OUT" >/dev/null 2>&1 || fail_msg "B11: staleness age must be measured from ingested_ts (>=100s), not emit_ts [$(cat "$ALARM_OUT" 2>/dev/null)]"
|
||||
) && ok
|
||||
|
||||
echo "== B12: HMAC-verify at record -> a spoofed (bad-sig) beacon is REJECTED =="
|
||||
if ! command -v openssl >/dev/null 2>&1; then
|
||||
echo "SKIP: openssl not available" >&2
|
||||
else
|
||||
(
|
||||
H="$(fresh_home b12)"
|
||||
export WAKE_STATE_HOME="$H"
|
||||
export MOSAIC_CREDENTIALS_FILE="$H/credentials.json"
|
||||
export WAKE_BEACON_HMAC_KEY_NAME="beacon"
|
||||
jq -cn --arg k "test-beacon-hmac-key-do-not-echo" '{wake:{hmac_keys:{"beacon":$k}}}' >"$MOSAIC_CREDENTIALS_FILE"
|
||||
export WAKE_BEACON_INDEPENDENCE="off-host"
|
||||
# A capturing sink that keeps the exact shipped (signed) beacon record.
|
||||
SHIPPED="$H/shipped.json"
|
||||
SINK="$H/sink.sh"; printf '#!/usr/bin/env bash\ncat >"%s"\n' "$SHIPPED" >"$SINK"; chmod +x "$SINK"
|
||||
export WAKE_BEACON_SINK_CMD="$SINK"
|
||||
export WAKE_BEACON_RECEIVED="$H/received.json"
|
||||
"$BEACON" emit >/dev/null 2>&1 || fail_msg "B12: a signed emit must succeed with a by-name key"
|
||||
jq -e '.beacon_envelope' "$SHIPPED" >/dev/null 2>&1 || fail_msg "B12: a configured key must produce a signed beacon_envelope [$(cat "$SHIPPED" 2>/dev/null)]"
|
||||
# (a) the authentic signed beacon is ACCEPTED at record.
|
||||
"$BEACON" record <"$SHIPPED" >/dev/null 2>&1 || fail_msg "B12: an authentic signed beacon must be accepted at record"
|
||||
[ -s "$WAKE_BEACON_RECEIVED" ] || fail_msg "B12: the authentic beacon must be stored"
|
||||
# (b) a SPOOFED beacon (fields altered, envelope lifted) is REJECTED.
|
||||
rm -f "$WAKE_BEACON_RECEIVED"
|
||||
spoof="$H/spoof.json"
|
||||
jq -c '.beacon_seq = 99999 | .host_id = "attacker"' "$SHIPPED" >"$spoof"
|
||||
out="$("$BEACON" record <"$spoof" 2>&1)"; rc=$?
|
||||
[ "$rc" -ne 0 ] || fail_msg "B12: a spoofed beacon (altered fields) must be REJECTED at record; got rc=$rc [$out]"
|
||||
echo "$out" | grep -qi 'spoofed beacon' || fail_msg "B12: the rejection must name the spoof [$out]"
|
||||
[ ! -s "$WAKE_BEACON_RECEIVED" ] || fail_msg "B12: a rejected spoof must NOT be stored (dead-man clock not advanced)"
|
||||
# (c) an UNSIGNED beacon is rejected when signing is configured.
|
||||
unsigned="$H/unsigned.json"
|
||||
jq -c 'del(.beacon_envelope)' "$SHIPPED" >"$unsigned"
|
||||
out2="$("$BEACON" record <"$unsigned" 2>&1)"; rc2=$?
|
||||
[ "$rc2" -ne 0 ] || fail_msg "B12: an unsigned beacon must be REJECTED when signing is configured; got rc=$rc2 [$out2]"
|
||||
# beacon.sh must not inline the key material.
|
||||
grep -qF "test-beacon-hmac-key-do-not-echo" "$BEACON" && fail_msg "B12: beacon.sh must NOT inline the HMAC key"
|
||||
true
|
||||
) && ok
|
||||
fi
|
||||
|
||||
echo
|
||||
if [ -s "$FAILFILE" ]; then
|
||||
echo "wake beacon harness: FAILED ($(grep -c . "$FAILFILE") assertion(s))" >&2
|
||||
|
||||
Reference in New Issue
Block a user