diff --git a/charts/certimate/DESIGN.md b/charts/certimate/DESIGN.md index 25f8fb72..f3182bef 100644 --- a/charts/certimate/DESIGN.md +++ b/charts/certimate/DESIGN.md @@ -28,6 +28,12 @@ Disabling persistence is a breaking, explicit choice: users must set keeps disposable CI and demo installs available while preventing accidental loss of certificates, ACME accounts, provider credentials, and workflow state. +Certificate workflows create temporary processing files under `/tmp`. The chart +mounts a dedicated ephemeral `emptyDir` there so these workflows remain +functional while `securityContext.readOnlyRootFilesystem` stays enabled. Operators +can set `tmpStorage.sizeLimit` to bound the volume and apply the same value as the +container's ephemeral-storage request and limit. + ## Exposure The chart supports Kubernetes Ingress and Gateway API HTTPRoute. TLS termination is expected at the ingress controller or gateway. Certimate itself remains an internal HTTP service. diff --git a/charts/certimate/README.md b/charts/certimate/README.md index 7979d4cc..7bc87dd2 100644 --- a/charts/certimate/README.md +++ b/charts/certimate/README.md @@ -11,6 +11,8 @@ This chart packages the official `certimate/certimate:v0.4.30` image and follows - explicit `persistence.ephemeral=true` opt-in for disposable emptyDir installs - default resource requests and memory limit for scheduler and Kubescape hygiene - restricted ServiceAccount token mounting by default +- ephemeral writable `/tmp` storage while the container root filesystem remains read-only +- optional `tmpStorage.sizeLimit` with matching ephemeral-storage request and limit - Kubernetes Ingress and Gateway API HTTPRoute support - optional NetworkPolicy with explicit additional egress for ACME DNS APIs, DNS, SMTP, webhooks, and target deployment systems - ExternalSecret support for environment variables or other integration secrets diff --git a/charts/certimate/templates/deployment.yaml b/charts/certimate/templates/deployment.yaml index 331af9b2..a0fa3403 100644 --- a/charts/certimate/templates/deployment.yaml +++ b/charts/certimate/templates/deployment.yaml @@ -1,4 +1,24 @@ {{/* SPDX-License-Identifier: Apache-2.0 */}} +{{- if eq .Values.persistence.mountPath "/tmp" }} +{{- fail "persistence.mountPath must not use the reserved mountPath /tmp" }} +{{- end }} +{{- range .Values.extraVolumeMounts }} +{{- if eq (get . "mountPath") "/tmp" }} +{{- fail "extraVolumeMounts must not use the reserved mountPath /tmp" }} +{{- end }} +{{- end }} +{{- range .Values.extraVolumes }} +{{- if eq (get . "name") "tmp" }} +{{- fail "extraVolumes must not use the reserved volume name tmp" }} +{{- end }} +{{- end }} +{{- $resources := deepCopy .Values.resources }} +{{- if .Values.tmpStorage.sizeLimit }} +{{- if not (hasKey $resources "requests") }}{{- $_ := set $resources "requests" dict }}{{- end }} +{{- if not (hasKey $resources "limits") }}{{- $_ := set $resources "limits" dict }}{{- end }} +{{- $_ := set (get $resources "requests") "ephemeral-storage" .Values.tmpStorage.sizeLimit }} +{{- $_ := set (get $resources "limits") "ephemeral-storage" .Values.tmpStorage.sizeLimit }} +{{- end }} apiVersion: apps/v1 kind: Deployment metadata: @@ -105,13 +125,16 @@ spec: timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }} failureThreshold: {{ .Values.probes.readiness.failureThreshold }} {{- end }} - {{- with .Values.resources }} + {{- with $resources }} resources: {{- toYaml . | nindent 12 }} {{- end }} volumeMounts: - name: data mountPath: {{ .Values.persistence.mountPath | quote }} + - name: tmp + mountPath: /tmp + readOnly: false {{- with .Values.extraVolumeMounts }} {{- toYaml . | nindent 12 }} {{- end }} @@ -126,6 +149,13 @@ spec: {{- else }} emptyDir: {} {{- end }} + - name: tmp + {{- if .Values.tmpStorage.sizeLimit }} + emptyDir: + sizeLimit: {{ .Values.tmpStorage.sizeLimit | quote }} + {{- else }} + emptyDir: {} + {{- end }} {{- with .Values.extraVolumes }} {{- toYaml . | nindent 8 }} {{- end }} diff --git a/charts/certimate/tests/templates_test.yaml b/charts/certimate/tests/templates_test.yaml index e418814d..f20afb63 100644 --- a/charts/certimate/tests/templates_test.yaml +++ b/charts/certimate/tests/templates_test.yaml @@ -96,3 +96,60 @@ tests: - equal: path: spec.template.spec.containers[0].resources.limits.cpu value: 500m + - it: provides writable temporary storage with a read-only root filesystem + template: templates/deployment.yaml + asserts: + - equal: + path: spec.template.spec.containers[0].securityContext.readOnlyRootFilesystem + value: true + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: tmp + mountPath: /tmp + readOnly: false + - equal: + path: spec.template.spec.volumes[1].name + value: tmp + - equal: + path: spec.template.spec.volumes[1].emptyDir + value: {} + - it: rejects an extra volume mount that duplicates the reserved tmp path + template: templates/deployment.yaml + set: + extraVolumeMounts: + - name: custom-tmp + mountPath: /tmp + asserts: + - failedTemplate: + errorMessage: extraVolumeMounts must not use the reserved mountPath /tmp + - it: rejects persistence mounted at the reserved tmp path + template: templates/deployment.yaml + set: + persistence.mountPath: /tmp + asserts: + - failedTemplate: + errorMessage: persistence.mountPath must not use the reserved mountPath /tmp + - it: rejects an extra volume using the reserved tmp name + template: templates/deployment.yaml + set: + extraVolumes: + - name: tmp + emptyDir: {} + asserts: + - failedTemplate: + errorMessage: extraVolumes must not use the reserved volume name tmp + - it: bounds temporary storage when configured + template: templates/deployment.yaml + set: + tmpStorage.sizeLimit: 1Gi + asserts: + - equal: + path: spec.template.spec.volumes[1].emptyDir.sizeLimit + value: 1Gi + - equal: + path: spec.template.spec.containers[0].resources.requests.ephemeral-storage + value: 1Gi + - equal: + path: spec.template.spec.containers[0].resources.limits.ephemeral-storage + value: 1Gi diff --git a/charts/certimate/values.schema.json b/charts/certimate/values.schema.json index c35a798f..c8699258 100644 --- a/charts/certimate/values.schema.json +++ b/charts/certimate/values.schema.json @@ -59,6 +59,13 @@ "externalSecrets": { "$ref": "#/definitions/externalSecrets" }, "probes": { "$ref": "#/definitions/probes" }, "resources": { "type": "object" }, + "tmpStorage": { + "type": "object", + "required": ["sizeLimit"], + "properties": { + "sizeLimit": { "type": "string", "default": "", "pattern": "^$|^[0-9]+(\\.[0-9]+)?(Ei|Pi|Ti|Gi|Mi|Ki|E|P|T|G|M|k)?$" } + } + }, "test": { "type": "object", "properties": { diff --git a/charts/certimate/values.yaml b/charts/certimate/values.yaml index 797dfd02..cca08110 100644 --- a/charts/certimate/values.yaml +++ b/charts/certimate/values.yaml @@ -136,6 +136,10 @@ resources: cpu: 500m memory: 512Mi +tmpStorage: + # -- Optional limit for writable /tmp storage. When set, the same value is applied to the container ephemeral-storage request and limit. + sizeLimit: "" + test: resources: requests: