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:
@@ -31,19 +31,24 @@ const certValidity = 10 * 365 * 24 * time.Hour
|
||||
//
|
||||
// Persisting the generated pair keeps the fingerprint STABLE across agent restarts — a fresh
|
||||
// cert each boot would silently invalidate every already-issued bootstrap's pin.
|
||||
func EnsureLeaf(certPath, keyPath, bridgeHost string) (tls.Certificate, string, error) {
|
||||
// The returned `generated` is false when an existing pair was LOADED (the fingerprint is stable) and
|
||||
// true when a fresh leaf was GENERATED (every previously-issued bootstrap pin is now invalid — the
|
||||
// caller logs this LOUD, B.1, so an accidental regeneration like the 2026-06-28 migration is visible
|
||||
// immediately instead of silently breaking every controller's pin for days).
|
||||
func EnsureLeaf(certPath, keyPath, bridgeHost string) (cert tls.Certificate, fingerprint string, generated bool, err error) {
|
||||
if fileExists(certPath) && fileExists(keyPath) {
|
||||
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
|
||||
cert, err = tls.LoadX509KeyPair(certPath, keyPath)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, "", fmt.Errorf("localapi: load leaf %s: %w", certPath, err)
|
||||
return tls.Certificate{}, "", false, fmt.Errorf("localapi: load leaf %s: %w", certPath, err)
|
||||
}
|
||||
fp, err := leafFingerprint(cert)
|
||||
fingerprint, err = leafFingerprint(cert)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, "", err
|
||||
return tls.Certificate{}, "", false, err
|
||||
}
|
||||
return cert, fp, nil
|
||||
return cert, fingerprint, false, nil // LOADED
|
||||
}
|
||||
return generateLeaf(certPath, keyPath, bridgeHost)
|
||||
cert, fingerprint, err = generateLeaf(certPath, keyPath, bridgeHost)
|
||||
return cert, fingerprint, true, err // GENERATED
|
||||
}
|
||||
|
||||
// generateLeaf creates a self-signed ECDSA-P256 leaf, writes the cert (0644) + key (0600) to
|
||||
|
||||
Reference in New Issue
Block a user