agent: EnsureLeaf signals + loud-WARNs a regenerated leaf (prevention B.1) v0.46.0
EnsureLeaf returns generated bool; call-site logs INFO 'leaf LOADED' vs WARN 'leaf REGENERATED — previously issued bootstrap pins now INVALID'. Catches the 2026-06-28 silent-regen incident class. Test: first=generated, second=loaded+same fp. No new sudo surface. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pg8ANF97SEeKYSN5Jxw3qJ
This commit is contained in:
@@ -45,7 +45,7 @@ import (
|
||||
|
||||
// version is the agent version. Overridable at build time with
|
||||
// -ldflags "-X main.version=<v>"; defaults to the in-repo CHANGELOG version.
|
||||
var version = "0.45.0"
|
||||
var version = "0.46.0"
|
||||
|
||||
// runGuestHook is the PVE pre-start hook body (`felhom-agent guest-hook <vmid> <phase>`). On the
|
||||
// pre-start phase it creates placeholder dirs for any absent bind-mount source so the guest always boots
|
||||
@@ -710,12 +710,19 @@ func buildLocalAPIServer(cfg config.Config, px *proxmox.Client, store *backup.St
|
||||
}
|
||||
*outTokens = tokens
|
||||
host, _, _ := net.SplitHostPort(cfg.LocalAPI.ListenAddr)
|
||||
cert, fp, err := localapi.EnsureLeaf(cfg.LocalAPI.CertPath(), cfg.LocalAPI.KeyPath(), host)
|
||||
cert, fp, generated, err := localapi.EnsureLeaf(cfg.LocalAPI.CertPath(), cfg.LocalAPI.KeyPath(), host)
|
||||
if err != nil {
|
||||
logger.Warn("daemon: local-api disabled (leaf cert)", "err", err)
|
||||
return nil
|
||||
}
|
||||
logger.Info("local-api leaf ready", "fingerprint_sha256", fp, "cert", cfg.LocalAPI.CertPath())
|
||||
if generated {
|
||||
// B.1: a freshly-minted leaf invalidates every already-issued bootstrap pin. LOUD so an
|
||||
// accidental regeneration (e.g. a state-dir move during a reinstall) is caught immediately.
|
||||
logger.Warn("local-api leaf REGENERATED — any previously issued bootstrap pins are now INVALID; controllers will fail the pin check until re-bootstrapped (or restore the prior leaf)",
|
||||
"fingerprint_sha256", fp, "cert", cfg.LocalAPI.CertPath())
|
||||
} else {
|
||||
logger.Info("local-api leaf LOADED", "fingerprint_sha256", fp, "cert", cfg.LocalAPI.CertPath())
|
||||
}
|
||||
runner := backup.NewBackupRunner(px, cfg.Backup.BackupTarget(), "", "felhom local-api", logger)
|
||||
// Guest data-drive passthrough (slice 10 P2): a root-CLI runner for the `pct set` bind + chown
|
||||
// (same fenced ExecRunner the host-storage + provision back-half use).
|
||||
@@ -1298,7 +1305,7 @@ func runSelftestProvision(ctx context.Context, cfg config.Config, logger *slog.L
|
||||
// The leaf fingerprint baked into the bootstrap must be the SAME leaf the daemon's local-API
|
||||
// server serves — so use the configured (persisted) cert path. EnsureLeaf generates it once.
|
||||
host, _, _ := net.SplitHostPort(cfg.LocalAPI.ListenAddr)
|
||||
_, fingerprint, err := localapi.EnsureLeaf(cfg.LocalAPI.CertPath(), cfg.LocalAPI.KeyPath(), host)
|
||||
_, fingerprint, _, err := localapi.EnsureLeaf(cfg.LocalAPI.CertPath(), cfg.LocalAPI.KeyPath(), host)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "selftest=provision: local-api leaf:", err)
|
||||
return 1
|
||||
|
||||
Reference in New Issue
Block a user