From d3dca678411cc3086c78426d437f3ca4cd4a629a Mon Sep 17 00:00:00 2001 From: Tommy Skaug Date: Mon, 11 Nov 2024 21:11:58 +0100 Subject: [PATCH] chore(immich): hardening of security according to audit report by Polaris --- charts/immich/Chart.yaml | 3 +- charts/immich/ci/pluto-values.yaml | 78 +++++++++++++++++++ charts/immich/templates/db-init.yaml | 35 +++++++-- .../deployment-machine-learning.yaml | 47 ++++++++--- .../templates/deployment-microservices.yaml | 32 +++++++- .../immich/templates/deployment-server.yaml | 33 +++++++- 6 files changed, 204 insertions(+), 24 deletions(-) create mode 100644 charts/immich/ci/pluto-values.yaml diff --git a/charts/immich/Chart.yaml b/charts/immich/Chart.yaml index 8a77216..2f3f932 100644 --- a/charts/immich/Chart.yaml +++ b/charts/immich/Chart.yaml @@ -2,10 +2,11 @@ apiVersion: v2 name: immich description: | High-performance self-hosted photo and video management +# renovate: image=ghcr.io/immich-app/immich-server appVersion: 1.105.1 type: application icon: https://avatars.githubusercontent.com/u/109746326?s=200&v=4 -version: 0.1.16 +version: 1.0.0 maintainers: - name: Tommy Skaug email: tommy@skaug.me diff --git a/charts/immich/ci/pluto-values.yaml b/charts/immich/ci/pluto-values.yaml new file mode 100644 index 0000000..41f5a32 --- /dev/null +++ b/charts/immich/ci/pluto-values.yaml @@ -0,0 +1,78 @@ +config: + externalUrl: https://photos.example.com/ + machineLearningUrl: http://immich-machine-learning.home.svc.cluster.local:3003 + +image: + repository: ghcr.io/immich-app/immich-server + +envSecretName: immich-secret + +library: + persistence: + existingClaim: immich + configuration: + trash: + enabled: false + days: 30 + storageTemplate: + enabled: true + template: "{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}" + +microservices: + securityContext: {} + resources: + limits: + memory: "3Gi" + cpu: "1000m" + requests: + cpu: 100m + memory: 100Mi + +server: + securityContext: {} + resources: + limits: + memory: "3Gi" + cpu: "1000m" + requests: + cpu: 100m + memory: 100Mi + +machineLearning: + persistence: + accessMode: ReadWriteOnce + size: 5Gi + image: + repository: ghcr.io/immich-app/immich-machine-learning + tag: v1.120.1 + securityContext: {} + resources: + limits: + memory: "3Gi" + cpu: "1000m" + requests: + cpu: 100m + memory: 100Mi + +initContainers: + dbInit: + image: + repository: ghcr.io/onedr0p/postgres-init + tag: "16" + existingSecretName: immich-postgres-init-secret + pgvecto: + image: + repository: tensorchord/pgvecto-rs + tag: "pg16-v0.2.1-rootless" + existingSecretName: immich-postgres-init-secret + +metrics: + enabled: true + port: 9001 + +redis: + host: dragonfly.databases.svc.cluster.local + port: 6397 + +postgres: + host: postgres-pgvectors-rw.databases.svc.cluster.local diff --git a/charts/immich/templates/db-init.yaml b/charts/immich/templates/db-init.yaml index f8793ea..a564e7a 100644 --- a/charts/immich/templates/db-init.yaml +++ b/charts/immich/templates/db-init.yaml @@ -1,4 +1,3 @@ ---- apiVersion: batch/v1 kind: Job metadata: @@ -9,22 +8,37 @@ spec: template: spec: restartPolicy: Never + automountServiceAccountToken: false + priorityClassName: "high-priority" containers: - name: general-db-init image: "{{ .Values.initContainers.dbInit.image.repository }}:{{ .Values.initContainers.dbInit.image.tag }}" + imagePullPolicy: IfNotPresent env: - - name: INIT_POSTGRES_HOST - value: {{ .Values.postgres.host }} + - name: INIT_POSTGRES_HOST + value: {{ .Values.postgres.host }} envFrom: - secretRef: name: {{ .Values.initContainers.dbInit.existingSecretName }} + securityContext: + runAsUser: 1000 + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "1Gi" + cpu: "500m" - name: pgvecto-db-init command: [ "bin/sh", "-c", "psql -a -f /initdb/create-extensions.sql" ] image: "{{ .Values.initContainers.pgvecto.image.repository }}:{{ .Values.initContainers.pgvecto.image.tag }}" + imagePullPolicy: IfNotPresent volumeMounts: - name: pgvector-initdb mountPath: /initdb - readOnly: false + readOnly: true env: - name: PGPASSWORD valueFrom: @@ -40,8 +54,19 @@ spec: key: INIT_POSTGRES_DBNAME - name: PGHOST value: {{ .Values.postgres.host }} + securityContext: + runAsUser: 1000 + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "1Gi" + cpu: "500m" volumes: - name: pgvector-initdb configMap: name: {{ include "immich.fullname" . }}-pgvector-initdb - backoffLimit: 3 \ No newline at end of file + backoffLimit: 3 diff --git a/charts/immich/templates/deployment-machine-learning.yaml b/charts/immich/templates/deployment-machine-learning.yaml index 3a325d3..8845843 100644 --- a/charts/immich/templates/deployment-machine-learning.yaml +++ b/charts/immich/templates/deployment-machine-learning.yaml @@ -1,4 +1,3 @@ ---- apiVersion: apps/v1 kind: Deployment metadata: @@ -17,14 +16,30 @@ spec: labels: app: {{ include "immich.fullname" . }}-machine-learning spec: + priorityClassName: "high-priority" + automountServiceAccountToken: false + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: "topology.kubernetes.io/zone" + whenUnsatisfiable: DoNotSchedule + labelSelector: + matchLabels: + app: {{ include "immich.fullname" . }}-machine-learning securityContext: - {{- toYaml .Values.machineLearning.securityContext | nindent 8 }} + runAsUser: 1000 + runAsNonRoot: true + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true containers: - name: immich-machine-learning env: {{- include "immich.envs" . | nindent 10 }} image: {{ .Values.machineLearning.image.repository }}:{{ .Values.machineLearning.image.tag }} - imagePullPolicy: IfNotPresent + imagePullPolicy: Always + securityContext: + runAsUser: 1000 + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true livenessProbe: failureThreshold: 3 httpGet: @@ -35,9 +50,9 @@ spec: successThreshold: 1 timeoutSeconds: 1 ports: - - containerPort: 3003 - name: http - protocol: TCP + - containerPort: 3003 + name: http + protocol: TCP readinessProbe: failureThreshold: 3 httpGet: @@ -52,10 +67,20 @@ spec: terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: - - mountPath: /cache - name: cache + - mountPath: /cache + name: cache serviceAccountName: {{ include "immich.fullname" . }} volumes: - - name: cache - persistentVolumeClaim: - claimName: {{ include "immich.fullname" . }}-machine-learning-cache \ No newline at end of file + - name: cache + persistentVolumeClaim: + claimName: {{ include "immich.fullname" . }}-machine-learning-cache +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "immich.fullname" . }}-machine-learning-pdb +spec: + minAvailable: 1 + selector: + matchLabels: + app: {{ include "immich.fullname" . }}-machine-learning diff --git a/charts/immich/templates/deployment-microservices.yaml b/charts/immich/templates/deployment-microservices.yaml index 0944a94..f118eb8 100644 --- a/charts/immich/templates/deployment-microservices.yaml +++ b/charts/immich/templates/deployment-microservices.yaml @@ -1,4 +1,3 @@ ---- apiVersion: apps/v1 kind: Deployment metadata: @@ -15,9 +14,20 @@ spec: labels: app: {{ include "immich.fullname" . }}-microservices spec: + priorityClassName: "high-priority" + automountServiceAccountToken: false + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: "topology.kubernetes.io/zone" + whenUnsatisfiable: DoNotSchedule + labelSelector: + matchLabels: + app: {{ include "immich.fullname" . }}-microservices securityContext: - {{- toYaml .Values.microservices.securityContext | nindent 8 }} - automountServiceAccountToken: true + runAsUser: 1000 + runAsNonRoot: true + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true containers: - name: immich-microservices args: @@ -27,7 +37,11 @@ spec: env: {{- include "immich.envs" . | nindent 10 }} image: {{ .Values.image.repository }}:v{{ .Chart.AppVersion }} - imagePullPolicy: IfNotPresent + imagePullPolicy: Always + securityContext: + runAsUser: 1000 + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true livenessProbe: failureThreshold: 3 periodSeconds: 10 @@ -65,3 +79,13 @@ spec: - name: library persistentVolumeClaim: claimName: {{ .Values.library.persistence.existingClaim }} +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "immich.fullname" . }}-microservices-pdb +spec: + minAvailable: 1 + selector: + matchLabels: + app: {{ include "immich.fullname" . }}-microservices diff --git a/charts/immich/templates/deployment-server.yaml b/charts/immich/templates/deployment-server.yaml index da81c18..6dbd287 100644 --- a/charts/immich/templates/deployment-server.yaml +++ b/charts/immich/templates/deployment-server.yaml @@ -1,4 +1,3 @@ ---- apiVersion: apps/v1 kind: Deployment metadata: @@ -15,8 +14,22 @@ spec: labels: app: {{ include "immich.fullname" . }}-server spec: + priorityClassName: "high-priority" + automountServiceAccountToken: false + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: "topology.kubernetes.io/zone" + whenUnsatisfiable: DoNotSchedule + labelSelector: + matchLabels: + app: {{ include "immich.fullname" . }}-server securityContext: - {{- toYaml .Values.server.securityContext | nindent 8 }} + runAsUser: 1000 + runAsNonRoot: true + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: ["ALL"] containers: - name: immich-server args: @@ -26,7 +39,11 @@ spec: env: {{- include "immich.envs" . | nindent 10 }} image: {{ .Values.image.repository }}:v{{ .Chart.AppVersion }} - imagePullPolicy: IfNotPresent + imagePullPolicy: Always + securityContext: + runAsUser: 1000 + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true livenessProbe: failureThreshold: 3 httpGet: @@ -64,3 +81,13 @@ spec: - name: library persistentVolumeClaim: claimName: {{ .Values.library.persistence.existingClaim }} +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "immich.fullname" . }}-server-pdb +spec: + minAvailable: 1 + selector: + matchLabels: + app: {{ include "immich.fullname" . }}-server