hub v0.66.0 + ISO v1.20.0: customer self-bind (R-27 slice 1)
Let a customer bind their own freshly-installed appliance without the operator: operator "Send self-bind link" mints a 7-day tokenized capability link, emailed (Hungarian, sibling sender) to the customer, who opens a public /bind/<token> page and proves two factors — the console pairing code shown on the box screen + their retrieval passphrase — and the hub stages the bind via the same BindAppliance (provenance customer_selfbind). The box's ~30s appliance poll delivers. Viktor's three rulings verbatim: console pairing code (no appliance list ever rendered), operator-sent tokenized link, 5-attempt lockout -> "call support". Wrong code == wrong passphrase (one generic failure, no oracle, both factors compared unconditionally); expiry falls back to operator-bind unchanged. THE TRAP: one public prefix /bind/, exempt from auth+CSRF at both /login gate sites via a single isPublicBindPath predicate (tight trailing-slash match; ServeMux ..-cleans; handler rejects '/' in token). 9 tests (Scenarios A-F + F1/F2); 4 red-proofs verified red-then-green (lockout, oracle, widened-prefix, single-active). GC verdict: no appliance GC -> the 7-day TTL stands alone. Controller/agent untouched; R-27b deferred. Green: full hub build/vet/test (17 ok) + bash -n + hub confirm gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017qDiBqKKQ5vPB5fXBqu7Kp
This commit is contained in:
@@ -1,5 +1,17 @@
|
||||
# Felhom scripts — Changelog
|
||||
|
||||
## build-felhom-iso.sh v1.20.0 — console pairing-code banner for customer self-bind (R-27 slice 1) (2026-07-17)
|
||||
|
||||
Supports the hub's customer self-bind flow (hub v0.66.0). In PAIRING mode, `felhom-bootstrap.sh` now
|
||||
reads the additive `pairing_code` from the `POST /api/v1/appliance/register` response, persists it at
|
||||
`/etc/felhom/appliance-pairing-code`, and prints a Hungarian **console banner** (to `/dev/console`,
|
||||
stdout fallback) each pairing cycle so the customer can read the code off the physical screen and type
|
||||
it — together with their retrieval passphrase — on the hub's public `/bind/<token>` page. The code is
|
||||
**non-secret** (possession proof only; the passphrase is the second factor), so it is safe on the
|
||||
console. **Graceful degradation both ways:** a hub older than v0.66.0 omits `pairing_code` → the banner
|
||||
prints nothing and register/poll are unchanged; an old ISO against a v0.66.0 hub simply ignores the new
|
||||
field. No change to DIRECT mode. Green: `bash -n` clean on both scripts.
|
||||
|
||||
## build-felhom-iso.sh v1.19.0 — the universal secret-free ISO: `--pairing` mode (R-21 slice C) (2026-07-17)
|
||||
|
||||
The scripts half of the universal ISO. `felhom-bootstrap.sh` gains a PAIRING mode — **one unit, two
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#===============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
ISO_VERSION="1.19.0" # Felhom release the ISO is tagged to (aligns with felhom-host-install SCRIPT_VERSION).
|
||||
ISO_VERSION="1.20.0" # Felhom release the ISO is tagged to (aligns with felhom-host-install SCRIPT_VERSION).
|
||||
IMAGE="${FELHOM_ISO_ASSISTANT_IMAGE:-felhom-iso-assistant:trixie}"
|
||||
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
|
||||
@@ -33,9 +33,26 @@ STATE_FILE=/var/lib/felhom-install/state.json
|
||||
PASS_FILE=/run/felhom-bootstrap-pass
|
||||
SCRIPT_TMP=/run/felhom-host-install.sh
|
||||
TOKEN_FILE=/etc/felhom/appliance-token # PAIRING: the box's only pre-day-0 credential (0600, persists reboots)
|
||||
PAIRING_CODE_FILE=/etc/felhom/appliance-pairing-code # R-27: non-secret pairing code shown on the console
|
||||
|
||||
log() { echo "felhom-bootstrap: $*"; }
|
||||
|
||||
# print_pairing_banner (R-27, v0.66.0) — show the pairing code prominently on the physical console while
|
||||
# the box waits to be bound, so the customer can read it into the self-bind page. Non-secret (the bind
|
||||
# still requires the customer's retrieval passphrase). A hub older than v0.66.0 sends no code → no banner
|
||||
# (the box stays operator-bind-only — graceful, no behavior change).
|
||||
print_pairing_banner() {
|
||||
local code; code=$(cat "$PAIRING_CODE_FILE" 2>/dev/null)
|
||||
[[ -n "$code" ]] || return 0
|
||||
{ printf '\n================================================\n'
|
||||
printf ' Felhom — a doboz parositasra var / párosításra vár\n\n'
|
||||
printf ' Párosító kód: %s\n\n' "$code"
|
||||
printf ' Nyisd meg az e-mailben kapott self-bind linket,\n'
|
||||
printf ' és add meg ezt a kódot + a jelszavadat.\n'
|
||||
printf '================================================\n\n'
|
||||
} > /dev/console 2>/dev/null || printf 'Párosító kód: %s\n' "$code"
|
||||
}
|
||||
|
||||
cleanup_pass() { [[ -e "$PASS_FILE" ]] && { shred -u "$PASS_FILE" 2>/dev/null || rm -f "$PASS_FILE"; }; return 0; }
|
||||
trap cleanup_pass EXIT
|
||||
|
||||
@@ -175,8 +192,14 @@ run_pairing() {
|
||||
exit 1
|
||||
fi
|
||||
( umask 077; printf '%s' "$token" > "$TOKEN_FILE" )
|
||||
log "registered — appliance token stored (0600); waiting for the operator to bind this box"
|
||||
# R-27 (v0.66.0): persist the non-secret pairing code (absent on a pre-v0.66.0 hub — tolerated).
|
||||
local pcode; pcode=$(printf '%s' "$resp" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("pairing_code",""))' 2>/dev/null)
|
||||
if [[ -n "$pcode" ]]; then
|
||||
printf '%s' "$pcode" > "$PAIRING_CODE_FILE"
|
||||
fi
|
||||
log "registered — appliance token stored (0600); waiting for the operator or a customer self-bind"
|
||||
fi
|
||||
print_pairing_banner # show the code on the console each pairing cycle
|
||||
|
||||
# 2. ONE poll. RestartSec=30 is the poll interval.
|
||||
local token; token=$(cat "$TOKEN_FILE")
|
||||
|
||||
Reference in New Issue
Block a user