kubernetes · diff

git:20260516.215750b to git:20261011.38d4a57

14 added, 0 removed. Audit A to A.

---
description: Kubernetes and OpenShift workload generation rules — security defaults, resource limits, probes
globs: ["**/*.yaml", "**/*.yml", "**/deployment*.yaml", "**/pod*.yaml"]
alwaysApply: false
---
# Kubernetes & OpenShift Rules
Always generate Deployments with all of the following. Missing any item is a Critical finding.
## Required fields on every container
```yaml
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
memory: "256Mi" # Set memory limit. Omit cpu limit — it causes throttling.
securityContext:
runAsNonRoot: true
runAsUser: 1000 # Omit on OpenShift — SCC assigns the UID
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
livenessProbe:
httpGet: { path: /healthz, port: 8080 }
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet: { path: /ready, port: 8080 }
initialDelaySeconds: 5
periodSeconds: 5
```
## Required ServiceAccount settings
```yaml
automountServiceAccountToken: false
```
## Required pod-level securityContext
```yaml
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
```
+ ## Ingress
+
+ Never propose ingress-nginx for new work: its best-effort maintenance ended in March 2026 (no releases, bug fixes or security fixes). Default to Gateway API with Envoy Gateway.
+
+ - One shared `Gateway` owned by the platform team; app teams own `HTTPRoute`s in their own namespaces
+ - Bind each HTTPS listener to the namespace that owns its hostname (`allowedRoutes` selector on `kubernetes.io/metadata.name`); never `from: All`, never one shared opt-in label
+ - Gateway API CRDs on the **standard** channel, applied as plain manifests, never a Helm release (too large for its 1 MiB Secret), and never garbage-collected: Flux `prune: false` + `deletionPolicy: Orphan` (deleting a CRD deletes every Gateway and route). Envoy Gateway's controller chart bundles experimental CRDs: set `crds.enabled: false`
+ - App namespaces belong to the platform layer, not the app layer
+ - TLS via cert-manager v1.15+ with `config.gatewayAPI.enabled: true`; one HTTPS listener per hostname with `hostname`, `tls.mode: Terminate`, `certificateRefs`
+ - Flux: `infrastructure` (CRDs, controllers) → `infrastructure-configs` (Gateway objects) → `apps` (routes), with a `healthCheckExprs` check on Gateway `Programmed`
+ - Port-80 HTTPS redirect with `statusCode: 308`; `backendRefs` use the Service port **number**
+ - Migrating: inventory annotations first; treat every ingress2gateway warning as blocking; run both controllers in parallel and move traffic by DNS one hostname at a time; decommission ingress-nginx in a separate change after a soak
+
## Never generate
- `privileged: true`
- `hostNetwork: true` / `hostPID: true` / `hostIPC: true`
- `image: myapp:latest` — always pin to a digest or explicit tag
- Missing `PodDisruptionBudget` for services with `replicas > 1`
- Missing `NetworkPolicy` for any namespace that handles sensitive data
+ - ingress-nginx (`ingressClassName: nginx`, the `ingress-nginx` chart) for new work: use an `HTTPRoute` on the shared Gateway