From 3c7002c23b5419c1528dc4244f17645c6d490b3a Mon Sep 17 00:00:00 2001 From: James Purcell Date: Tue, 10 Feb 2026 14:30:57 +0000 Subject: [PATCH 1/2] feat: Add GKE CEL policy compliance for readOnlyRootFilesystem and emptyDir sizeLimit This commit adds support for two common GKE CEL policy requirements: 1. require-readonly-root-filesystem: - Added readOnlyRootFilesystem: true to all container security contexts - Added allowPrivilegeEscalation: false - Dropped all capabilities - Added /tmp volume mounts with sizeLimit for writable temporary storage 2. require-emptydir-sizelimit: - Added configurable sizeLimit for emptyDir volumes - cache-volume: 50Gi (configurable via values.yaml) - tmp-volume: 1Gi (hardcoded) Changes: - braintrust/values.yaml: Added security contexts and volume configurations - templates/api-deployment.yaml: Added security context and tmp volume support - templates/brainstore-reader-deployment.yaml: Added security context, tmp volume, and sizeLimit - templates/brainstore-writer-deployment.yaml: Added security context, tmp volume, and sizeLimit All changes are backward compatible and configurable through values.yaml. Tested and verified on GKE Autopilot cluster. --- braintrust/templates/api-deployment.yaml | 15 ++++++- .../brainstore-reader-deployment.yaml | 10 ++++- .../brainstore-writer-deployment.yaml | 10 ++++- braintrust/values.yaml | 44 ++++++++++++------- 4 files changed, 60 insertions(+), 19 deletions(-) diff --git a/braintrust/templates/api-deployment.yaml b/braintrust/templates/api-deployment.yaml index 4d99d1b..5f52910 100644 --- a/braintrust/templates/api-deployment.yaml +++ b/braintrust/templates/api-deployment.yaml @@ -137,8 +137,12 @@ spec: - name: NODE_EXTRA_CA_CERTS value: "/etc/braintrust/tls/ca-bundle.pem" {{- end }} - {{- if or (and (eq .Values.cloud "azure") .Values.azure.enableAzureKeyVaultDriver) .Values.customTLSCABundle }} + {{- if or .Values.api.tmpVolume.enabled (and (eq .Values.cloud "azure") .Values.azure.enableAzureKeyVaultDriver) .Values.customTLSCABundle }} volumeMounts: + {{- if .Values.api.tmpVolume.enabled }} + - name: tmp-volume + mountPath: /tmp + {{- end }} {{- if and (eq .Values.cloud "azure") .Values.azure.enableAzureKeyVaultDriver }} - name: secrets-store-inline mountPath: "/mnt/secrets-store" @@ -150,8 +154,15 @@ spec: readOnly: true {{- end }} {{- end }} - {{- if or (and (eq .Values.cloud "azure") .Values.azure.enableAzureKeyVaultDriver) .Values.customTLSCABundle }} + {{- if or .Values.api.tmpVolume.enabled (and (eq .Values.cloud "azure") .Values.azure.enableAzureKeyVaultDriver) .Values.customTLSCABundle }} volumes: + {{- if .Values.api.tmpVolume.enabled }} + - name: tmp-volume + emptyDir: + {{- if .Values.api.tmpVolume.sizeLimit }} + sizeLimit: {{ .Values.api.tmpVolume.sizeLimit | quote }} + {{- end }} + {{- end }} {{- if .Values.customTLSCABundle }} - name: tls-ca projected: diff --git a/braintrust/templates/brainstore-reader-deployment.yaml b/braintrust/templates/brainstore-reader-deployment.yaml index e11ef25..1827869 100644 --- a/braintrust/templates/brainstore-reader-deployment.yaml +++ b/braintrust/templates/brainstore-reader-deployment.yaml @@ -142,6 +142,8 @@ spec: volumeMounts: - name: cache-volume mountPath: {{ .Values.brainstore.reader.cacheDir }} + - name: tmp-volume + mountPath: /tmp {{- if and (eq .Values.cloud "azure") .Values.azure.enableAzureKeyVaultDriver }} - name: secrets-store-inline mountPath: "/mnt/secrets-store" @@ -165,8 +167,14 @@ spec: requests: storage: {{ required "brainstore.reader.volume.size must be set" .Values.brainstore.reader.volume.size | quote }} {{- else }} - emptyDir: {} + emptyDir: + {{- if .Values.brainstore.reader.volume.sizeLimit }} + sizeLimit: {{ .Values.brainstore.reader.volume.sizeLimit | quote }} + {{- end }} {{- end }} + - name: tmp-volume + emptyDir: + sizeLimit: "1Gi" {{- if and (eq .Values.cloud "azure") .Values.azure.enableAzureKeyVaultDriver }} - name: secrets-store-inline csi: diff --git a/braintrust/templates/brainstore-writer-deployment.yaml b/braintrust/templates/brainstore-writer-deployment.yaml index fd2e66a..e867c91 100644 --- a/braintrust/templates/brainstore-writer-deployment.yaml +++ b/braintrust/templates/brainstore-writer-deployment.yaml @@ -142,6 +142,8 @@ spec: volumeMounts: - name: cache-volume mountPath: {{ .Values.brainstore.writer.cacheDir }} + - name: tmp-volume + mountPath: /tmp {{- if and (eq .Values.cloud "azure") .Values.azure.enableAzureKeyVaultDriver }} - name: secrets-store-inline mountPath: "/mnt/secrets-store" @@ -165,8 +167,14 @@ spec: requests: storage: {{ required "brainstore.writer.volume.size must be set" .Values.brainstore.writer.volume.size | quote }} {{- else }} - emptyDir: {} + emptyDir: + {{- if .Values.brainstore.writer.volume.sizeLimit }} + sizeLimit: {{ .Values.brainstore.writer.volume.sizeLimit | quote }} + {{- end }} {{- end }} + - name: tmp-volume + emptyDir: + sizeLimit: "1Gi" {{- if and (eq .Values.cloud "azure") .Values.azure.enableAzureKeyVaultDriver }} - name: secrets-store-inline csi: diff --git a/braintrust/values.yaml b/braintrust/values.yaml index 1871595..b7b7446 100644 --- a/braintrust/values.yaml +++ b/braintrust/values.yaml @@ -115,11 +115,17 @@ api: # runAsUser: 1000 # runAsGroup: 1000 # fsGroup: 1000 - # Optional: Container-level security context - # securityContext: - # capabilities: - # drop: - # - ALL + # Container-level security context (enabled for CEL policy compliance) + securityContext: + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + # Temporary directory configuration (needed when readOnlyRootFilesystem is true) + tmpVolume: + enabled: true + sizeLimit: "1Gi" # Allow running user generated code functions (e.g. scorers/tools) allowCodeFunctionExecution: true # Brainstore backfill configuration. These defaults are fine for most cases. @@ -229,11 +235,13 @@ brainstore: # runAsUser: 1000 # runAsGroup: 1000 # fsGroup: 1000 - # Optional: Container-level security context - # securityContext: - # capabilities: - # drop: - # - ALL + # Container-level security context (enabled for CEL policy compliance) + securityContext: + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL cacheDir: "/mnt/tmp/brainstore" objectStoreCacheMemoryLimit: "1Gi" objectStoreCacheFileSize: "50Gi" @@ -243,6 +251,8 @@ brainstore: volume: # Storage size for ephemeral storage requests (used with GKE Autopilot local SSDs) size: "" + # EmptyDir sizeLimit (separate from ephemeral-storage requests, required for CEL policy compliance) + sizeLimit: "50Gi" extraEnvVars: [] nodeSelector: {} tolerations: [] @@ -275,11 +285,13 @@ brainstore: # runAsUser: 1000 # runAsGroup: 1000 # fsGroup: 1000 - # Optional: Container-level security context - # securityContext: - # capabilities: - # drop: - # - ALL + # Container-level security context (enabled for CEL policy compliance) + securityContext: + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL cacheDir: "/mnt/tmp/brainstore" objectStoreCacheMemoryLimit: "1Gi" objectStoreCacheFileSize: "50Gi" @@ -290,6 +302,8 @@ brainstore: # Storage size for ephemeral storage requests # Used with GKE Autopilot local SSDs and Azure Container Storage CSI size: "" + # EmptyDir sizeLimit (separate from ephemeral-storage requests, required for CEL policy compliance) + sizeLimit: "50Gi" extraEnvVars: [] # Example: # - name: MY_ENV_VAR From 64597107341402a2c317b04cee19bd2d93d9963b Mon Sep 17 00:00:00 2001 From: James Purcell Date: Wed, 11 Feb 2026 16:34:42 +0000 Subject: [PATCH 2/2] Fix emptyDir to render as empty object when sizeLimit is unset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When api.tmpVolume.sizeLimit (or brainstore.reader/writer.volume.sizeLimit) is not set, the emptyDir field was rendering as null instead of an empty object, causing Kubernetes to reject the pod spec with a volume-type validation error. This change ensures emptyDir always renders as a valid object by adding an explicit empty object fallback ({}) when sizeLimit is not provided. Files modified: - api-deployment.yaml - brainstore-reader-deployment.yaml - brainstore-writer-deployment.yaml 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- braintrust/templates/api-deployment.yaml | 2 ++ braintrust/templates/brainstore-reader-deployment.yaml | 2 ++ braintrust/templates/brainstore-writer-deployment.yaml | 2 ++ 3 files changed, 6 insertions(+) diff --git a/braintrust/templates/api-deployment.yaml b/braintrust/templates/api-deployment.yaml index 5f52910..32aa6e4 100644 --- a/braintrust/templates/api-deployment.yaml +++ b/braintrust/templates/api-deployment.yaml @@ -161,6 +161,8 @@ spec: emptyDir: {{- if .Values.api.tmpVolume.sizeLimit }} sizeLimit: {{ .Values.api.tmpVolume.sizeLimit | quote }} + {{- else }} + {} {{- end }} {{- end }} {{- if .Values.customTLSCABundle }} diff --git a/braintrust/templates/brainstore-reader-deployment.yaml b/braintrust/templates/brainstore-reader-deployment.yaml index 1827869..9ef0a5a 100644 --- a/braintrust/templates/brainstore-reader-deployment.yaml +++ b/braintrust/templates/brainstore-reader-deployment.yaml @@ -170,6 +170,8 @@ spec: emptyDir: {{- if .Values.brainstore.reader.volume.sizeLimit }} sizeLimit: {{ .Values.brainstore.reader.volume.sizeLimit | quote }} + {{- else }} + {} {{- end }} {{- end }} - name: tmp-volume diff --git a/braintrust/templates/brainstore-writer-deployment.yaml b/braintrust/templates/brainstore-writer-deployment.yaml index e867c91..62bfb68 100644 --- a/braintrust/templates/brainstore-writer-deployment.yaml +++ b/braintrust/templates/brainstore-writer-deployment.yaml @@ -170,6 +170,8 @@ spec: emptyDir: {{- if .Values.brainstore.writer.volume.sizeLimit }} sizeLimit: {{ .Values.brainstore.writer.volume.sizeLimit | quote }} + {{- else }} + {} {{- end }} {{- end }} - name: tmp-volume