openova/platform/kyverno-policies/chart/templates/baseline/05-resource-requests.yaml
hatiyildiz fb952fadf9 feat(security/kyverno): split policies into bp-kyverno-policies@1.0.0 Blueprint
Splits the 20 EPIC-1 (#1096) compliance ClusterPolicy templates out of
bp-kyverno (engine umbrella chart) into a dedicated Blueprint
bp-kyverno-policies@1.0.0 with its own HelmRelease, ordered via HR-to-HR
dependsOn on bp-kyverno in the bootstrap-kit Kustomization.

WHY (the bug we're killing):
PR #1138 (2026-05-08) shipped 20 ClusterPolicy templates with
`enabled: false` defaults → dead-on-arrival for 11 days. PR #1933
(2026-05-19) flipped 18 defaults to `enabled: true` + bumped chart
1.1.0 → 1.2.0 + bumped the bootstrap-kit pin — but hit a CRD install-
ordering race on fresh prov t33: ClusterPolicy CRs (in
templates/policies/baseline/*.yaml) and Kyverno CRDs (in upstream
charts/crds/templates/) render in the SAME Helm pass, and the
apiserver's RESTMapper has not yet learned kyverno.io/v1.ClusterPolicy
when Helm applies the ClusterPolicy CRs. PR #1935 reverted ONLY the
bootstrap-kit pin (1.2.0 → 1.1.0) — chart source kept claiming policies
were on by default while the deployed pin pulled an engine-only artifact
with zero policies. "Theater on theater" — founder walk on t34 confirmed
GET /api/v1/sovereigns/<id>/compliance/policies returns `policyCount=0`,
only `useraccess-boundary` (from bp-crossplane-claims) was installed.

The structural fix is splitting the chart so the engine + CRDs reconcile
+ register first, THEN the policy chart applies its CRs cleanly. Audit
mode default = non-blocking (admission still passes, PolicyReport rows
populate). Operators flip individual policies to Enforce per-Sovereign
overlay or via EnvironmentPolicy.spec.compliance.modes (slice C2
controller path — separate work item).

CHANGES:

1. NEW chart `platform/kyverno-policies/chart/`:
   - Chart.yaml: name=bp-kyverno-policies, version=1.0.0, no subchart deps
   - values.yaml: `compliancePolicies:` block moved verbatim from bp-kyverno
     (defaults: 18 enabled+Audit, 2 intentionally OFF — `hubbleFlowsSeen`
     stub for W2 evaluator, `cosignVerified` until operator supplies PEM)
   - templates/baseline/01-..20-*.yaml: 20 ClusterPolicy templates moved
     via `git mv` (preserves blame; preserves PR #1933's 3 operator fixes
     — regex_match JMESPath + operator: Equals for 11/12/19)
   - tests/fixtures/: moved with the policies (fixtures reference policy
     output, not engine output)

2. ENGINE chart `platform/kyverno/chart/`:
   - Chart.yaml: 1.2.0 → 1.2.1 (policies removed, source no longer
     drift-claims compliance content)
   - values.yaml: `compliancePolicies:` block deleted (now lives in
     bp-kyverno-policies)
   - templates/clusterpolicy-mutate-add-openova-labels.yaml + ...require-
     openova-labels.yaml KEPT (engine-coupled mutating policies, EPIC-0
     label-vocab E1/E2, defaults OFF — separate concern from EPIC-1
     compliance library)
   - Empty `templates/policies/` directory removed

3. NEW bootstrap-kit slot `clusters/_template/bootstrap-kit/27a-kyverno-
   policies.yaml`:
   - HelmRelease bp-kyverno-policies pinned at chart `1.0.0`
   - HR-level `dependsOn: [bp-kyverno]` — same-kind, honored by Flux
     (per docs/INVIOLABLE-PRINCIPLES.md #14 cross-kind HR→Kustomization
     dependsOn is silently ignored, so we keep ordering at HR→HR within
     the single bootstrap-kit Kustomization)
   - targetNamespace: kyverno (same as engine — ClusterPolicy is cluster-
     scoped but the umbrella overlay namespacing matches the engine)
   - disableWait: true — Kyverno reports ClusterPolicy Ready asynchronously
     so we don't want downstream HRs stalling on policy-level health

4. UPDATED `clusters/_template/bootstrap-kit/kustomization.yaml`:
   - Added `27a-kyverno-policies.yaml` immediately after `27-kyverno.yaml`

5. BUMPED `clusters/_template/bootstrap-kit/27-kyverno.yaml`:
   - Engine pin 1.1.0 → 1.2.1 (engine-only; install behavior identical
     to 1.1.0 since policies + their values are no longer in this chart)

VALIDATION (Principle #15 — validate against fresh state, not stable state):

  $ helm template bp-kyverno-policies platform/kyverno-policies/chart \
      | grep -c '^kind: ClusterPolicy'
  18
  $ helm lint platform/kyverno-policies/chart && helm lint platform/kyverno/chart
  ==> 1 chart(s) linted, 0 chart(s) failed (both)
  $ helm template bp-kyverno platform/kyverno/chart \
      | grep -c '^kind: ClusterPolicy'
  0   # engine no longer renders any ClusterPolicy CRs
  $ helm package platform/kyverno-policies/chart
  Successfully packaged → bp-kyverno-policies-1.0.0.tgz (20 templates)

  CRD-race REPRODUCED locally without container runtime: applying the
  rendered policy YAML to a cluster WITHOUT Kyverno CRDs returns
    "no matches for kind \"ClusterPolicy\" in version \"kyverno.io/v1\"
     ensure CRDs are installed first"
  for every policy — proving the install-order fix is necessary.

  Full `helm install` from-scratch on Kind blocked locally (no container
  runtime on bastion); the Blueprint-Release CI workflow runs the full
  `helm dependency build` + package + GHCR push pipeline AND a
  `helm template` smoke render at publish time — that is the fresh-state
  Helm install gate before any pin lands.

CI / GHCR (Principle #13):
  Blueprint-Release workflow auto-detects `platform/kyverno-policies/chart/**`
  and publishes `oci://ghcr.io/openova-io/bp-kyverno-policies:1.0.0`
  on push to main. The slot pin in 27a-kyverno-policies.yaml is set to
  `1.0.0` to match (auto-bump-pin step is a no-op when source version
  already matches the slot pin).

DELIBERATELY OUT OF SCOPE:
  - W2 Go evaluator for `hubble-flows-seen` (stub stays a no-op)
  - Cosign publicKey supply path for `cosign-verified`
  - Per-Environment EnvironmentPolicy.spec.compliance.modes enforcement
    flip controller
  - Score-aggregator weight defaults configuration UI
  - `useraccess-boundary` (lives in bp-crossplane-claims, unchanged)

This does NOT close #1096. The EPIC remains open until a fresh-prov walk
shows `kubectl get clusterpolicies -A` returning the 18 baseline policies
+ useraccess-boundary, plus the AppDetail Compliance tab rendering non-
zero policyCount. Founder closes #1096 after that walk.

Refs #1096, Refs #2019, Refs #1929, Refs #1936
2026-05-20 02:32:54 +02:00

68 lines
2.3 KiB
YAML

{{/*
K1 — resource-requests (resilience, ENFORCING default).
Every container must declare resources.requests.cpu AND
resources.requests.memory. Without requests, the scheduler treats the
Pod as BestEffort, which means it (a) gets evicted first under
pressure and (b) can't be reasoned about for capacity planning.
Default mode enforcing per design §4.3 — requests are mandatory for
Catalyst's per-Org accounting (slice S1 sums them for billing
projection) so missing values silently corrupt the rollup.
*/}}
{{- if .Values.compliancePolicies.resourceRequests.enabled -}}
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: resource-requests
labels:
app.kubernetes.io/name: bp-kyverno
app.kubernetes.io/component: compliance-policy
catalyst.openova.io/policy-tier: compliance
catalyst.openova.io/policy-domain: resilience
annotations:
policies.kyverno.io/title: Containers must declare CPU and memory requests
policies.kyverno.io/category: catalyst-compliance
policies.kyverno.io/severity: high
policies.kyverno.io/description: >-
Every container must declare resources.requests.cpu AND
resources.requests.memory. BestEffort Pods evict first under
pressure and can't be reasoned about for capacity planning or
per-Org cost rollups.
spec:
validationFailureAction: {{ .Values.compliancePolicies.resourceRequests.action | default "Audit" }}
background: true
rules:
- name: containers-must-declare-cpu-memory-requests
match:
any:
- resources:
kinds:
- Deployment
- StatefulSet
- DaemonSet
- Job
- CronJob
exclude:
any:
- resources:
namespaces:
{{- range $ns := .Values.compliancePolicies.resourceRequests.excludeNamespaces }}
- {{ $ns }}
{{- end }}
validate:
message: >-
Every container in
{{ `{{request.object.kind}}/{{request.object.metadata.name}}` }} must
declare resources.requests.cpu and resources.requests.memory.
pattern:
spec:
template:
spec:
containers:
- resources:
requests:
cpu: "?*"
memory: "?*"
{{- end -}}