From 04f9363e4b5a220b53a53608444cd0c20ff75f19 Mon Sep 17 00:00:00 2001 From: Nick Szaunig Date: Mon, 12 Oct 2026 05:25:45 +0200 Subject: [PATCH] fix(k8s): persist documents and app backups, pin images, run hardened Documents, avatars and the encrypted app backups lived in ./uploads and ./backups inside the container and were lost on every restart. The backend now mounts the apicore-data volume at /data (configmap paths /data/documents and /data/backups). Both deployments pin the image to a CI build (sha-) with imagePullPolicy IfNotPresent instead of :latest, run as uid 1000 with seccomp RuntimeDefault, no privilege escalation and all capabilities dropped. The frontend gets a liveness probe. Co-Authored-By: Claude Opus 5.5 (1M context) --- deploy/k8s/backend/data-pvc.yaml | 15 +++++++++++++++ deploy/k8s/backend/deployment.yaml | 28 +++++++++++++++++++++++++++- deploy/k8s/frontend/deployment.yaml | 22 +++++++++++++++++++++- 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 deploy/k8s/backend/data-pvc.yaml diff --git a/deploy/k8s/backend/data-pvc.yaml b/deploy/k8s/backend/data-pvc.yaml new file mode 100644 index 0000000..53284f0 --- /dev/null +++ b/deploy/k8s/backend/data-pvc.yaml @@ -0,0 +1,15 @@ +# Persistenter Speicher fuer Dokumente, Avatare und die verschluesselten +# App-Backups (ConfigMap: DOCUMENTS_STORAGE_PATH=/data/documents, +# BACKUP_STORAGE_PATH=/data/backups). Im Cluster: StorageClass smb-app +# (SMB 3.1.1 mit seal, uid/gid 1000 = User "node" aus dem Dockerfile). +# ReadWriteMany, damit mehrere Backend-Replicas dieselben Dateien sehen. +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: apicore-data +spec: + accessModes: [ReadWriteMany] + storageClassName: smb-app + resources: + requests: + storage: 20Gi diff --git a/deploy/k8s/backend/deployment.yaml b/deploy/k8s/backend/deployment.yaml index 4292b96..d9c4282 100644 --- a/deploy/k8s/backend/deployment.yaml +++ b/deploy/k8s/backend/deployment.yaml @@ -14,9 +14,35 @@ spec: labels: app: apicore-backend spec: + securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + seccompProfile: + type: RuntimeDefault + volumes: + # Dokumente, Avatare und App-Backups (DOCUMENTS_STORAGE_PATH / + # BACKUP_STORAGE_PATH unter /data). Ohne Volume landen sie im + # Container und sind beim naechsten Neustart weg. + - name: data + persistentVolumeClaim: + claimName: apicore-data containers: - name: apicore-backend - image: git.encry.de/nick.rosenloecher/apicore-backend:latest + # Fest auf einen Build pinnen (Tag sha- aus der CI oder Digest), + # nicht :latest - sonst gibt es kein Rollback und jeder Neustart zieht + # ungeprueft das neueste Image. Ausrollen: + # kubectl -n apicore set image deploy/apicore-backend \ + # apicore-backend=git.encry.de/nick.rosenloecher/apicore-backend:sha- + image: git.encry.de/nick.rosenloecher/apicore-backend:sha-REPLACE_ME + imagePullPolicy: IfNotPresent + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + volumeMounts: + - name: data + mountPath: /data ports: - containerPort: 3001 envFrom: diff --git a/deploy/k8s/frontend/deployment.yaml b/deploy/k8s/frontend/deployment.yaml index 77088c3..f8e6d18 100644 --- a/deploy/k8s/frontend/deployment.yaml +++ b/deploy/k8s/frontend/deployment.yaml @@ -14,9 +14,21 @@ spec: labels: app: apicore-frontend spec: + securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + seccompProfile: + type: RuntimeDefault containers: - name: apicore-frontend - image: git.encry.de/nick.rosenloecher/apicore-frontend:latest + # Fest auf einen Build pinnen (sha- aus der CI), siehe Backend. + image: git.encry.de/nick.rosenloecher/apicore-frontend:sha-REPLACE_ME + imagePullPolicy: IfNotPresent + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] ports: - containerPort: 3000 envFrom: @@ -28,6 +40,14 @@ spec: port: 3000 initialDelaySeconds: 5 periodSeconds: 10 + livenessProbe: + httpGet: + path: / + port: 3000 + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 5 + failureThreshold: 3 resources: requests: cpu: "0"