controller v0.196.0: the recovered key installs itself (R-200 plumbing half) -- MinAgent 0.125.0
gates / gates (push) Successful in 8s
gates / gates (push) Successful in 8s
--recover-offsite-install is the sibling of --recover-offsite-check: same fetch/unseal path through the agent, same STDIN discipline for R, but it PLACES the recovered repository password via InjectOffboxPassword so a rebuilt box reopens the history it inherited. Doing this by hand would put the offsite DATA key through a terminal, a clipboard and shell history. In-process the value goes agent -> this process -> the 0600 file and is rendered nowhere. The confirmation is a SECOND invocation: without --confirm-install it prints both hashes and writes nothing, so the operator sees the comparison before any write is possible. Three outcomes, named distinctly: installed (no local password -- the rebuilt-box shape), unchanged (identical key already present, nothing written), refused (a DIFFERENT key present; installing would clobber the key the current repository is encrypted under, and no force option is offered). Exit 2 for the refusal, distinct from 1 for a failed step. Red-proof: removing the confirmation gate makes the dry run write, failing the test. The R-persistence test carries a positive control -- a planted copy is found, then removed and not found -- because an absence check is worth only what its sensitivity is.
This commit is contained in:
@@ -76,6 +76,8 @@ func main() {
|
||||
printResetCode := flag.Bool("print-reset-code", false, "Customer-claim escape hatch (v0.122.0, F-4): print a fresh one-time local claim/reset code to stdout, then exit. Root-gated by reachability (docker exec). Same gate consumes it.")
|
||||
printInfraImages := flag.Bool("print-infra-images", false, "Print every controller-managed infra image (one per line) and exit. Read by the golden bake (felhom-agent configs/build-golden.sh) so the appliance image pre-pulls exactly what THIS controller version will request.")
|
||||
recoverOffsiteCheck := flag.Bool("recover-offsite-check", false, "R-200 diagnostic: read the customer recovery code from STDIN, recover the offsite repository password from the hub-held sealed escrow via the agent, and report whether it matches the one on disk — BY HASH. Compares, never installs; writes nothing. Exit 0 = match, 2 = clean mismatch, 1 = a step failed.")
|
||||
recoverOffsiteInstall := flag.Bool("recover-offsite-install", false, "R-200: read the customer recovery code from STDIN, recover the offsite repository password, print both hashes, and — with --confirm-install — PLACE it so the existing repository reopens. Without --confirm-install it is a dry run that writes nothing.")
|
||||
confirmInstall := flag.Bool("confirm-install", false, "Required alongside --recover-offsite-install to actually write the recovered repository password. Deliberately a second invocation so the hashes are seen before any write is possible.")
|
||||
flag.Parse()
|
||||
|
||||
if *showVersion {
|
||||
@@ -92,6 +94,35 @@ func main() {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// R-200 (v0.196.0) — the INSTALL sibling. Same fetch/unseal path, same stdin discipline for R;
|
||||
// the difference is that it places the recovered password so a rebuilt box reopens the off-site
|
||||
// history it inherited. Two invocations by design: without --confirm-install it prints the hashes
|
||||
// and writes nothing.
|
||||
if *recoverOffsiteInstall {
|
||||
cfg, err := config.LoadPermissive(*configPath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "recover-offsite-install: loading config: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
sett, err := settings.Load(cfg.Paths.DataDir+"/settings.json", log.New(os.Stderr, "", 0))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "recover-offsite-install: loading settings: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
ac, err := agentapi.New(cfg.LocalAPI.Endpoint, cfg.LocalAPI.Token, cfg.LocalAPI.Fingerprint)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "recover-offsite-install: agent channel: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(backup.RecoverAndInstall(backup.RecoveryCheckDeps{
|
||||
Manager: backup.NewManager(cfg, sett, log.New(os.Stderr, "", 0)),
|
||||
Recoverer: ac,
|
||||
In: os.Stdin,
|
||||
Out: os.Stdout,
|
||||
Err: os.Stderr,
|
||||
}, *confirmInstall))
|
||||
}
|
||||
|
||||
// R-200 (v0.195.0) — the offsite-key recovery check. A `docker exec` escape hatch in the shape of
|
||||
// --print-reset-code above, and deliberately NOT a page or a browser-reachable API: the
|
||||
// customer-facing flow is designed on top of a chain that has been walked, and this is the walk.
|
||||
|
||||
Reference in New Issue
Block a user