Files
helm-charts/rallly/values.yaml
T
jklein 37eda69fdd rallly: strict values.schema.json, ingress gate and null-safe hardening
Three changes, all about values that do not do what they promise:

1. values.schema.json (#68): additionalProperties: false on every
   chart-owned object level, so a key the chart does not read is rejected
   at render time instead of being silently ignored.

2. Ingress gate (#93): the Ingress template is wrapped in
   {{- if .Values.ingress.enabled }} and the key is added to values.yaml
   with default true, so behaviour is unchanged.

   ingress.domain is NOT a pure Ingress value here - it is also the app's
   public base URL (NEXT_PUBLIC_BASE_URL). Gating alone would therefore
   have introduced a new silent failure: with the Ingress switched off and
   no domain, the deployment rendered value: "https://" and every
   generated link would be broken. The deployment's read of the value is
   now required(), so that case fails loudly. Only clusterIssuer actually
   becomes unnecessary when the Ingress is off.

3. Null-safety for hardening, probes and resources (#99): an erased block
   fell back to "securityContext: null" (unhardened), a vanished probe or
   a container without resource accounting. Restoring the pod context
   matters especially here, because its fsGroup is what makes the Next.js
   cache emptyDir group-writable for the container.

rallly.cache is deliberately NOT null-safe: cache.path is required(), so
an erased block fails loudly and belongs to the loud class #99 excludes.

Apart from an added comment, the rendered manifest is unchanged.
2026-08-23 20:58:52 +02:00

115 lines
4.2 KiB
YAML

# Scale the whole chart down without uninstalling it: scaleDown: true renders
# every Deployment/StatefulSet with replicas: 0 (and suspends CronJobs) and
# takes precedence over any per-workload replicas value. PVCs, Secrets and
# Services stay in place; scaleDown: false brings the workloads back up.
scaleDown: false
secrets:
ralllySecretRef: null
oidcSecretRef: null
databaseSecretRef: null
smtpSecretRef: null
rallly:
# Replica count for this workload; 0 is allowed and scales it down.
# The chart-global scaleDown flag takes precedence.
replicas: 1
smtp:
enabled: false
allowedEmails: '*'
supportEmail: null
oidc:
name: 'OIDC'
pictureClaimPath: 'picture'
emailClaimPath: 'email'
nameClaimPath: 'name'
# Next.js writes its cache below the app directory (prerendered/ISR pages and
# the optimized images of /_next/image). The container runs as uid 20000
# while /app belongs to the image's own "nextjs" user, so that path is not
# writable for the app even without readOnlyRootFilesystem — the chart mounts
# a writable volume over it. The pod's fsGroup (1000) is what makes the
# mounted volume group-writable for the container.
#
# Deliberately an emptyDir, not a PVC: the cache is volatile. After every pod
# restart it starts out empty and Next.js rebuilds it on demand, which costs
# a little time on the first request after a restart. That is correct for a
# cache — its entries are derived data tied to the build id of the image, so
# a persistent volume would only carry stale entries across app updates.
cache:
# Mount path of the cache volume. Matches the Next.js cache directory of
# the upstream image; only needs changing if the image layout changes.
path: /app/apps/web/.next/cache
# Optional upper bound for the emptyDir, e.g. 512Mi. Unset means the volume
# is only bounded by the container's ephemeral-storage limit (computed as
# limitFactor x request, see resources below) — note that emptyDir usage
# counts against that limit, and exceeding either bound evicts the pod.
sizeLimit: null
securityContext:
pod:
runAsNonRoot: true
runAsUser: 20000
runAsGroup: 1000
fsGroup: 1000
fsGroupChangePolicy: Always
container:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
# No capabilities are added: the app is a plain Node process listening on
# the unprivileged port 3000, verified to run with all capabilities
# dropped. Do not reintroduce an "add:" key here — an empty list item
# under it renders as a null entry, which is what Trivy reported as
# AVD-KSV-0022 for the older chart version deployed in the cluster (#84).
capabilities:
drop:
- ALL
# Extra environment variables, appended to the ones the chart always sets.
# Values are rendered through tpl; value, secretKeyRef and configMapKeyRef
# forms are supported.
env: []
resources:
# Limits are optional: memory and ephemeral-storage limits default to
# limitFactor x the request, an explicitly set limit always wins, and a
# CPU limit is only set when configured explicitly under "limits".
limitFactor: 3
requests:
cpu: 0.1
memory: 512Mi
ephemeral-storage: 100Mi
livenessProbe:
httpGet:
path: /
port: http
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /
port: http
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
# The startup probe gates liveness/readiness, so those need no initialDelaySeconds.
# Startup budget = periodSeconds * failureThreshold = 150s.
startupProbe:
httpGet:
path: /
port: http
periodSeconds: 5
timeoutSeconds: 5
failureThreshold: 30
ingress:
# Set to false to omit the Ingress entirely. Note that ingress.domain stays
# required even then: it is also the app's public base URL
# (NEXT_PUBLIC_BASE_URL), not just the Ingress host. Only clusterIssuer
# becomes unnecessary.
enabled: true
clusterIssuer: null
domain: null
className: nginx
# Extra annotations for the Ingress; merged with the cert-manager
# cluster-issuer annotation the chart always sets.
annotations: {}