From 0df91f13f819b85aa92cd1fbd521d46af0ed31b2 Mon Sep 17 00:00:00 2001 From: Mark Garratt Date: Sat, 14 Feb 2026 19:52:28 +0000 Subject: [PATCH 1/3] proton-bridge: add configurable volume permission handling --- charts/proton-bridge/README.md | 20 ++++++++++ .../proton-bridge/templates/deployment.yaml | 37 +++++++++++++++++-- charts/proton-bridge/values.yaml | 20 ++++++++++ 3 files changed, 74 insertions(+), 3 deletions(-) diff --git a/charts/proton-bridge/README.md b/charts/proton-bridge/README.md index a3923ee..ccb8ec8 100644 --- a/charts/proton-bridge/README.md +++ b/charts/proton-bridge/README.md @@ -94,6 +94,9 @@ Common overrides: - `bridge.host`, `bridge.smtpPort`, `bridge.imapPort` - `container.smtpPort`, `container.imapPort` - `container.enablePrivilegedPortBinding` +- `podSecurityContext.fsGroup` +- `containerSecurityContext` +- `volumePermissions.enabled` - `existingSecret` To bind directly on container ports `25` and `143`, enable privileged port binding: @@ -104,3 +107,20 @@ container: imapPort: 143 enablePrivilegedPortBinding: true ``` + +## Troubleshooting Startup Permission Errors + +If the container cannot write under `/home/bridge` at startup, set a pod `fsGroup` so Kubernetes adjusts volume group ownership: + +```yaml +podSecurityContext: + fsGroup: 1000 +``` + +If your storage backend still needs explicit ownership fixes, enable the permissions init container: + +```yaml +volumePermissions: + enabled: true + chown: "1000:1000" +``` diff --git a/charts/proton-bridge/templates/deployment.yaml b/charts/proton-bridge/templates/deployment.yaml index 76804b3..175c2fa 100644 --- a/charts/proton-bridge/templates/deployment.yaml +++ b/charts/proton-bridge/templates/deployment.yaml @@ -22,19 +22,50 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} spec: + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} + {{- if and .Values.persistence.enabled .Values.volumePermissions.enabled }} + initContainers: + - name: volume-permissions + image: "{{ .Values.volumePermissions.image.repository }}:{{ .Values.volumePermissions.image.tag }}" + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy }} + {{- with .Values.volumePermissions.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + command: + - /bin/sh + - -ec + - >- + chown -R {{ .Values.volumePermissions.chown }} {{ .Values.volumePermissions.mountPath }} + && chmod -R {{ .Values.volumePermissions.chmod }} {{ .Values.volumePermissions.mountPath }} + volumeMounts: + - name: bridge-data + mountPath: {{ .Values.volumePermissions.mountPath }} + {{- end }} containers: - name: proton-bridge image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- $containerSecurityContext := deepCopy (.Values.containerSecurityContext | default dict) }} {{- if .Values.container.enablePrivilegedPortBinding }} + {{- $capabilities := get $containerSecurityContext "capabilities" | default dict }} + {{- $add := get $capabilities "add" | default (list) }} + {{- if not (has "NET_BIND_SERVICE" $add) }} + {{- $add = append $add "NET_BIND_SERVICE" }} + {{- end }} + {{- $capabilities = mergeOverwrite $capabilities (dict "add" $add) }} + {{- $containerSecurityContext = mergeOverwrite $containerSecurityContext (dict "capabilities" $capabilities) }} + {{- end }} + {{- if $containerSecurityContext }} securityContext: - capabilities: - add: - - NET_BIND_SERVICE + {{- toYaml $containerSecurityContext | nindent 12 }} {{- end }} env: - name: BRIDGE_MODE diff --git a/charts/proton-bridge/values.yaml b/charts/proton-bridge/values.yaml index f42f3a3..9a84164 100644 --- a/charts/proton-bridge/values.yaml +++ b/charts/proton-bridge/values.yaml @@ -37,6 +37,26 @@ container: # Set true to add NET_BIND_SERVICE capability so non-root process can bind <1024 (e.g. 25/143). enablePrivilegedPortBinding: false +# Pod-level security context (for example: fsGroup to allow writing mounted volumes). +podSecurityContext: {} + +# Container-level security context for the main proton-bridge container. +containerSecurityContext: {} + +# Optional init container to force volume ownership/permissions before bridge starts. +volumePermissions: + enabled: false + image: + repository: busybox + tag: "1.37.0" + pullPolicy: IfNotPresent + securityContext: + runAsUser: 0 + runAsGroup: 0 + mountPath: /home/bridge + chown: "1000:1000" + chmod: "u+rwX,g+rwX" + existingSecret: "" secretName: "" From b749d2e5fc85488c4173c834ca8d1c718a38c935 Mon Sep 17 00:00:00 2001 From: Mark Garratt Date: Sat, 14 Feb 2026 19:55:18 +0000 Subject: [PATCH 2/3] proton-bridge: bump chart version to 0.1.5 --- charts/proton-bridge/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/proton-bridge/Chart.yaml b/charts/proton-bridge/Chart.yaml index ae58870..4933c0c 100644 --- a/charts/proton-bridge/Chart.yaml +++ b/charts/proton-bridge/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: proton-bridge description: Proton Mail Bridge deployment for in-cluster SMTP/IMAP access type: application -version: 0.1.4 +version: 0.1.5 appVersion: "3.22.0" From 1642f2a5536b0659f8eb84a06920466ee63997a6 Mon Sep 17 00:00:00 2001 From: Mark Garratt Date: Sat, 14 Feb 2026 19:59:19 +0000 Subject: [PATCH 3/3] proton-bridge: make volumePermissions upgrade-safe --- .../proton-bridge/templates/deployment.yaml | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/charts/proton-bridge/templates/deployment.yaml b/charts/proton-bridge/templates/deployment.yaml index 175c2fa..985b502 100644 --- a/charts/proton-bridge/templates/deployment.yaml +++ b/charts/proton-bridge/templates/deployment.yaml @@ -22,6 +22,13 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} spec: + {{- $volumePermissions := .Values.volumePermissions | default dict }} + {{- $volumePermissionsEnabled := (get $volumePermissions "enabled" | default false) }} + {{- $vpImage := (get $volumePermissions "image" | default dict) }} + {{- $vpSecurityContext := (get $volumePermissions "securityContext") }} + {{- $vpMountPath := (get $volumePermissions "mountPath" | default "/home/bridge") }} + {{- $vpChown := (get $volumePermissions "chown" | default "1000:1000") }} + {{- $vpChmod := (get $volumePermissions "chmod" | default "u+rwX,g+rwX") }} {{- with .Values.podSecurityContext }} securityContext: {{- toYaml . | nindent 8 }} @@ -30,12 +37,12 @@ spec: imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} - {{- if and .Values.persistence.enabled .Values.volumePermissions.enabled }} + {{- if and .Values.persistence.enabled $volumePermissionsEnabled }} initContainers: - name: volume-permissions - image: "{{ .Values.volumePermissions.image.repository }}:{{ .Values.volumePermissions.image.tag }}" - imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy }} - {{- with .Values.volumePermissions.securityContext }} + image: "{{ (get $vpImage "repository" | default "busybox") }}:{{ (get $vpImage "tag" | default "1.37.0") }}" + imagePullPolicy: {{ (get $vpImage "pullPolicy" | default "IfNotPresent") }} + {{- with $vpSecurityContext }} securityContext: {{- toYaml . | nindent 12 }} {{- end }} @@ -43,11 +50,11 @@ spec: - /bin/sh - -ec - >- - chown -R {{ .Values.volumePermissions.chown }} {{ .Values.volumePermissions.mountPath }} - && chmod -R {{ .Values.volumePermissions.chmod }} {{ .Values.volumePermissions.mountPath }} + chown -R {{ $vpChown }} {{ $vpMountPath }} + && chmod -R {{ $vpChmod }} {{ $vpMountPath }} volumeMounts: - name: bridge-data - mountPath: {{ .Values.volumePermissions.mountPath }} + mountPath: {{ $vpMountPath }} {{- end }} containers: - name: proton-bridge