Clear the onboard phase flags on offboard, and let the mirror signal the owner before revoking the key it signals with
This commit is contained in:
@@ -37,7 +37,8 @@
|
|||||||
# Step 5: Disarm sync gates — same four gates as the owner path
|
# Step 5: Disarm sync gates — same four gates as the owner path
|
||||||
# Step 6: Revoke Emby admin — remove own admin account from local Emby instance
|
# Step 6: Revoke Emby admin — remove own admin account from local Emby instance
|
||||||
# Step 7: Restart own stack — bring up own parked containers
|
# Step 7: Restart own stack — bring up own parked containers
|
||||||
# Step 8: SSH revocation — revoke keys both directions, write state, signal owner
|
# Step 8: Finalise — write INACTIVE, clear phase flags, signal owner, THEN revoke
|
||||||
|
# keys. Revocation is last because the signal needs the key
|
||||||
#
|
#
|
||||||
# ==============================================================================================
|
# ==============================================================================================
|
||||||
# DESIGN PRINCIPLES
|
# DESIGN PRINCIPLES
|
||||||
@@ -406,19 +407,31 @@ if [[ "$AM_MIRROR" == true ]]; then
|
|||||||
echo ""
|
echo ""
|
||||||
echo "━━━ $ICON_SHIELD Step 8/8 — SSH Revocation + State ━━━"
|
echo "━━━ $ICON_SHIELD Step 8/8 — SSH Revocation + State ━━━"
|
||||||
|
|
||||||
do_ssh_key_revocation "${OWNER_IP:-}"
|
# State first, keys last — same ordering the owner path needed. Revocation used to run here,
|
||||||
|
# before the push below, so the mirror destroyed the key and then tried to tell the owner it
|
||||||
|
# had left using that key. The owner never heard, and the notify promised it would "finalise
|
||||||
|
# on next check" — a check that now had no way in.
|
||||||
NOW=$(date '+%Y-%m-%d %H:%M:%S')
|
NOW=$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
if [[ "$DRY_RUN" == false ]]; then
|
if [[ "$DRY_RUN" == false ]]; then
|
||||||
write_state_file "$LOCAL_STATE_FILE" \
|
write_state_file "$LOCAL_STATE_FILE" \
|
||||||
"INACTIVE" "" "$NOW" "$LOCAL_SERVER_NAME" "$REASON"
|
"INACTIVE" "" "$NOW" "$LOCAL_SERVER_NAME" "$REASON"
|
||||||
echo "Local state: INACTIVE ✅"
|
echo "Local state: INACTIVE ✅"
|
||||||
add_to_blocklist "$OWNER" "$REASON"
|
add_to_blocklist "$OWNER" "$REASON"
|
||||||
|
|
||||||
|
# Inverse of onboard's write_onboard_phase. MIRROR_ID is this host on this path, and the
|
||||||
|
# flags are named for the mirror on both sides, so the same names clear here.
|
||||||
|
_setup_db="$(platform_setup_db_path)"
|
||||||
|
for _flag in "${MIRROR_ID}_PHASE1_DONE" "${MIRROR_ID}_PHASE2_DONE" "${MIRROR_ID}_KEY_READY"; do
|
||||||
|
clear_state_var "$_setup_db" "$_flag"
|
||||||
|
done
|
||||||
|
echo "Onboard phase flags cleared ✅"
|
||||||
|
unset _setup_db _flag
|
||||||
else
|
else
|
||||||
warn "DRY RUN — would write INACTIVE state and blocklist $OWNER"
|
warn "DRY RUN — would write INACTIVE state, clear phase flags and blocklist $OWNER"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$OWNER_REACHABLE" == true ]]; then
|
if [[ "$OWNER_REACHABLE" == true ]]; then
|
||||||
|
platform_push_setup_state 2>/dev/null || warn "Could not push cleared setup state to $OWNER"
|
||||||
push_state_to_remote "$LOCAL_STATE_FILE" "$OWNER_IP" "$OWNER_SSH_KEY"
|
push_state_to_remote "$LOCAL_STATE_FILE" "$OWNER_IP" "$OWNER_SSH_KEY"
|
||||||
notify "Partnership offboard requested by $MIRROR — $OWNER will finalise on next check" \
|
notify "Partnership offboard requested by $MIRROR — $OWNER will finalise on next check" \
|
||||||
"Partnership" "normal"
|
"Partnership" "normal"
|
||||||
@@ -426,6 +439,9 @@ if [[ "$AM_MIRROR" == true ]]; then
|
|||||||
warn "$OWNER unreachable — state written locally, owner will see it when reachable"
|
warn "$OWNER unreachable — state written locally, owner will see it when reachable"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Last, for the reason above: everything before it needs the key.
|
||||||
|
do_ssh_key_revocation "${OWNER_IP:-}"
|
||||||
|
|
||||||
# ── Summary ───────────────────────────────────────────────────────────────────────────────
|
# ── Summary ───────────────────────────────────────────────────────────────────────────────
|
||||||
END=$(date +%s)
|
END=$(date +%s)
|
||||||
echo ""
|
echo ""
|
||||||
@@ -605,7 +621,26 @@ if [[ "$DRY_RUN" == false ]]; then
|
|||||||
STEP_STATE_WRITE_OK=false
|
STEP_STATE_WRITE_OK=false
|
||||||
fi
|
fi
|
||||||
add_to_blocklist "$MIRROR" "$REASON"
|
add_to_blocklist "$MIRROR" "$REASON"
|
||||||
|
|
||||||
|
# The inverse of onboard's write_onboard_phase, which had none. Without this a completed
|
||||||
|
# offboard leaves the state file reading INACTIVE beside HOST*_PHASE1_DONE / _PHASE2_DONE
|
||||||
|
# still set — and the setup checklist and partnership card read the flags, not the state
|
||||||
|
# file, so a torn-down partnership went on presenting itself as fully onboarded.
|
||||||
|
#
|
||||||
|
# KEY_READY goes too: it means "a key is generated and waiting to be installed", which stops
|
||||||
|
# being true the moment Step 11 revokes both sides.
|
||||||
|
_setup_db="$(platform_setup_db_path)"
|
||||||
|
for _flag in "${MIRROR_ID}_PHASE1_DONE" "${MIRROR_ID}_PHASE2_DONE" "${MIRROR_ID}_KEY_READY"; do
|
||||||
|
clear_state_var "$_setup_db" "$_flag"
|
||||||
|
done
|
||||||
|
echo "Onboard phase flags cleared ✅"
|
||||||
|
unset _setup_db _flag
|
||||||
|
|
||||||
if [[ "$MIRROR_REACHABLE" == true ]]; then
|
if [[ "$MIRROR_REACHABLE" == true ]]; then
|
||||||
|
# Pushed after the flags are cleared, so the mirror receives the cleared file rather than
|
||||||
|
# the version that still claimed a finished onboard.
|
||||||
|
platform_push_setup_state 2>/dev/null || \
|
||||||
|
warn "Could not push cleared setup state to $MIRROR"
|
||||||
if push_state_to_remote "$LOCAL_STATE_FILE" "$MIRROR_IP" "$MIRROR_SSH_KEY"; then
|
if push_state_to_remote "$LOCAL_STATE_FILE" "$MIRROR_IP" "$MIRROR_SSH_KEY"; then
|
||||||
STEP_STATE_PUSH_OK=true
|
STEP_STATE_PUSH_OK=true
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -996,6 +996,22 @@ set_state_var() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Removes a key from a flat state file. The inverse of set_state_var, and the reason it exists:
|
||||||
|
# onboarding writes HOST*_PHASE*_DONE and nothing took them away again, so a completed offboard
|
||||||
|
# left a state file reading INACTIVE beside flags still claiming a finished onboard. Anything
|
||||||
|
# reading the flags — the setup checklist, the partnership card — believed the torn-down
|
||||||
|
# partnership was live.
|
||||||
|
#
|
||||||
|
# Absent key is success, not an error: clearing twice is how a re-run after a partial teardown
|
||||||
|
# should behave.
|
||||||
|
# Usage: clear_state_var "$state_file" "$key"
|
||||||
|
clear_state_var() {
|
||||||
|
local file="$1" key="$2"
|
||||||
|
[[ -f "$file" ]] || return 0
|
||||||
|
grep -q "^${key}=" "$file" 2>/dev/null || return 0
|
||||||
|
sed -i "/^${key}=/d" "$file"
|
||||||
|
}
|
||||||
|
|
||||||
# ==============================================================================================
|
# ==============================================================================================
|
||||||
# ── CONNECTIVITY CHECKS ───────────────────────────────────────────────────────────────────────
|
# ── CONNECTIVITY CHECKS ───────────────────────────────────────────────────────────────────────
|
||||||
# ==============================================================================================
|
# ==============================================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user