hub v0.62.0 + scripts v1.19.0 — R-21 slice C: the universal secret-free ISO
A generic ISO carries NO customer secret. The box registers itself at the hub as an unclaimed appliance; the operator binds it to a customer; the hub delivers the customer-id + retrieval passphrase ONCE; day-0 completes via the slice-A path. Hub (v0.62.0): - store/appliance.go: appliance_registrations keyed by (uuid, mac_set) — MAC set is the tiebreaker (duplicate SMBIOS UUIDs); token stored as sha256 only. Idempotent register (sticky-discard), atomic one-shot delivery, bind/discard. - api/appliance.go: POST /appliance/register (the one unauth endpoint, per-IP rate-limited, 256-bit token); GET /appliance/poll (404 no-oracle / 204 unbound / 200 deliver-once / 410 delivered). Passphrase read live, never logged. - web/appliances.go: Hosts-page "Unclaimed appliances" section + BIND (customer picker, host count display-only) + DISCARD; SSH host-key fingerprints; events. - Red-proofs: one-shot delivery + register idempotency (both proven red); 404-no-oracle, sticky-discard, bind staging, render. Green + confirm gate. Scripts (v1.19.0): - felhom-bootstrap.sh: ONE unit, TWO modes. Direct (env has customer/passphrase) = slice-A path, byte-identical, only branched around. Pairing (generic) = register + poll (RestartSec=30 is the poll timer); on delivery write the env 0600 and fall through to direct. Secrets + token shredded on success. - build-felhom-iso.sh --pairing: generic secret-free ISO, -generic filename, manifest mode=pairing. profiles/generic.profile (new). - test/bootstrap-modes.sh: Scenario D (direct = zero appliance calls) + pairing register/poll + delivery handoff — all green in a debian container.
This commit is contained in:
@@ -611,6 +611,36 @@ func (s *Store) migrate() error {
|
||||
legs_json TEXT NOT NULL DEFAULT '{}'
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_customer_resets_customer ON customer_resets(customer_id, id DESC);
|
||||
|
||||
-- appliance_registrations (v0.62.0, R-21 slice C — the universal secret-free ISO): a box
|
||||
-- booted from the GENERIC ISO registers itself here as an UNCLAIMED appliance, the operator
|
||||
-- binds it to a customer, and one poll delivers the customer-id + retrieval passphrase ONCE.
|
||||
-- Keyed by (uuid, mac_set): the N100 DMI verdict says serials are unusable ("Default string"),
|
||||
-- and cheap boards ship DUPLICATE SMBIOS UUIDs — the MAC set is the tiebreaker, so the same
|
||||
-- uuid with a different mac_set is a DISTINCT appliance. token_hash = sha256(appliance token);
|
||||
-- the token itself is never stored. status: registered→bound→delivered (one-shot) | discarded.
|
||||
-- This table's own timestamps ARE the provenance for the pre-bind phase (no customer to scope a
|
||||
-- customer-events row to yet — mirrors host_deletions/customer_resets self-contained provenance).
|
||||
CREATE TABLE IF NOT EXISTS appliance_registrations (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
uuid TEXT NOT NULL,
|
||||
mac_set TEXT NOT NULL,
|
||||
ssh_host_pubkeys TEXT NOT NULL DEFAULT '',
|
||||
hw_summary TEXT NOT NULL DEFAULT '',
|
||||
token_hash TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'registered',
|
||||
customer_id TEXT,
|
||||
install_mode TEXT,
|
||||
extra_args TEXT,
|
||||
first_seen DATETIME NOT NULL DEFAULT (datetime('now')),
|
||||
last_seen DATETIME NOT NULL DEFAULT (datetime('now')),
|
||||
bound_at DATETIME,
|
||||
delivered_at DATETIME,
|
||||
discarded_at DATETIME,
|
||||
UNIQUE(uuid, mac_set)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_appliance_status ON appliance_registrations(status, last_seen DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_appliance_token ON appliance_registrations(token_hash);
|
||||
`)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
Reference in New Issue
Block a user