Merge pull request #52 from jk-cluster/feat/cwa-conventions

Calibre-Web-Automated chart: naming scheme, security hardening (conventions batch 3)
This commit is contained in:
jklein authored and GitHub committed 2026-08-22 16:52:16 +00:00
commit 193f38c283
10 files changed
+99 -23

No files matched your search

+1 -1
View File
@@ -5,7 +5,7 @@ name: calibre-web-automated
description: A Helm chart to deploy calibere-web-automated
icon: https://raw.githubusercontent.com/crocodilestick/Calibre-Web-Automated/main/README_images/cwa-logo-round-dark.png
version: 3.1.1+upv4.0.6
version: 4.0.0+upv4.0.6
appVersion: v4.0.6
maintainers:
@@ -1,6 +1,9 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
# Deliberate naming exception (see README): this PVC keeps its historical
# <chart>-<release>-cwa-consume-pvc name — renaming a PVC would provision
# a new, empty volume and orphan the existing data.
name: {{ .Chart.Name }}-{{ .Release.Name }}-cwa-consume-pvc
spec:
accessModes:
@@ -1,20 +1,23 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Chart.Name }}-{{ .Release.Name }}-cwa-ingress
name: {{ .Release.Name }}-{{ .Chart.Name }}-ingress
annotations:
cert-manager.io/cluster-issuer: {{ required "A Cluster-Issuer must be provided" .Values.ingress.clusterIssuer }}
nginx.ingress.kubernetes.io/proxy-body-size: {{ .Values.cwa.maxUploadFileSize }}
nginx.org/client-max-body-size: {{ .Values.cwa.maxUploadFileSize }}
{{- with .Values.ingress.annotations }}
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
ingressClassName: nginx
ingressClassName: {{ .Values.ingress.className }}
rules:
- host: {{ required "A Domain/Host must be provided" .Values.ingress.domain }}
http:
paths:
- backend:
service:
name: {{ .Chart.Name }}-{{ .Release.Name }}-cwa-service
name: {{ .Release.Name }}-{{ .Chart.Name }}-service
port:
name: http
path: /
@@ -22,4 +25,4 @@ spec:
tls:
- hosts:
- {{ required "A Domain/Host must be provided" .Values.ingress.domain }}
secretName: tls-{{ .Chart.Name }}-{{ .Release.Name }}-ingress
secretName: tls-{{ .Release.Name }}-{{ .Chart.Name }}-ingress
@@ -1,6 +1,9 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
# Deliberate naming exception (see README): this PVC keeps its historical
# <chart>-<release>-cwa-pvc name — renaming a PVC would provision a new,
# empty volume and orphan the existing data.
name: {{ .Chart.Name }}-{{ .Release.Name }}-cwa-pvc
spec:
accessModes:
@@ -1,6 +1,6 @@
apiVersion: onepassword.com/v1
kind: OnePasswordItem
metadata:
name: {{ .Chart.Name }}-{{ .Release.Name }}-cwa-secret
name: {{ .Release.Name }}-{{ .Chart.Name }}-secret
spec:
itemPath: {{ .Values.secrets.cwaSecretRef }}
@@ -1,10 +1,10 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}-{{ .Release.Name }}-cwa-service
name: {{ .Release.Name }}-{{ .Chart.Name }}-service
spec:
selector:
app: {{ .Chart.Name }}-{{ .Release.Name }}-cwa-sfs
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
ports:
- protocol: TCP
port: 80
@@ -1,23 +1,26 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ .Chart.Name }}-{{ .Release.Name }}-cwa-sfs
name: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
labels:
app: {{ .Chart.Name }}-{{ .Release.Name }}
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
spec:
serviceName: {{ .Chart.Name }}-{{ .Release.Name }}-cwa-service
serviceName: {{ .Release.Name }}-{{ .Chart.Name }}-service
replicas: {{ include "calibre-web-automated.replicas" (dict "scaleDown" .Values.scaleDown "replicas" .Values.cwa.replicas) }}
selector:
matchLabels:
app: {{ .Chart.Name }}-{{ .Release.Name }}-cwa-sfs
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
template:
metadata:
name: {{ .Chart.Name }}-{{ .Release.Name }}-cwa-sfs
name: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
labels:
app: {{ .Chart.Name }}-{{ .Release.Name }}-cwa-sfs
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
spec:
automountServiceAccountToken: false
securityContext:
{{- toYaml .Values.cwa.securityContext.pod | nindent 8 }}
containers:
- name: {{ .Chart.Name }}-{{ .Release.Name }}-cwa-container
- name: {{ .Release.Name }}-{{ .Chart.Name }}-container
image: "ghcr.io/crocodilestick/calibre-web-automated:{{ .Chart.AppVersion }}"
imagePullPolicy: IfNotPresent
ports:
@@ -34,12 +37,32 @@ spec:
- name: HARDCOVER_TOKEN
valueFrom:
secretKeyRef:
name: {{ .Chart.Name }}-{{ .Release.Name }}-cwa-secret
name: {{ .Release.Name }}-{{ .Chart.Name }}-secret
key: hardcover-api-token
- name: CALIBRE_CONFIG_DIR
value: "/config/.config/calibre"
- name: NETWORK_SHARE_MODE
value: "false"
{{- range $value := .Values.cwa.env }}
{{- if $value.value }}
- name: {{ $value.name | quote }}
value: {{ tpl $value.value $ | quote }}
{{- else if $value.valueFrom }}
{{- if $value.valueFrom.secretKeyRef }}
- name: {{ $value.name }}
valueFrom:
secretKeyRef:
name: {{ tpl $value.valueFrom.secretKeyRef.name $ | quote }}
key: {{ tpl $value.valueFrom.secretKeyRef.key $ | quote }}
{{- else if $value.valueFrom.configMapKeyRef }}
- name: {{ $value.name }}
valueFrom:
configMapKeyRef:
name: {{ tpl $value.valueFrom.configMapKeyRef.name $ | quote }}
key: {{ tpl $value.valueFrom.configMapKeyRef.key $ | quote }}
{{- end }}
{{- end }}
{{- end }}
volumeMounts:
- mountPath: "/config"
name: cwa
@@ -63,12 +86,13 @@ spec:
{{- toYaml .Values.cwa.startupProbe | nindent 12 }}
resources:
{{- include "calibre-web-automated.resources" .Values.cwa.resources | nindent 12 }}
automountServiceAccountToken: false
securityContext:
fsGroup: 1000
fsGroupChangePolicy: Always
{{- toYaml .Values.cwa.securityContext.container | nindent 12 }}
restartPolicy: Always
volumes:
# Deliberate naming exception (see README): both PVCs keep their
# historical <chart>-<release>-cwa-* names — renaming a PVC would
# provision a new, empty volume and orphan the existing data.
- name: cwa
persistentVolumeClaim:
claimName: {{ .Chart.Name }}-{{ .Release.Name }}-cwa-pvc
@@ -77,7 +101,7 @@ spec:
claimName: {{ .Chart.Name }}-{{ .Release.Name }}-cwa-consume-pvc
- name: cwa-plugins-config
secret:
secretName: {{ .Chart.Name }}-{{ .Release.Name }}-cwa-secret
secretName: {{ .Release.Name }}-{{ .Chart.Name }}-secret
items:
- key: calibre-plugins
path: customize.py.json
+41 -1
View File
@@ -46,7 +46,47 @@ cwa:
cpu: 0.1
memory: 512Mi
ephemeralStorage: 100Mi
# Documented deviation from the repo-wide hardening standard: the CWA image
# (linuxserver.io-style) must start as root — it boots via the s6-overlay
# init system, whose stage-1/2 setup runs as uid 0 and then drops the app
# to the PUID/PGID user (1000), so runAsNonRoot/runAsUser are deliberately
# not set (s6 supervisors stay root, the application runs as uid 1000).
# readOnlyRootFilesystem is deliberately false: in read-only mode the image
# disables the PUID/PGID remapping (the runtime uid would silently change
# from 1000 to the built-in 911 and break existing volume data) and its
# init fails to write the ImageMagick policy under /etc. Everything else
# is hardened: no privilege escalation and all capabilities dropped except
# the minimal set the s6 init/supervision needs (verified against v4.0.6):
# CHOWN/DAC_OVERRIDE/FOWNER/SETGID/SETUID for the ownership fix-up of
# /config etc. and the root->PUID drop, KILL so the root supervisor can
# signal the uid-1000 services (without it the pod cannot shut down
# cleanly and gets SIGKILLed).
securityContext:
pod:
fsGroup: 1000
fsGroupChangePolicy: Always
container:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: false
capabilities:
drop:
- ALL
add:
- CHOWN
- DAC_OVERRIDE
- FOWNER
- KILL
- SETGID
- SETUID
# Extra environment variables, appended to the ones the chart always sets.
# Rendered through tpl; value, secretKeyRef and configMapKeyRef forms are
# supported (see template-chart).
env: []
ingress:
clusterIssuer: "letsencrypt-issuer"
clusterIssuer: null
domain: null
className: nginx
# Extra annotations for the Ingress; merged with the cert-manager
# cluster-issuer and proxy-body-size annotations the chart always sets.
annotations: {}
+4 -2
View File
@@ -1,7 +1,9 @@
# Fixtures for the calibre-web-automated install-test: the cwa secret the
# 1Password operator would otherwise materialize and static hostPath PVs
# (the consume PVC is ReadWriteMany, which local-path cannot provision).
# This chart names resources <chart>-<release>-..., release name is ci.
# Release name is ci; resources are named <release>-<chart>-..., except the
# PVCs, which keep their historical <chart>-<release>-cwa-* names
# (documented naming exception, see README).
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
@@ -74,7 +76,7 @@ spec:
apiVersion: v1
kind: Secret
metadata:
name: calibre-web-automated-ci-cwa-secret
name: ci-calibre-web-automated-secret
stringData:
hardcover-api-token: ci-dummy-token
# Mounted as /config/.config/calibre/customize.py.json (calibre plugin
@@ -9,4 +9,5 @@ cwa:
storage: 2Gi
ingress:
clusterIssuer: ci
domain: cwa.ci.local