Harden pod and container security contexts across all workloads
Add securityContext hardening (runAsNonRoot, readOnlyRootFilesystem, capabilities drop ALL, allowPrivilegeEscalation false) to the 10 workloads that lacked it: apache-tika, cyberchef, gotenberg, homeassistant, matrix-delegation-nginx, paperless-server, paperless-consume cronjob (plus automountServiceAccountToken false), patchmon-frontend, urlaubsverwaltung and zipline. All contexts are configurable via securityContext.pod / securityContext.container in values.yaml; writable paths are provided as emptyDirs. Image-forced deviations (Home Assistant root start for s6-overlay + NET_RAW for discovery, paperless root init container for /run ownership) are documented in the charts' values.yaml. Major version bump for every changed published chart.
This commit is contained in:
31 files changed
+357
-16
No files matched your search
@@ -4,7 +4,7 @@ description: A Helm chart to deploy Paperless-NGX on Kubernetes
|
||||
|
||||
type: application
|
||||
|
||||
version: 5.0.0+up2.20.13
|
||||
version: 6.0.0+up2.20.13
|
||||
|
||||
appVersion: 2.20.13
|
||||
|
||||
|
||||
@@ -9,10 +9,15 @@ spec:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
securityContext:
|
||||
{{- toYaml .Values.smbConnection.cron.securityContext.pod | nindent 12 }}
|
||||
containers:
|
||||
- name: paperless-consume-cronjob
|
||||
image: busybox:1.28
|
||||
imagePullPolicy: IfNotPresent
|
||||
securityContext:
|
||||
{{- toYaml .Values.smbConnection.cron.securityContext.container | nindent 16 }}
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
|
||||
@@ -17,10 +17,49 @@ spec:
|
||||
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
securityContext:
|
||||
{{- toYaml .Values.paperless.securityContext.pod | nindent 8 }}
|
||||
initContainers:
|
||||
# s6-overlay refuses to start unless /run is owned by the runtime uid,
|
||||
# but emptyDirs are always created root-owned. This minimal init
|
||||
# container hands /run over to the paperless uid (see
|
||||
# paperless.securityContext in values.yaml) so the main container can
|
||||
# run fully hardened (non-root, no privilege escalation).
|
||||
- name: init-run-ownership
|
||||
image: busybox:1.28
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- chown {{ .Values.paperless.securityContext.pod.runAsUser }}:{{ .Values.paperless.securityContext.pod.runAsGroup }} /run
|
||||
securityContext:
|
||||
runAsNonRoot: false
|
||||
runAsUser: 0
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
add:
|
||||
- CHOWN
|
||||
volumeMounts:
|
||||
- mountPath: /run
|
||||
name: run
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 16Mi
|
||||
ephemeral-storage: 1Mi
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 32Mi
|
||||
ephemeral-storage: 10Mi
|
||||
containers:
|
||||
- name: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
||||
image: "ghcr.io/paperless-ngx/paperless-ngx:{{ .Chart.AppVersion }}"
|
||||
imagePullPolicy: IfNotPresent
|
||||
securityContext:
|
||||
{{- toYaml .Values.paperless.securityContext.container | nindent 12 }}
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
protocol: TCP
|
||||
@@ -153,6 +192,12 @@ spec:
|
||||
subPath: export/
|
||||
- mountPath: "/usr/src/paperless/consume"
|
||||
name: "paperless-consume-volume"
|
||||
# Writable emptyDirs so the root filesystem can stay read-only:
|
||||
# s6-overlay needs /run, OCR/consumption uses /tmp for scratch files.
|
||||
- mountPath: /tmp
|
||||
name: tmp
|
||||
- mountPath: /run
|
||||
name: run
|
||||
resources:
|
||||
requests:
|
||||
memory: "{{ .Values.paperless.resources.requests.memory }}"
|
||||
@@ -189,6 +234,10 @@ spec:
|
||||
timeoutSeconds: 5
|
||||
restartPolicy: Always
|
||||
volumes:
|
||||
- name: tmp
|
||||
emptyDir: {}
|
||||
- name: run
|
||||
emptyDir: {}
|
||||
- name: paperless-volume
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ .Release.Name }}-{{ .Chart.Name }}-volume-claim
|
||||
|
||||
@@ -42,11 +42,49 @@ paperless:
|
||||
cpu: 1
|
||||
memory: 2048Mi
|
||||
ephemeralStorage: 400Mi
|
||||
# Paperless-ngx supports rootless operation: the pod runs as uid/gid 1000
|
||||
# (the image's default paperless user), fsGroup makes the PVCs writable and
|
||||
# /tmp + /run are emptyDirs so the root filesystem stays read-only.
|
||||
# Documented deviation: s6-overlay requires /run to be OWNED by the runtime
|
||||
# uid (group-write is not enough), so a minimal root init container chowns
|
||||
# the /run emptyDir to runAsUser/runAsGroup before the hardened main
|
||||
# container starts.
|
||||
securityContext:
|
||||
pod:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
runAsGroup: 1000
|
||||
fsGroup: 1000
|
||||
fsGroupChangePolicy: Always
|
||||
container:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
smbConnection:
|
||||
enabled: false
|
||||
server: null
|
||||
cronJob: "0 0,12 * * *"
|
||||
cron:
|
||||
# The consume mover only shuffles files between the SMB share (mounted
|
||||
# with 0777 modes) and the consume PVC, so it runs as the same non-root
|
||||
# uid/gid as paperless itself with a fully hardened context.
|
||||
securityContext:
|
||||
pod:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
runAsGroup: 1000
|
||||
fsGroup: 1000
|
||||
fsGroupChangePolicy: Always
|
||||
container:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
|
||||
Reference in new issue
Block a user