agent (unreleased): the quiet branch for older root files never hides a failed pin (the commit's security review)

Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
admin committed 2026-11-15 10:08:05 +01:00
1 parent 5ecdd3d521
commit 2bc3b57963
2 files changed
+55 -3

No files matched your search

+17 -3
View File
@@ -247,11 +247,15 @@ func (m *Manager) Ensure(ctx context.Context, why string) bool {
c, err := m.EnsureCopy(cctx)
ccancel()
if err != nil {
if rootStepTooOld(err) && cur.Fingerprint != "" {
m.mu.Lock()
recorded := m.status.Problem
m.mu.Unlock()
if rootStepTooOld(err) && cur.Fingerprint != "" && !isTrustCause(why) && !isTrustCause(recorded) {
// The agent is newer than the box's root files: between the agent update and the root-files job the
// wrapper does not know the step yet. The pinned certificate works; this is not a trust problem, and
// naming one mailed the operator at every box's update (seen on Tester 1, 2026-11-15). The bundle
// install calls Ensure again.
// install calls Ensure again. NEVER on a run that a failed call started, and never over a recorded
// trust failure: then the pin does NOT work, and the box must say so (the commit's security review).
lg.Info("pvetrust: the root files are older than this agent (no CA-copy step yet) — the pinned certificate stays; the next root-files update moves the box", "err", err)
set(modeOf(cur), "", "", nil)
return false
@@ -419,5 +423,15 @@ func (m *Manager) writeConfig(caPath string) error {
// R11 „unknown mode”): the box's root files are older than the agent. Pinned by
// TestRootStepOlderThanTheAgent_IsNotATrustProblem.
func rootStepTooOld(err error) bool {
return err != nil && strings.Contains(err.Error(), "unknown mode")
return err != nil && strings.Contains(err.Error(), `"R11"`) && strings.Contains(err.Error(), "unknown mode")
}
// isTrustCause says whether s is one of the causes proxmox.TrustProblem names for a FAILED call.
func isTrustCause(s string) bool {
switch s {
case proxmox.TrustPinnedCertChanged, proxmox.TrustCAMismatch, proxmox.TrustCertExpired,
proxmox.TrustCAFileUnreadable, proxmox.TrustNameMismatch:
return true
}
return false
}
+38
View File
@@ -525,4 +525,42 @@ func TestRootStepOlderThanTheAgent_IsNotATrustProblem(t *testing.T) {
if st := b3.m.Status(); st.Problem != ProblemCopyUnavailable {
t.Fatalf("a real refusal lost its name: %+v", st)
}
// a refusal with ANOTHER code whose text happens to hold the words is still named: the code decides
b4 := newBox(t, srv, proxmox.TLSConfig{Fingerprint: fp(leaf)}, nil)
b4.m.EnsureCopy = func(context.Context) (Copy, error) {
return Copy{}, errors.New(`pve-ca-copy refused: {"code": "T3", "reason": "the source has an unknown mode"}`)
}
b4.m.Ensure(context.Background(), "start")
if st := b4.m.Status(); st.Problem != ProblemCopyUnavailable {
t.Fatalf("a refusal with another code was read as older root files: %+v", st)
}
}
// The quiet branch must never hide a box whose pin does NOT work: a run started by a failed call, or a run after a
// failed call was recorded, names the problem even when the root files are older than the agent.
func TestRootStepOlderThanTheAgent_NeverHidesAFailedPin(t *testing.T) {
p := newPKI(t, "PVE CA")
leaf := p.leaf()
srv := newPVE(t, leaf)
old := errors.New(`pve-ca-copy refused: {"code": "R11", "reason": "unknown mode 'pve-ca-copy'"}`)
// (1) the run was started by a failed call
b := newBox(t, srv, proxmox.TLSConfig{Fingerprint: fp(leaf)}, nil)
b.m.EnsureCopy = func(context.Context) (Copy, error) { return Copy{}, old }
b.m.Ensure(context.Background(), proxmox.TrustPinnedCertChanged)
if st := b.m.Status(); st.Problem == "" {
t.Fatalf("a run started by a failed pin reports no problem: %+v", st)
}
// (2) a failed call was recorded, then the timer runs
b2 := newBox(t, srv, proxmox.TLSConfig{Fingerprint: fp(leaf)}, nil)
b2.m.EnsureCopy = func(context.Context) (Copy, error) { return Copy{}, old }
b2.m.mu.Lock()
b2.m.status.Problem = proxmox.TrustPinnedCertChanged
b2.m.mu.Unlock()
b2.m.Ensure(context.Background(), "timer")
if st := b2.m.Status(); st.Problem == "" {
t.Fatalf("the timer cleared a recorded pin failure: %+v", st)
}
}