v0.106.0: offsite provisioning SLICE 2 — controller apply-bridge
On startup reconcile the hub-served offsite: descriptor into a key-only offbox target. internal/offsiteapply.Bridge: verify-pin box host key vs host_fingerprint (NO blind TOFU) → consume the one-time password (single-use, never logged) → sshpass ssh-copy-id -s -f install + verify → configure offbox → EscrowState=pending (fork-4 via Manager.ApplyOffsiteTarget) → persist a descriptor-hash marker LAST. Idempotent + fail-safe. Seams faked in tests; both red-proofs run+reverted. Dockerfile + sshpass. NOT yet live-applied (supervised end-to-end next runbook). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -164,6 +164,30 @@ func (m *Manager) OffboxConfigured() bool {
|
||||
// offboxRepoPwPattern matches a valid restic repo password (generateOffboxPassword = 32 rand bytes → 64 hex).
|
||||
var offboxRepoPwPattern = regexp.MustCompile(`^[0-9a-fA-F]{64}$`)
|
||||
|
||||
// ApplyOffsiteTarget configures the offbox target from a hub-provisioned descriptor (SLICE 2 apply-bridge):
|
||||
// it writes the 0600 SSH key + pinned known_hosts, sets the target with EscrowState="pending", and pushes
|
||||
// the repo password to the agent for escrow — the SAME fork-4 enable path a manual config takes. `stage` is
|
||||
// the agent escrow-stage push (nil skips it, e.g. when the agent is unreachable — the run gate still holds).
|
||||
func (m *Manager) ApplyOffsiteTarget(ctx context.Context, tgt *settings.OffboxTarget, sshKeyPEM, knownHosts string, stage func(ctx context.Context, pw string) error) error {
|
||||
if err := m.WriteOffboxSecrets(sshKeyPEM, knownHosts); err != nil {
|
||||
return fmt.Errorf("apply offsite secrets: %w", err)
|
||||
}
|
||||
if tgt.EscrowState != "escrowed" {
|
||||
tgt.EscrowState = "pending"
|
||||
}
|
||||
if err := m.settings.SetOffboxTarget(tgt); err != nil {
|
||||
return fmt.Errorf("apply offsite target: %w", err)
|
||||
}
|
||||
if stage != nil {
|
||||
// Best-effort: the offbox is configured + pending regardless. A stage-push failure (agent momentarily
|
||||
// unreachable) is logged, not fatal — the escrow can be (re-)staged later (operator ceremony / re-enable).
|
||||
if err := m.PushOffboxPasswordForEscrow(ctx, stage); err != nil {
|
||||
m.logger.Printf("[WARN] [offbox] apply-offsite: escrow stage push failed (agent unreachable?) — offbox configured pending, re-stage later: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PushOffboxPasswordForEscrow reads the 0600 repo password and hands it to `stage` (the agent push), so
|
||||
// the web/handler caller never sees the value — used by the enable flow to escrow-stage the offsite key.
|
||||
func (m *Manager) PushOffboxPasswordForEscrow(ctx context.Context, stage func(ctx context.Context, pw string) error) error {
|
||||
|
||||
Reference in New Issue
Block a user