Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,23 @@ spec:
# Neither carries the runAsGroup-without-runAsUser coupling documented above, so
# neither can stop the sandbox building.
#
# πŸ”΄ The pod-level op adds a LEAF into the chart's pod securityContext, which renders
# as `{}` (read from the live Deployment 2026-09-04). A leaf add is deliberate: adding
# the whole object would REPLACE it and silently discard any field the chart starts
# setting later, which is exactly what the +(key) anchors elsewhere exist to prevent.
# The cost is that a chart that stopped rendering the object would fail this patch β€”
# loudly, on a Renovate chart bump with CI in front of it, never silently.
# πŸ”΄ The two fields need DIFFERENT patch kinds, because the chart renders their
# parents differently. Verified by rendering flux-operator 0.50.0 with this
# HelmRelease's own values 2026-09-04:
# - containers[0].securityContext IS rendered, so a JSON-6902 leaf `add` applies.
# - spec.template.spec.securityContext is NOT rendered AT ALL, so a JSON-6902 leaf
# `add` under it fails `doc is missing path` β€” and because a JSON patch is
# all-or-nothing, that one op takes the container ops down with it.
#
# Do NOT read this off the live Deployment: the API server serialises the absent
# pod-level object as `{}`, so the cluster shows a parent that the rendered chart
# does not have. The rendered manifest is the surface a post-renderer patches.
#
# The pod-level field is therefore a strategic-merge patch, which creates the
# missing parent AND merges into a chart-set one β€” so if a future chart starts
# rendering spec.template.spec.securityContext, its fields survive. A JSON-6902
# `add` of the whole object would have replaced them silently; that is why this is
# a merge rather than an object add.
postRenderers:
- kustomize:
patches:
Expand All @@ -99,6 +110,16 @@ spec:
path: /spec/template/spec/containers/0/securityContext/seLinuxOptions
value:
level: s0
- op: add
path: /spec/template/spec/securityContext/fsGroupChangePolicy
value: OnRootMismatch
- target:
kind: Deployment
name: flux-operator
patch: |
apiVersion: apps/v1
kind: Deployment
metadata:
name: flux-operator
spec:
template:
spec:
securityContext:
fsGroupChangePolicy: OnRootMismatch
34 changes: 32 additions & 2 deletions scripts/validate-eks-ci-role-policy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -1684,10 +1684,40 @@ const (
// rendered with the SAME local renderer; the digest is not, so it comes from the
// pinned one.
//
// Re-approved 2026-09-04 for #3580, which repairs the change the paragraph above
// approved. That change could not apply at all: the pod-level op targets
// /spec/template/spec/securityContext/fsGroupChangePolicy, but the flux-operator
// chart renders no pod-level securityContext, so helm-controller failed the whole
// post-render with `doc is missing path` and neither field reached the cluster.
// #3580 moves that one field to a strategic-merge patch, which creates the missing
// parent; the container ops are unchanged.
//
// Conservation re-proven the same way, with ONE renderer on both sides: all five
// overlays rendered from main d7613b03 and from this branch, compared over the full
// apiVersion|kind|namespace|name identity in BOTH directions. 552 documents each
// side β€” ZERO added, ZERO removed. Per-root counts (126/226/188/8/4) were
// corroborated against raw document-separator counts, because `yq eval-all`
// evaluates once across a whole stream and silently reports 1. A negative control
// that drops a single document reports removed=1 and names it, so the comparison is
// not vacuous.
//
// Four of the five roots render byte-identical. The ENTIRE delta is confined to the
// flux-operator HelmRelease in the controllers root: 3 lines removed, 13 added, all
// inside its postRenderers block. No rule, verb, resource, group, subject,
// ServiceAccount, role reference or policy moved. The digest moves for the same
// reason as last time β€” that HelmRelease is itself a selected authorization-capable
// document β€” not because privilege changed.
//
// The value below is again CI's own computed digest (run 33866934697, head
// 8c9d1d11), for the same reason: this validator pins kubectl v1.36.2 and refuses
// any other, and the preparing host has v1.36.1. That run reported exactly one
// error class β€” this fingerprint β€” so nothing else in the authorization surface
// objected.
//
// The previous approved aggregate digest was:
//
// a16fbd577f8155e64aacc02cb624f26d9628d75658ce804215d9d69a3884bf92
const expectedRenderedSurfaceSHA = "ab05bc2c95924e372c038f878872aa94e3bd81380daa96a283bd7f74e2132a69"
// ab05bc2c95924e372c038f878872aa94e3bd81380daa96a283bd7f74e2132a69
const expectedRenderedSurfaceSHA = "a5dac56d1ee989648670e2bd265b56e574512b37e2e19b8caf0fe4738816d1a4"

// authorizationOverlayPaths lists every independently reconciled production
// layer where an object can grant privileges to the aws/aws service account.
Expand Down
Loading