agent (unreleased): the quiet branch for older root files never hides a failed pin (the commit's security review)
gates / gates (push) Successful in 38s
gates / gates (push) Successful in 38s
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
1 parent
5ecdd3d521
commit
88d5d56b50
2 files changed
+55
-3
No files matched your search
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user