diff --git a/.github/workflows/helm_ci.yml b/.github/workflows/helm_ci.yml index 3e12704c4..f2740f4b1 100644 --- a/.github/workflows/helm_ci.yml +++ b/.github/workflows/helm_ci.yml @@ -731,6 +731,7 @@ jobs: --set "$component.containerSecurityContext.enabled=true" --set "$component.containerSecurityContext.privileged=false" --set "$component.containerSecurityContext.allowPrivilegeEscalation=false" + --set "$component.containerSecurityContext.readOnlyRootFilesystem=true" --set "$component.containerSecurityContext.capabilities.drop[0]=ALL" --set "$component.containerSecurityContext.seccompProfile.type=RuntimeDefault" ) @@ -746,10 +747,15 @@ jobs: --set worker.enabled=true \ --set volume.idx.type=hostPath \ --set volume.idx.hostPathPrefix=/tmp \ - --set cosi.enabled=true > /tmp/security-contexts.yaml + --set cosi.enabled=true \ + --set global.seaweedfs.tmpDir.sizeLimit=64Mi > /tmp/security-contexts.yaml helm template test $CHART_DIR \ "${security_context_args[@]}" \ - --set allInOne.enabled=true > /tmp/security-contexts-aio.yaml + --set allInOne.enabled=true \ + --set master.enabled=false \ + --set volume.enabled=false \ + --set filer.enabled=false \ + --set global.seaweedfs.tmpDir.sizeLimit=64Mi > /tmp/security-contexts-aio.yaml python3 - /tmp/security-context-defaults.yaml /tmp/security-contexts.yaml /tmp/security-contexts-aio.yaml <<'PYEOF' import sys @@ -770,10 +776,15 @@ jobs: errors.append(f"{prefix}: privileged is not false") if context.get("allowPrivilegeEscalation") is not False: errors.append(f"{prefix}: allowPrivilegeEscalation is not false") + if context.get("readOnlyRootFilesystem") is not True: + errors.append(f"{prefix}: readOnlyRootFilesystem is not true") if context.get("capabilities", {}).get("drop") != ["ALL"]: errors.append(f"{prefix}: capabilities.drop is not exactly [ALL]") if context.get("seccompProfile", {}).get("type") != "RuntimeDefault": errors.append(f"{prefix}: seccompProfile is not RuntimeDefault") + mounts = {mount["name"]: mount for mount in container.get("volumeMounts", [])} + if mounts.get("seaweedfs-tmp", {}).get("mountPath") != "/tmp": + errors.append(f"{prefix}: writable temporary volume is not mounted at /tmp") with open(sys.argv[1]) as stream: default_documents = [document for document in yaml.safe_load_all(stream) if document] @@ -784,12 +795,22 @@ jobs: pod = document["spec"]["template"]["spec"] if "securityContext" in pod: errors.append(f"{name}: pod securityContext should be absent by default") + if any(volume["name"] == "seaweedfs-tmp" for volume in pod.get("volumes", [])): + errors.append(f"{name}: writable /tmp volume should be absent by default") for container in pod.get("containers", []): if "securityContext" in container: errors.append( f"{name}/{container['name']}: container securityContext " f"should be absent by default" ) + if any( + mount["name"] == "seaweedfs-tmp" + for mount in container.get("volumeMounts", []) + ): + errors.append( + f"{name}/{container['name']}: writable /tmp mount " + f"should be absent by default" + ) for path in sys.argv[2:]: with open(path) as stream: @@ -800,6 +821,12 @@ jobs: workloads += 1 name = document["metadata"]["name"] pod = document["spec"]["template"]["spec"] + volumes = {volume["name"]: volume for volume in pod.get("volumes", [])} + temporary = volumes.get("seaweedfs-tmp", {}).get("emptyDir") + if temporary is None: + errors.append(f"{name}: writable /tmp emptyDir is missing") + elif temporary.get("sizeLimit") != "64Mi": + errors.append(f"{name}: temporary volume sizeLimit is not 64Mi") component = document["spec"]["template"]["metadata"]["labels"].get("app.kubernetes.io/component") if component: components.add(component) @@ -828,9 +855,9 @@ jobs: f"security context workload coverage is incomplete: " f"expected {sorted(expected_components)}, got {sorted(components)}" ) - if workloads != 10 or containers != 11: + if workloads != 10 or containers != 12: errors.append( - f"expected 10 workloads and 11 containers, got " + f"expected 10 workloads and 12 containers, got " f"{workloads} workloads and {containers} containers" ) if chart_managed_init_containers != 1: @@ -1768,8 +1795,10 @@ jobs: --set volume.containerSecurityContext.enabled=true \ --set volume.containerSecurityContext.privileged=false \ --set volume.containerSecurityContext.allowPrivilegeEscalation=false \ + --set volume.containerSecurityContext.readOnlyRootFilesystem=true \ --set volume.containerSecurityContext.capabilities.drop[0]=ALL \ --set volume.containerSecurityContext.seccompProfile.type=RuntimeDefault \ + --set global.seaweedfs.tmpDir.sizeLimit=64Mi \ > /tmp/security-context-resize-hook.yaml python3 - /tmp/security-context-resize-hook.yaml <<'PYEOF' @@ -1797,8 +1826,15 @@ jobs: "allowPrivilegeEscalation": False, "capabilities": {"drop": ["ALL"]}, "privileged": False, + "readOnlyRootFilesystem": True, "seccompProfile": {"type": "RuntimeDefault"}, } + assert pod["containers"][0]["volumeMounts"] == [ + {"mountPath": "/tmp", "name": "seaweedfs-tmp"}, + ] + assert pod["volumes"] == [ + {"emptyDir": {"sizeLimit": "64Mi"}, "name": "seaweedfs-tmp"}, + ] PYEOF kubectl delete namespace "$NS" echo "Volume resize hook security contexts render correctly" diff --git a/k8s/charts/seaweedfs/Chart.yaml b/k8s/charts/seaweedfs/Chart.yaml index 99b2866a1..493597029 100644 --- a/k8s/charts/seaweedfs/Chart.yaml +++ b/k8s/charts/seaweedfs/Chart.yaml @@ -3,4 +3,4 @@ description: SeaweedFS name: seaweedfs appVersion: "4.48" # Dev note: Trigger a helm chart release by `git tag -a helm-` -version: 4.48.1 +version: 4.48.2 diff --git a/k8s/charts/seaweedfs/README.md b/k8s/charts/seaweedfs/README.md index 53556c38a..e68d19f68 100644 --- a/k8s/charts/seaweedfs/README.md +++ b/k8s/charts/seaweedfs/README.md @@ -572,11 +572,20 @@ The DNS selectors default to CoreDNS as kubeadm, kind and the managed offerings ## Pod and container security contexts -Pod and container security contexts are configurable independently for every built-in workload and remain empty by default for backwards compatibility. The examples in `values.yaml` show how to enable a `RuntimeDefault` seccomp profile, disable privilege escalation and privileged mode, and drop all Linux capabilities. +Pod and container security contexts are configurable independently for every built-in workload and remain empty by default for backwards compatibility. The examples in `values.yaml` show how to enable a `RuntimeDefault` seccomp profile, disable privilege escalation and privileged mode, drop all Linux capabilities, and use a read-only root filesystem. + +SeaweedFS uses `/tmp` for Unix sockets, temporary uploads, worker task files, and other runtime data. When `readOnlyRootFilesystem` is enabled for a built-in component, the chart mounts a writable `emptyDir` at `/tmp` for its chart-managed containers. Its optional size limit can be configured globally: + +```yaml +global: + seaweedfs: + tmpDir: + sizeLimit: 1Gi +``` The chart does not enable `runAsNonRoot` by default because its default `hostPath` storage may be owned by root. To enforce the Kubernetes `restricted` Pod Security Standard, use storage that is writable by a non-root user and configure `runAsNonRoot` or use the OpenShift overrides below. -Security contexts configured for a component also apply to the chart-managed helper containers for that component. User-provided init containers and sidecars must define their own container security context. +Security contexts configured for a component also apply to the chart-managed helper containers for that component. User-provided init containers and sidecars must define their own container security context and writable mounts. ## OpenShift Support @@ -586,6 +595,7 @@ To deploy on OpenShift, use the provided `openshift-values.yaml` which overrides 1. Use `PersistentVolumeClaims` instead of `hostPath`. 2. Enable `runAsNonRoot` and omit hardcoded UIDs to allow OpenShift to assign valid UIDs automatically. 3. Apply appropriate `seccompProfile` and drop capabilities. +4. Use a read-only root filesystem with writable temporary storage at `/tmp`. Usage: ```bash diff --git a/k8s/charts/seaweedfs/openshift-values.yaml b/k8s/charts/seaweedfs/openshift-values.yaml index 3846f7297..e844e7833 100644 --- a/k8s/charts/seaweedfs/openshift-values.yaml +++ b/k8s/charts/seaweedfs/openshift-values.yaml @@ -15,6 +15,7 @@ # automatically assign a valid UID from the namespace's allocated range. # 3. Dropping all Linux capabilities and setting allowPrivilegeEscalation: false # 4. Enabling RuntimeDefault seccompProfile +# 5. Using a read-only root filesystem with writable temporary storage # # Usage: # helm install seaweedfs seaweedfs/seaweedfs \ @@ -49,6 +50,7 @@ master: containerSecurityContext: enabled: true allowPrivilegeEscalation: false + readOnlyRootFilesystem: true capabilities: drop: ["ALL"] runAsNonRoot: true @@ -76,6 +78,7 @@ volume: containerSecurityContext: enabled: true allowPrivilegeEscalation: false + readOnlyRootFilesystem: true capabilities: drop: ["ALL"] runAsNonRoot: true @@ -101,6 +104,7 @@ filer: containerSecurityContext: enabled: true allowPrivilegeEscalation: false + readOnlyRootFilesystem: true capabilities: drop: ["ALL"] runAsNonRoot: true @@ -125,6 +129,7 @@ s3: containerSecurityContext: enabled: true allowPrivilegeEscalation: false + readOnlyRootFilesystem: true capabilities: drop: ["ALL"] runAsNonRoot: true diff --git a/k8s/charts/seaweedfs/templates/admin/admin-statefulset.yaml b/k8s/charts/seaweedfs/templates/admin/admin-statefulset.yaml index 2bab36fe2..82bf7b7c7 100644 --- a/k8s/charts/seaweedfs/templates/admin/admin-statefulset.yaml +++ b/k8s/charts/seaweedfs/templates/admin/admin-statefulset.yaml @@ -192,6 +192,7 @@ spec: {{ $arg }}{{- if lt $index (sub (len $.Values.admin.extraArgs) 1) }} \{{ end }} {{- end }} volumeMounts: + {{- include "seaweedfs.tmpDirVolumeMount" (list . .Values.admin.containerSecurityContext (tpl (.Values.admin.extraVolumeMounts | default "") .) (tpl (.Values.admin.extraVolumes | default "") .)) | nindent 12 }} {{- if or (eq .Values.admin.data.type "hostPath") (eq .Values.admin.data.type "persistentVolumeClaim") (eq .Values.admin.data.type "emptyDir") (eq .Values.admin.data.type "existingClaim") }} - name: admin-data mountPath: /data @@ -267,6 +268,7 @@ spec: {{- include "seaweedfs.tplvalues.render" (dict "value" .Values.admin.sidecars "context" $) | nindent 8 }} {{- end }} volumes: + {{- include "seaweedfs.tmpDirVolume" (list . .Values.admin.containerSecurityContext (tpl (.Values.admin.extraVolumeMounts | default "") .) (tpl (.Values.admin.extraVolumes | default "") .) false) | nindent 8 }} {{- if eq .Values.admin.data.type "hostPath" }} - name: admin-data hostPath: diff --git a/k8s/charts/seaweedfs/templates/all-in-one/all-in-one-deployment.yaml b/k8s/charts/seaweedfs/templates/all-in-one/all-in-one-deployment.yaml index 9f4a92e75..60bac4ade 100644 --- a/k8s/charts/seaweedfs/templates/all-in-one/all-in-one-deployment.yaml +++ b/k8s/charts/seaweedfs/templates/all-in-one/all-in-one-deployment.yaml @@ -306,6 +306,7 @@ spec: {{- end }} volumeMounts: + {{- include "seaweedfs.tmpDirVolumeMount" (list . .Values.allInOne.containerSecurityContext (tpl (.Values.allInOne.extraVolumeMounts | default "") .) (tpl (.Values.allInOne.extraVolumes | default "") .)) | nindent 12 }} - name: data mountPath: /data {{- if and .Values.allInOne.s3.enabled (or .Values.allInOne.s3.enableAuth .Values.s3.enableAuth .Values.filer.s3.enableAuth) }} @@ -433,6 +434,7 @@ spec: {{- include "seaweedfs.tplvalues.render" (dict "value" .Values.allInOne.sidecars "context" $) | nindent 8 }} {{- end }} volumes: + {{- include "seaweedfs.tmpDirVolume" (list . .Values.allInOne.containerSecurityContext (tpl (.Values.allInOne.extraVolumeMounts | default "") .) (tpl (.Values.allInOne.extraVolumes | default "") .) false) | nindent 8 }} {{- include "seaweedfs.licenseVolume" . | nindent 8 }} - name: data {{- if eq .Values.allInOne.data.type "hostPath" }} diff --git a/k8s/charts/seaweedfs/templates/cosi/cosi-deployment.yaml b/k8s/charts/seaweedfs/templates/cosi/cosi-deployment.yaml index 005df44ea..09d79da0a 100644 --- a/k8s/charts/seaweedfs/templates/cosi/cosi-deployment.yaml +++ b/k8s/charts/seaweedfs/templates/cosi/cosi-deployment.yaml @@ -113,6 +113,7 @@ spec: {{- end -}} {{- end }} volumeMounts: + {{- include "seaweedfs.tmpDirVolumeMount" (list . .Values.cosi.containerSecurityContext (tpl (.Values.cosi.extraVolumeMounts | default "") .) (tpl (.Values.cosi.extraVolumes | default "") .)) | nindent 12 }} - mountPath: /var/lib/cosi name: socket {{- if .Values.cosi.enableAuth }} @@ -162,6 +163,7 @@ spec: fieldRef: fieldPath: metadata.namespace volumeMounts: + {{- include "seaweedfs.tmpDirVolumeMount" (list . .Values.cosi.containerSecurityContext "" "") | nindent 12 }} - mountPath: /var/lib/cosi name: socket {{- with .Values.cosi.sidecar.resources }} @@ -175,6 +177,7 @@ spec: {{- include "seaweedfs.tplvalues.render" (dict "value" .Values.cosi.sidecars "context" $) | nindent 8 }} {{- end }} volumes: + {{- include "seaweedfs.tmpDirVolume" (list . .Values.cosi.containerSecurityContext (tpl (.Values.cosi.extraVolumeMounts | default "") .) (tpl (.Values.cosi.extraVolumes | default "") .) true) | nindent 8 }} - name: socket emptyDir: {} {{- if .Values.cosi.enableAuth }} diff --git a/k8s/charts/seaweedfs/templates/filer/filer-statefulset.yaml b/k8s/charts/seaweedfs/templates/filer/filer-statefulset.yaml index 5ded97469..fe11c4b18 100644 --- a/k8s/charts/seaweedfs/templates/filer/filer-statefulset.yaml +++ b/k8s/charts/seaweedfs/templates/filer/filer-statefulset.yaml @@ -229,6 +229,7 @@ spec: {{ . }} \ {{- end }} volumeMounts: + {{- include "seaweedfs.tmpDirVolumeMount" (list . .Values.filer.containerSecurityContext (tpl (.Values.filer.extraVolumeMounts | default "") .) (tpl (.Values.filer.extraVolumes | default "") .)) | nindent 12 }} {{- if (or (eq .Values.filer.logs.type "hostPath") (eq .Values.filer.logs.type "persistentVolumeClaim") (eq .Values.filer.logs.type "emptyDir")) }} - name: seaweedfs-filer-log-volume mountPath: "/logs/" @@ -337,6 +338,7 @@ spec: {{- include "seaweedfs.tplvalues.render" (dict "value" .Values.filer.sidecars "context" $) | nindent 8 }} {{- end }} volumes: + {{- include "seaweedfs.tmpDirVolume" (list . .Values.filer.containerSecurityContext (tpl (.Values.filer.extraVolumeMounts | default "") .) (tpl (.Values.filer.extraVolumes | default "") .) false) | nindent 8 }} {{- if eq .Values.filer.logs.type "hostPath" }} - name: seaweedfs-filer-log-volume hostPath: diff --git a/k8s/charts/seaweedfs/templates/master/master-statefulset.yaml b/k8s/charts/seaweedfs/templates/master/master-statefulset.yaml index 89e99d470..08a371c30 100644 --- a/k8s/charts/seaweedfs/templates/master/master-statefulset.yaml +++ b/k8s/charts/seaweedfs/templates/master/master-statefulset.yaml @@ -179,6 +179,7 @@ spec: {{ . }} \ {{- end }} volumeMounts: + {{- include "seaweedfs.tmpDirVolumeMount" (list . .Values.master.containerSecurityContext (tpl (.Values.master.extraVolumeMounts | default "") .) (tpl (.Values.master.extraVolumes | default "") .)) | nindent 12 }} - name : data-{{ .Release.Namespace }} mountPath: /data {{- if or (eq .Values.master.logs.type "hostPath") (eq .Values.master.logs.type "persistentVolumeClaim") (eq .Values.master.logs.type "emptyDir") }} @@ -258,6 +259,7 @@ spec: {{- include "seaweedfs.tplvalues.render" (dict "value" .Values.master.sidecars "context" $) | nindent 8 }} {{- end }} volumes: + {{- include "seaweedfs.tmpDirVolume" (list . .Values.master.containerSecurityContext (tpl (.Values.master.extraVolumeMounts | default "") .) (tpl (.Values.master.extraVolumes | default "") .) false) | nindent 8 }} {{- include "seaweedfs.licenseVolume" . | nindent 8 }} {{- if eq .Values.master.logs.type "hostPath" }} - name: seaweedfs-master-log-volume diff --git a/k8s/charts/seaweedfs/templates/s3/s3-deployment.yaml b/k8s/charts/seaweedfs/templates/s3/s3-deployment.yaml index a8126830c..4fd499401 100644 --- a/k8s/charts/seaweedfs/templates/s3/s3-deployment.yaml +++ b/k8s/charts/seaweedfs/templates/s3/s3-deployment.yaml @@ -157,6 +157,7 @@ spec: {{ . }} \ {{- end }} volumeMounts: + {{- include "seaweedfs.tmpDirVolumeMount" (list . .Values.s3.containerSecurityContext (tpl (.Values.s3.extraVolumeMounts | default "") .) (tpl (.Values.s3.extraVolumes | default "") .)) | nindent 12 }} {{- if or (eq .Values.s3.logs.type "hostPath") (eq .Values.s3.logs.type "emptyDir") }} - name: logs mountPath: "/logs/" @@ -245,6 +246,7 @@ spec: {{- include "seaweedfs.tplvalues.render" (dict "value" .Values.s3.sidecars "context" $) | nindent 8 }} {{- end }} volumes: + {{- include "seaweedfs.tmpDirVolume" (list . .Values.s3.containerSecurityContext (tpl (.Values.s3.extraVolumeMounts | default "") .) (tpl (.Values.s3.extraVolumes | default "") .) false) | nindent 8 }} {{- if .Values.s3.enableAuth }} - name: config-users secret: diff --git a/k8s/charts/seaweedfs/templates/sftp/sftp-deployment.yaml b/k8s/charts/seaweedfs/templates/sftp/sftp-deployment.yaml index 17e29bdbe..dd75336ca 100644 --- a/k8s/charts/seaweedfs/templates/sftp/sftp-deployment.yaml +++ b/k8s/charts/seaweedfs/templates/sftp/sftp-deployment.yaml @@ -170,6 +170,7 @@ spec: -userStoreFile=/etc/sw/seaweedfs_sftp_config \ -filer={{ include "seaweedfs.componentName" (list . "filer-client") }}.{{ .Release.Namespace }}:{{ .Values.filer.port }} volumeMounts: + {{- include "seaweedfs.tmpDirVolumeMount" (list . .Values.sftp.containerSecurityContext (tpl (.Values.sftp.extraVolumeMounts | default "") .) (tpl (.Values.sftp.extraVolumes | default "") .)) | nindent 12 }} {{- if or (eq .Values.sftp.logs.type "hostPath") (eq .Values.sftp.logs.type "emptyDir") }} - name: logs mountPath: "/logs/" @@ -249,6 +250,7 @@ spec: {{- include "seaweedfs.tplvalues.render" (dict "value" .Values.sftp.sidecars "context" $) | nindent 8 }} {{- end }} volumes: + {{- include "seaweedfs.tmpDirVolume" (list . .Values.sftp.containerSecurityContext (tpl (.Values.sftp.extraVolumeMounts | default "") .) (tpl (.Values.sftp.extraVolumes | default "") .) false) | nindent 8 }} {{- if .Values.sftp.enableAuth }} - name: config-users secret: diff --git a/k8s/charts/seaweedfs/templates/shared/_helpers.tpl b/k8s/charts/seaweedfs/templates/shared/_helpers.tpl index 9c0315e26..86ee839d1 100644 --- a/k8s/charts/seaweedfs/templates/shared/_helpers.tpl +++ b/k8s/charts/seaweedfs/templates/shared/_helpers.tpl @@ -76,6 +76,43 @@ Inject extra environment vars in the format key:value, if populated {{- end }} {{- end -}} +{{/* +Writable temporary directory for containers using a read-only root filesystem. +Input: list of the root context, the component container security context, the +rendered extraVolumeMounts and extraVolumes, and whether the pod has secondary +chart-managed containers that mount seaweedfs-tmp. A user-supplied /tmp mount +only covers the main container, so the volume is still emitted for secondaries; +a user-supplied seaweedfs-tmp volume is reused rather than duplicated. +*/}} +{{- define "seaweedfs.tmpDirCovered" -}} +{{- regexMatch `(?m)^\s*-?\s*mountPath:\s*['"]?/tmp/?['"]?\s*(#.*)?$` (index . 2) -}} +{{- end -}} + +{{- define "seaweedfs.tmpDirVolume" -}} +{{- $root := index . 0 -}} +{{- $securityContext := index . 1 -}} +{{- if and $securityContext.enabled $securityContext.readOnlyRootFilesystem + (or (index . 4) (ne (include "seaweedfs.tmpDirCovered" .) "true")) + (not (regexMatch `(?m)^\s*-?\s*name:\s*['"]?seaweedfs-tmp['"]?\s*(#.*)?$` (index . 3))) }} +- name: seaweedfs-tmp + {{- with $root.Values.global.seaweedfs.tmpDir.sizeLimit }} + emptyDir: + sizeLimit: {{ . | quote }} + {{- else }} + emptyDir: {} + {{- end }} +{{- end }} +{{- end -}} + +{{- define "seaweedfs.tmpDirVolumeMount" -}} +{{- $securityContext := index . 1 -}} +{{- if and $securityContext.enabled $securityContext.readOnlyRootFilesystem + (ne (include "seaweedfs.tmpDirCovered" .) "true") }} +- name: seaweedfs-tmp + mountPath: /tmp +{{- end }} +{{- end -}} + {{/* Whether the mysql filer store is selected; a flag the chart cannot read counts as selected. */}} {{- define "seaweedfs.filer.mysqlEnabled" -}} {{- $merged := dict -}} diff --git a/k8s/charts/seaweedfs/templates/shared/post-install-bucket-hook.yaml b/k8s/charts/seaweedfs/templates/shared/post-install-bucket-hook.yaml index 5868c1c54..756590a69 100644 --- a/k8s/charts/seaweedfs/templates/shared/post-install-bucket-hook.yaml +++ b/k8s/charts/seaweedfs/templates/shared/post-install-bucket-hook.yaml @@ -16,6 +16,12 @@ {{- $clusterUpper := upper $clusterAlias }} {{- $clusterMasterKey := printf "WEED_CLUSTER_%s_MASTER" $clusterUpper }} {{- $clusterFilerKey := printf "WEED_CLUSTER_%s_FILER" $clusterUpper }} +{{- $podSecurityContext := .Values.filer.podSecurityContext }} +{{- $containerSecurityContext := .Values.filer.containerSecurityContext }} +{{- if .Values.allInOne.enabled }} + {{- $podSecurityContext = .Values.allInOne.podSecurityContext }} + {{- $containerSecurityContext = .Values.allInOne.containerSecurityContext }} +{{- end }} {{- /* Check allInOne mode first */}} {{- if .Values.allInOne.enabled }} @@ -71,8 +77,8 @@ spec: app.kubernetes.io/component: bucket-hook spec: restartPolicy: Never - {{- if .Values.filer.podSecurityContext.enabled }} - securityContext: {{- omit .Values.filer.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- if $podSecurityContext.enabled }} + securityContext: {{- omit $podSecurityContext "enabled" | toYaml | nindent 8 }} {{- end }} {{- include "seaweedfs.imagePullSecrets" $ | nindent 6 }} containers: @@ -202,8 +208,9 @@ spec: /usr/bin/weed shell {{- end }} {{- end }} - {{- if or $enableAuth (include "seaweedfs.securityConfigEnabled" .) }} + {{- if or (and $containerSecurityContext.enabled $containerSecurityContext.readOnlyRootFilesystem) $enableAuth (include "seaweedfs.securityConfigEnabled" .) }} volumeMounts: + {{- include "seaweedfs.tmpDirVolumeMount" (list . $containerSecurityContext "" "") | nindent 10 }} {{- if $enableAuth }} - name: config-users mountPath: /etc/sw @@ -229,11 +236,12 @@ spec: resources: {{- toYaml . | nindent 10 }} {{- end }} - {{- if .Values.filer.containerSecurityContext.enabled }} - securityContext: {{- omit .Values.filer.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- if $containerSecurityContext.enabled }} + securityContext: {{- omit $containerSecurityContext "enabled" | toYaml | nindent 12 }} {{- end }} - {{- if or $enableAuth (include "seaweedfs.securityConfigEnabled" .) }} + {{- if or (and $containerSecurityContext.enabled $containerSecurityContext.readOnlyRootFilesystem) $enableAuth (include "seaweedfs.securityConfigEnabled" .) }} volumes: + {{- include "seaweedfs.tmpDirVolume" (list . $containerSecurityContext "" "" false) | nindent 8 }} {{- if $enableAuth }} - name: config-users secret: diff --git a/k8s/charts/seaweedfs/templates/volume/volume-resize-hook.yaml b/k8s/charts/seaweedfs/templates/volume/volume-resize-hook.yaml index 161af3050..5680b7aa6 100644 --- a/k8s/charts/seaweedfs/templates/volume/volume-resize-hook.yaml +++ b/k8s/charts/seaweedfs/templates/volume/volume-resize-hook.yaml @@ -38,13 +38,26 @@ spec: containers: - name: resize image: {{ .Values.volume.resizeHook.image }} + {{- if and .Values.volume.containerSecurityContext.enabled .Values.volume.containerSecurityContext.readOnlyRootFilesystem }} + env: + - name: HOME + value: /tmp + {{- end }} command: ["sh", "-xec"] args: - | {{ $commands | indent 14 }} + {{- if and .Values.volume.containerSecurityContext.enabled .Values.volume.containerSecurityContext.readOnlyRootFilesystem }} + volumeMounts: + {{- include "seaweedfs.tmpDirVolumeMount" (list . .Values.volume.containerSecurityContext "" "") | nindent 12 }} + {{- end }} {{- if .Values.volume.containerSecurityContext.enabled }} securityContext: {{- omit .Values.volume.containerSecurityContext "enabled" | toYaml | nindent 12 }} {{- end }} + {{- if and .Values.volume.containerSecurityContext.enabled .Values.volume.containerSecurityContext.readOnlyRootFilesystem }} + volumes: + {{- include "seaweedfs.tmpDirVolume" (list . .Values.volume.containerSecurityContext "" "" false) | nindent 8 }} + {{- end }} --- apiVersion: v1 kind: ServiceAccount diff --git a/k8s/charts/seaweedfs/templates/volume/volume-statefulset.yaml b/k8s/charts/seaweedfs/templates/volume/volume-statefulset.yaml index 04011d1d6..4f36c7791 100644 --- a/k8s/charts/seaweedfs/templates/volume/volume-statefulset.yaml +++ b/k8s/charts/seaweedfs/templates/volume/volume-statefulset.yaml @@ -104,6 +104,7 @@ spec: fi done volumeMounts: + {{- include "seaweedfs.tmpDirVolumeMount" (list $ $volume.containerSecurityContext "" "") | nindent 12 }} - name: idx mountPath: /idx {{- range $dir := $volume.dataDirs }} @@ -224,6 +225,7 @@ spec: {{ . }} \ {{- end }} volumeMounts: + {{- include "seaweedfs.tmpDirVolumeMount" (list $ $volume.containerSecurityContext (tpl (printf "{{ $volumeName := \"%s\" }}%s" $volumeName ($volume.extraVolumeMounts | default "")) $) (tpl ($volume.extraVolumes | default "") $)) | nindent 12 }} {{- range $dir := $volume.dataDirs }} {{- if not ( eq $dir.type "custom" ) }} - name: {{ $dir.name }} @@ -306,6 +308,7 @@ spec: {{- include "seaweedfs.tplvalues.render" (dict "value" (printf "{{ $volumeName := \"%s\" }}%s" $volumeName $volume.sidecars) "context" $) | nindent 8 }} {{- end }} volumes: + {{- include "seaweedfs.tmpDirVolume" (list $ $volume.containerSecurityContext (tpl (printf "{{ $volumeName := \"%s\" }}%s" $volumeName ($volume.extraVolumeMounts | default "")) $) (tpl ($volume.extraVolumes | default "") $) (and $initContainers_exists $volume.idx)) | nindent 8 }} {{- range $dir := $volume.dataDirs }} diff --git a/k8s/charts/seaweedfs/templates/worker/worker-deployment.yaml b/k8s/charts/seaweedfs/templates/worker/worker-deployment.yaml index f0cd12418..7607b7228 100644 --- a/k8s/charts/seaweedfs/templates/worker/worker-deployment.yaml +++ b/k8s/charts/seaweedfs/templates/worker/worker-deployment.yaml @@ -144,6 +144,7 @@ spec: {{ $arg }}{{- if lt $index (sub (len $.Values.worker.extraArgs) 1) }} \{{ end }} {{- end }} volumeMounts: + {{- include "seaweedfs.tmpDirVolumeMount" (list . .Values.worker.containerSecurityContext (tpl (.Values.worker.extraVolumeMounts | default "") .) (tpl (.Values.worker.extraVolumes | default "") .)) | nindent 12 }} {{- if or (eq .Values.worker.data.type "hostPath") (eq .Values.worker.data.type "emptyDir") (eq .Values.worker.data.type "existingClaim") }} - name: worker-data mountPath: {{ .Values.worker.workingDir }} @@ -262,15 +263,16 @@ spec: --metrics-ip=0.0.0.0 \ {{- end }} --max-concurrency={{ .Values.worker.maxExecute }} - {{- if .Values.global.seaweedfs.enableSecurity }} volumeMounts: + {{- include "seaweedfs.tmpDirVolumeMount" (list . .Values.worker.containerSecurityContext "" "") | nindent 12 }} + {{- if .Values.global.seaweedfs.enableSecurity }} - name: ca-cert readOnly: true mountPath: /usr/local/share/ca-certificates/ca/ - name: worker-cert readOnly: true mountPath: /usr/local/share/ca-certificates/worker/ - {{- end }} + {{- end }} {{- if .Values.worker.lanceMetricsPort }} ports: - containerPort: {{ .Values.worker.lanceMetricsPort }} @@ -302,6 +304,7 @@ spec: {{- include "seaweedfs.tplvalues.render" (dict "value" .Values.worker.sidecars "context" $) | nindent 8 }} {{- end }} volumes: + {{- include "seaweedfs.tmpDirVolume" (list . .Values.worker.containerSecurityContext (tpl (.Values.worker.extraVolumeMounts | default "") .) (tpl (.Values.worker.extraVolumes | default "") .) (ne (include "seaweedfs.worker.lanceNamespaceUrl" .) "")) | nindent 8 }} {{- if eq .Values.worker.data.type "hostPath" }} - name: worker-data hostPath: diff --git a/k8s/charts/seaweedfs/values.yaml b/k8s/charts/seaweedfs/values.yaml index 47d1fd103..3f7c56d07 100644 --- a/k8s/charts/seaweedfs/values.yaml +++ b/k8s/charts/seaweedfs/values.yaml @@ -25,6 +25,9 @@ global: imagePullPolicy: IfNotPresent restartPolicy: Always loggingLevel: 1 + # Writable temporary storage used when containers run with a read-only root filesystem. + tmpDir: + sizeLimit: "" enableSecurity: false masterServer: null # filerWrite: true mounts security.toml on filer + admin without needing @@ -274,6 +277,7 @@ master: # runAsNonRoot: true # privileged: false # allowPrivilegeEscalation: false + # readOnlyRootFilesystem: true # capabilities: # drop: # - ALL @@ -584,6 +588,7 @@ volume: # runAsNonRoot: true # privileged: false # allowPrivilegeEscalation: false + # readOnlyRootFilesystem: true # capabilities: # drop: # - ALL @@ -879,6 +884,7 @@ filer: # runAsNonRoot: true # privileged: false # allowPrivilegeEscalation: false + # readOnlyRootFilesystem: true # capabilities: # drop: # - ALL @@ -1171,6 +1177,7 @@ s3: # runAsNonRoot: true # privileged: false # allowPrivilegeEscalation: false + # readOnlyRootFilesystem: true # capabilities: # drop: # - ALL @@ -1348,6 +1355,7 @@ sftp: # runAsNonRoot: true # privileged: false # allowPrivilegeEscalation: false + # readOnlyRootFilesystem: true # capabilities: # drop: # - ALL @@ -1520,6 +1528,7 @@ admin: # runAsNonRoot: true # privileged: false # allowPrivilegeEscalation: false + # readOnlyRootFilesystem: true # capabilities: # drop: # - ALL @@ -1702,6 +1711,7 @@ worker: # runAsNonRoot: true # privileged: false # allowPrivilegeEscalation: false + # readOnlyRootFilesystem: true # capabilities: # drop: # - ALL @@ -1980,6 +1990,7 @@ allInOne: # runAsNonRoot: true # privileged: false # allowPrivilegeEscalation: false + # readOnlyRootFilesystem: true # capabilities: # drop: # - ALL @@ -2044,6 +2055,7 @@ cosi: # runAsNonRoot: true # privileged: false # allowPrivilegeEscalation: false + # readOnlyRootFilesystem: true # capabilities: # drop: # - ALL