@@ -9,6 +9,77 @@ The format is based on the regulated environment requirements:
99
1010---
1111
12+ ## [ 2026-04-24 15:00] - Security audit remediation: grace period, retry-count poisoning, input bounds
13+
14+ ** Author:** Erick Bourgeois
15+
16+ ### Changed
17+ - ` src/reconcilers/helpers.rs ` (` check_grace_period_elapsed ` ): guard
18+ against negative ` elapsed ` durations produced by clock-skew (NTP step,
19+ VM freeze-thaw, backward wall-clock adjustment). The prior comparison
20+ ` elapsed.num_seconds() >= timeout.as_secs() as i64 ` silently returned
21+ ` false ` when ` elapsed ` was negative, potentially bypassing the
22+ graceful-drain window entirely. Negative elapsed is now treated as
23+ "timeout reached" so the reconciler forces progress rather than
24+ stalling on a misbehaving clock.
25+ - ` src/reconcilers/helpers.rs ` (` error_policy ` ): replace
26+ ` ctx.retry_counts.lock().unwrap_or_else(PoisonError::into_inner) `
27+ with an explicit ` match ` that aborts the error-policy pass and
28+ requeues after ` ERROR_REQUEUE_SECS ` on poison. The prior pattern
29+ silently recovered a potentially-inconsistent map and continued
30+ mutating it, corrupting the exponential-backoff schedule for every
31+ subsequent error.
32+ - ` src/reconcilers/helpers.rs ` : new ` validate_cluster_name() ` and
33+ ` validate_kill_if_commands() ` with bounds from ` src/constants.rs ` .
34+ Called early in ` reconcile_inner ` (defence-in-depth against clusters
35+ that have not enabled the ValidatingAdmissionPolicy) and re-called in
36+ ` add_machine_to_cluster ` before touching CAPI.
37+ - ` src/constants.rs ` : new ` MAX_CLUSTER_NAME_LEN = 63 ` (RFC-1123 DNS
38+ label — the effective CAPI cluster-name cap), plus
39+ ` MAX_KILL_IF_COMMANDS_COUNT = 100 ` and ` MAX_KILL_IF_COMMAND_LEN = 256 ` .
40+ - ` src/crd.rs ` : attach bounded JSON-schema generators
41+ (` cluster_name_schema ` , ` kill_if_commands_schema ` ) so the generated
42+ CRD enforces the same limits at the kube API level.
43+ - ` src/metrics.rs ` : replace ` unreachable!() ` in the metric-registration
44+ fallback constructors with ` panic!() ` carrying
45+ ` FALLBACK_METRIC_BUG_MSG ` — a pointed diagnostic that identifies the
46+ offending metric name instead of a generic "entered unreachable code"
47+ message.
48+ - ` deploy/admission/validatingadmissionpolicy.yaml ` : add CEL expressions
49+ 1b/1c/1d/1e mirroring the new runtime validators — ` clusterName `
50+ length/charset, ` killIfCommands ` count/per-entry-length — so invalid
51+ specs are rejected at admission instead of only at reconciliation
52+ time.
53+ - ` src/reconcilers/helpers_tests.rs ` : 20 new tests covering the grace-
54+ period clock-skew fix, ` validate_cluster_name ` (happy path, empty,
55+ over-cap, control chars, non-ASCII), and ` validate_kill_if_commands `
56+ (None/empty/typical/cap-boundary/over-cap/empty-entry).
57+ - ` deploy/crds/scheduledmachine.yaml ` : regenerated via `cargo run --bin
58+ crdgen` to pick up the new schema constraints.
59+ - ` docs/src/reference/api.md ` : regenerated via ` make crddoc ` .
60+
61+ ### Why
62+ Findings from the deep security audit of the codebase (see
63+ conversation transcript 2026-04-24). Three issues were actionable: a
64+ clock-skew bypass of the graceful-drain window (critical; violates the
65+ contract of graceful shutdown under SOX §404 / NIST AC-3), an
66+ unbounded ` killIfCommands ` that could balloon Prometheus label
67+ cardinality and pin reclaim-agent CPU (high; DoS vector), and
68+ silent recovery from a poisoned retry-count mutex (medium; corrupts
69+ the exponential-backoff schedule). Cluster-name was also unbounded;
70+ the 63-char cap matches the effective CAPI constraint (cluster names
71+ flow into DNS labels downstream). Metric ` unreachable!() ` is a
72+ cosmetic bug-diagnostic improvement.
73+
74+ ### Impact
75+ - [ ] Breaking change
76+ - [x] Requires cluster rollout (new CRD schema constraints; existing
77+ CRs that violate them will be rejected on next ` kubectl apply ` )
78+ - [ ] Config change only
79+ - [ ] Documentation only
80+
81+ ---
82+
1283## [ 2026-04-23 08:00] - Fix vexctl-install Linux path (wrong asset filename)
1384
1485** Author:** Erick Bourgeois
0 commit comments