From 9e9ff531833f83849899a67d3112d1dff0a75120 Mon Sep 17 00:00:00 2001 From: Aravinda-HWK Date: Thu, 2 Apr 2026 09:13:22 +0530 Subject: [PATCH 1/9] Create Helm subchart for OpenDKIM service with configuration and templates --- .../charts/opendkim/templates/pvc-keys.yaml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 charts/silver/charts/opendkim/templates/pvc-keys.yaml diff --git a/charts/silver/charts/opendkim/templates/pvc-keys.yaml b/charts/silver/charts/opendkim/templates/pvc-keys.yaml new file mode 100644 index 0000000..fdbd204 --- /dev/null +++ b/charts/silver/charts/opendkim/templates/pvc-keys.yaml @@ -0,0 +1,17 @@ +{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "opendkim.fullname" . }}-keys + labels: + {{- include "opendkim.labels" . | nindent 4 }} +spec: + accessModes: + {{- toYaml .Values.persistence.accessModes | nindent 4 }} + resources: + requests: + storage: {{ .Values.persistence.size }} + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass }} + {{- end }} +{{- end }} From ed4fccca81758f6f1db8bfe357bac02c356c495b Mon Sep 17 00:00:00 2001 From: Aravinda-HWK Date: Thu, 2 Apr 2026 10:18:44 +0530 Subject: [PATCH 2/9] Refactor OpenDKIM Helm chart: remove PVC template and adjust workload.yaml for persistence handling --- .../charts/opendkim/templates/pvc-keys.yaml | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 charts/silver/charts/opendkim/templates/pvc-keys.yaml diff --git a/charts/silver/charts/opendkim/templates/pvc-keys.yaml b/charts/silver/charts/opendkim/templates/pvc-keys.yaml deleted file mode 100644 index fdbd204..0000000 --- a/charts/silver/charts/opendkim/templates/pvc-keys.yaml +++ /dev/null @@ -1,17 +0,0 @@ -{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: {{ include "opendkim.fullname" . }}-keys - labels: - {{- include "opendkim.labels" . | nindent 4 }} -spec: - accessModes: - {{- toYaml .Values.persistence.accessModes | nindent 4 }} - resources: - requests: - storage: {{ .Values.persistence.size }} - {{- if .Values.persistence.storageClass }} - storageClassName: {{ .Values.persistence.storageClass }} - {{- end }} -{{- end }} From 103fb91c1e031d3dad74766e4856f37feb6cb8d8 Mon Sep 17 00:00:00 2001 From: Aravinda-HWK Date: Thu, 2 Apr 2026 15:40:10 +0530 Subject: [PATCH 3/9] feat: Add Redis and Rspamd Helm charts with templates and configurations - Introduced Redis chart with service, statefulset, and service account templates. - Added Rspamd chart including service, statefulset, config maps, and network policies. - Implemented liveness and readiness probes for both Redis and Rspamd. - Configured persistence for Redis and Rspamd with volume claim templates. - Created helper templates for both charts to manage naming and labels. - Added README files for both charts with installation and configuration instructions. - Established dependencies between Rspamd and Redis, Unbound, and ClamAV. --- charts/silver/Chart.yaml | 12 ++ charts/silver/charts/redis/Chart.yaml | 12 ++ charts/silver/charts/redis/README.md | 48 +++++ .../charts/redis/templates/_helpers.tpl | 60 ++++++ .../charts/redis/templates/service.yaml | 19 ++ .../redis/templates/serviceaccount.yaml | 12 ++ .../charts/redis/templates/statefulset.yaml | 71 +++++++ charts/silver/charts/redis/values.yaml | 73 ++++++++ charts/silver/charts/rspamd/Chart.yaml | 14 ++ charts/silver/charts/rspamd/README.md | 108 +++++++++++ .../charts/rspamd/templates/_helpers.tpl | 60 ++++++ .../rspamd/templates/configmap-options.yaml | 17 ++ .../rspamd/templates/configmap-rspamd.yaml | 40 ++++ .../rspamd/templates/networkpolicy.yaml | 55 ++++++ .../charts/rspamd/templates/secret-webui.yaml | 11 ++ .../charts/rspamd/templates/service.yaml | 25 +++ .../rspamd/templates/serviceaccount.yaml | 6 + .../charts/rspamd/templates/statefulset.yaml | 174 ++++++++++++++++++ .../rspamd/templates/tests/test-rspamd.yaml | 36 ++++ charts/silver/charts/rspamd/values.yaml | 108 +++++++++++ charts/silver/charts/unbound/Chart.yaml | 13 ++ charts/silver/charts/unbound/README.md | 48 +++++ .../charts/unbound/templates/_helpers.tpl | 60 ++++++ .../charts/unbound/templates/configmap.yaml | 11 ++ .../charts/unbound/templates/service.yaml | 23 +++ .../unbound/templates/serviceaccount.yaml | 12 ++ .../charts/unbound/templates/statefulset.yaml | 74 ++++++++ charts/silver/charts/unbound/values.yaml | 73 ++++++++ charts/silver/values.yaml | 9 + 29 files changed, 1284 insertions(+) create mode 100644 charts/silver/charts/redis/Chart.yaml create mode 100644 charts/silver/charts/redis/README.md create mode 100644 charts/silver/charts/redis/templates/_helpers.tpl create mode 100644 charts/silver/charts/redis/templates/service.yaml create mode 100644 charts/silver/charts/redis/templates/serviceaccount.yaml create mode 100644 charts/silver/charts/redis/templates/statefulset.yaml create mode 100644 charts/silver/charts/redis/values.yaml create mode 100644 charts/silver/charts/rspamd/Chart.yaml create mode 100644 charts/silver/charts/rspamd/README.md create mode 100644 charts/silver/charts/rspamd/templates/_helpers.tpl create mode 100644 charts/silver/charts/rspamd/templates/configmap-options.yaml create mode 100644 charts/silver/charts/rspamd/templates/configmap-rspamd.yaml create mode 100644 charts/silver/charts/rspamd/templates/networkpolicy.yaml create mode 100644 charts/silver/charts/rspamd/templates/secret-webui.yaml create mode 100644 charts/silver/charts/rspamd/templates/service.yaml create mode 100644 charts/silver/charts/rspamd/templates/serviceaccount.yaml create mode 100644 charts/silver/charts/rspamd/templates/statefulset.yaml create mode 100644 charts/silver/charts/rspamd/templates/tests/test-rspamd.yaml create mode 100644 charts/silver/charts/rspamd/values.yaml create mode 100644 charts/silver/charts/unbound/Chart.yaml create mode 100644 charts/silver/charts/unbound/README.md create mode 100644 charts/silver/charts/unbound/templates/_helpers.tpl create mode 100644 charts/silver/charts/unbound/templates/configmap.yaml create mode 100644 charts/silver/charts/unbound/templates/service.yaml create mode 100644 charts/silver/charts/unbound/templates/serviceaccount.yaml create mode 100644 charts/silver/charts/unbound/templates/statefulset.yaml create mode 100644 charts/silver/charts/unbound/values.yaml diff --git a/charts/silver/Chart.yaml b/charts/silver/Chart.yaml index 5e7fe5a..0aaf3da 100644 --- a/charts/silver/Chart.yaml +++ b/charts/silver/Chart.yaml @@ -10,3 +10,15 @@ dependencies: version: 0.1.0 repository: file://charts/opendkim condition: opendkim.enabled + - name: redis + version: 0.1.0 + repository: file://charts/redis + condition: redis.enabled + - name: unbound + version: 0.1.0 + repository: file://charts/unbound + condition: unbound.enabled + - name: rspamd + version: 0.1.0 + repository: file://charts/rspamd + condition: rspamd.enabled diff --git a/charts/silver/charts/redis/Chart.yaml b/charts/silver/charts/redis/Chart.yaml new file mode 100644 index 0000000..a3650f4 --- /dev/null +++ b/charts/silver/charts/redis/Chart.yaml @@ -0,0 +1,12 @@ +apiVersion: v2 +name: redis +description: Redis in-memory data store for rspamd backend +type: application +version: 0.1.0 +appVersion: "7-alpine" +keywords: + - redis + - cache + - rspamd +maintainers: + - name: Silver Team diff --git a/charts/silver/charts/redis/README.md b/charts/silver/charts/redis/README.md new file mode 100644 index 0000000..679e993 --- /dev/null +++ b/charts/silver/charts/redis/README.md @@ -0,0 +1,48 @@ +# Redis Helm Chart + +Redis in-memory data store for rspamd backend storage. + +## Installation + +```bash +helm install redis ./redis \ + --set persistence.enabled=true \ + --set persistence.size=1Gi +``` + +## Configuration + +Key values: + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| `replicaCount` | int | `1` | Number of replicas (always 1 for StatefulSet) | +| `image.repository` | string | `redis` | Image repository | +| `image.tag` | string | `7-alpine` | Image tag | +| `persistence.enabled` | bool | `true` | Enable persistent volume | +| `persistence.size` | string | `1Gi` | Persistent volume size | +| `service.port` | int | `6379` | Service port | +| `securityContext.fsGroup` | int | `999` | Redis user UID | + +## Usage + +Access Redis within the cluster: + +```bash +redis-cli -h -redis -p 6379 +``` + +For port-forward: + +```bash +kubectl port-forward svc/-redis 6379:6379 +redis-cli -h localhost -p 6379 +``` + +## Persistence + +Redis data is stored in `/data` via `volumeClaimTemplates`. The PVC persists across pod restarts. + +## Dependencies + +Redis chart has no external dependencies. diff --git a/charts/silver/charts/redis/templates/_helpers.tpl b/charts/silver/charts/redis/templates/_helpers.tpl new file mode 100644 index 0000000..ee211df --- /dev/null +++ b/charts/silver/charts/redis/templates/_helpers.tpl @@ -0,0 +1,60 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "redis.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "redis.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "redis.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "redis.labels" -}} +helm.sh/chart: {{ include "redis.chart" . }} +{{ include "redis.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "redis.selectorLabels" -}} +app.kubernetes.io/name: {{ include "redis.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "redis.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "redis.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/charts/silver/charts/redis/templates/service.yaml b/charts/silver/charts/redis/templates/service.yaml new file mode 100644 index 0000000..e236d25 --- /dev/null +++ b/charts/silver/charts/redis/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "redis.fullname" . }} + labels: + {{- include "redis.labels" . | nindent 4 }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: {{ .Values.service.targetPort }} + protocol: TCP + name: redis + selector: + {{- include "redis.selectorLabels" . | nindent 4 }} diff --git a/charts/silver/charts/redis/templates/serviceaccount.yaml b/charts/silver/charts/redis/templates/serviceaccount.yaml new file mode 100644 index 0000000..95b8003 --- /dev/null +++ b/charts/silver/charts/redis/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "redis.serviceAccountName" . }} + labels: + {{- include "redis.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/silver/charts/redis/templates/statefulset.yaml b/charts/silver/charts/redis/templates/statefulset.yaml new file mode 100644 index 0000000..73f2c36 --- /dev/null +++ b/charts/silver/charts/redis/templates/statefulset.yaml @@ -0,0 +1,71 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "redis.fullname" . }} + labels: + {{- include "redis.labels" . | nindent 4 }} +spec: + serviceName: {{ include "redis.fullname" . }} + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "redis.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "redis.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "redis.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: redis + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: redis + containerPort: 6379 + protocol: TCP + livenessProbe: + {{- toYaml .Values.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + - name: data + mountPath: {{ .Values.persistence.mountPath }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + volumeClaimTemplates: + - metadata: + name: data + spec: + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass }} + {{- end }} + accessModes: + - {{ .Values.persistence.accessMode }} + resources: + requests: + storage: {{ .Values.persistence.size }} diff --git a/charts/silver/charts/redis/values.yaml b/charts/silver/charts/redis/values.yaml new file mode 100644 index 0000000..540d154 --- /dev/null +++ b/charts/silver/charts/redis/values.yaml @@ -0,0 +1,73 @@ +replicaCount: 1 + +image: + repository: redis + tag: "7-alpine" + pullPolicy: IfNotPresent + +imagePullSecrets: [] + +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + create: true + annotations: {} + name: "" + +podAnnotations: {} + +podSecurityContext: + fsGroup: 999 + runAsUser: 999 + runAsNonRoot: true + +securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + +service: + type: ClusterIP + port: 6379 + targetPort: 6379 + annotations: {} + +resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 100m + memory: 128Mi + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +persistence: + enabled: true + storageClass: "" + accessMode: ReadWriteOnce + size: 1Gi + mountPath: /data + +livenessProbe: + tcpSocket: + port: 6379 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + +readinessProbe: + tcpSocket: + port: 6379 + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 diff --git a/charts/silver/charts/rspamd/Chart.yaml b/charts/silver/charts/rspamd/Chart.yaml new file mode 100644 index 0000000..33d701d --- /dev/null +++ b/charts/silver/charts/rspamd/Chart.yaml @@ -0,0 +1,14 @@ +apiVersion: v2 +name: rspamd +description: Rspamd spam/malware filtering engine for mail processing +type: application +version: 0.1.0 +appVersion: "latest" +keywords: + - rspamd + - spam + - filtering + - antivirus + - milter +maintainers: + - name: Silver Team diff --git a/charts/silver/charts/rspamd/README.md b/charts/silver/charts/rspamd/README.md new file mode 100644 index 0000000..3c29ade --- /dev/null +++ b/charts/silver/charts/rspamd/README.md @@ -0,0 +1,108 @@ +# Rspamd Helm Chart + +Rspamd spam/malware filtering engine with antivirus (ClamAV), Bayes classifier, and metrics exporter. + +## Dependencies + +This chart requires: +- Redis: for learning/caching backend +- Unbound: for DNS queries +- ClamAV: for virus scanning (optional but recommended) + +## Installation + +Basic installation with all dependencies: + +```bash +helm upgrade --install silver ./charts/silver \ + --set redis.enabled=true \ + --set unbound.enabled=true \ + --set rspamd.enabled=true \ + --set 'rspamd.webui.password=mypassword' +``` + +## Configuration + +Key values: + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| `replicaCount` | int | `1` | Number of replicas | +| `image.repository` | string | `rspamd/rspamd` | Image repository | +| `image.tag` | string | `latest` | Image tag | +| `persistence.enabled` | bool | `true` | Enable persistent volume for state | +| `persistence.size` | string | `1Gi` | Persistent volume size | +| `dependencies.redis.host` | string | `silver-redis` | Redis service hostname | +| `dependencies.redis.port` | int | `6379` | Redis port | +| `dependencies.unbound.host` | string | `silver-unbound` | Unbound DNS hostname | +| `dependencies.unbound.port` | int | `53` | Unbound DNS port | +| `dependencies.clamav.host` | string | `clamav-server` | ClamAV service hostname | +| `dependencies.clamav.port` | int | `3310` | ClamAV port | +| `dependencies.strictInitChecks` | bool | `true` | Fail-fast on dependency unavailability | +| `dependencies.initCheckTimeout` | string | `60s` | Init check timeout duration | +| `modules.antivirus.enabled` | bool | `true` | Enable antivirus scanning | +| `modules.antivirus.clamav_action` | string | `add_header` | Action on virus: add_header, reject, discard | +| `modules.classifier_bayes.enabled` | bool | `true` | Enable Bayes spam classifier | +| `modules.classifier_bayes.backend` | string | `redis` | Backend for classifier storage | +| `modules.metrics_exporter.enabled` | bool | `true` | Enable Prometheus metrics export | +| `service.milter.port` | int | `11332` | SMTP milter port | +| `service.webui.port` | int | `11334` | Web UI port | +| `service.webui.enabled` | bool | `true` | Enable web UI service | +| `webui.password` | string | `` | Web UI password (empty = disabled) | + +## Override Dependency Hosts + +For custom dependency endpoints: + +```bash +helm upgrade --install silver ./charts/silver \ + --set rspamd.enabled=true \ + --set 'rspamd.dependencies.redis.host=custom-redis' \ + --set 'rspamd.dependencies.unbound.host=custom-unbound' \ + --set 'rspamd.dependencies.clamav.host=external-clamav' +``` + +## Web UI Access + +Port-forward to rspamd web UI: + +```bash +kubectl port-forward -n mail svc/silver-rspamd 11334:11334 +``` + +Then access: `http://localhost:11334/` with configured password. + +## Testing + +Run Helm test: + +```bash +helm test silver -n mail +``` + +Verify milter connectivity from postfix: + +```bash +kubectl exec -n mail -it -- nc -zv silver-rspamd 11332 +``` + +## Init Checks + +If `dependencies.strictInitChecks=true`, rspamd will not start until: +- Redis is reachable on configured host:port +- Unbound DNS is responding on configured host:port + +If init checks fail, inspect pod logs: + +```bash +kubectl logs -n mail -c check-redis +kubectl logs -n mail -c check-unbound +``` + +## Persistence + +Rspamd state (learning data, UCL files, ML models) is stored in `/var/lib/rspamd` via `volumeClaimTemplates`. The PVC persists across pod restarts. + +## Scaling + +v1 supports single replica only (`replicaCount: 1`). Multi-replica support requires shared Redis + distributed learning (future scope). diff --git a/charts/silver/charts/rspamd/templates/_helpers.tpl b/charts/silver/charts/rspamd/templates/_helpers.tpl new file mode 100644 index 0000000..1c2d3f0 --- /dev/null +++ b/charts/silver/charts/rspamd/templates/_helpers.tpl @@ -0,0 +1,60 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "rspamd.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "rspamd.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "rspamd.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "rspamd.labels" -}} +helm.sh/chart: {{ include "rspamd.chart" . }} +{{ include "rspamd.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "rspamd.selectorLabels" -}} +app.kubernetes.io/name: {{ include "rspamd.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "rspamd.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "rspamd.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/charts/silver/charts/rspamd/templates/configmap-options.yaml b/charts/silver/charts/rspamd/templates/configmap-options.yaml new file mode 100644 index 0000000..d436715 --- /dev/null +++ b/charts/silver/charts/rspamd/templates/configmap-options.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "rspamd.fullname" . }}-config-options + labels: + {{- include "rspamd.labels" . | nindent 4 }} +data: + options.inc: | + dns { + nameserver = ["{{ .Values.dependencies.unbound.host }}"]; + timeout = 1s; + sockets = 16; + } + + redis.conf: | + servers = "{{ .Values.dependencies.redis.host }}:{{ .Values.dependencies.redis.port }}"; + timeout = 1s; diff --git a/charts/silver/charts/rspamd/templates/configmap-rspamd.yaml b/charts/silver/charts/rspamd/templates/configmap-rspamd.yaml new file mode 100644 index 0000000..b10b4f2 --- /dev/null +++ b/charts/silver/charts/rspamd/templates/configmap-rspamd.yaml @@ -0,0 +1,40 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "rspamd.fullname" . }}-config-static + labels: + {{- include "rspamd.labels" . | nindent 4 }} +data: + antivirus.conf: | + clamav { + type = "clamav"; + servers = "{{ .Values.dependencies.clamav.host }}:{{ .Values.dependencies.clamav.port }}"; + action = "{{ .Values.modules.antivirus.clamav_action }}"; + symbol = "CLAM_VIRUS"; + log_clean = true; + } + + classifier-bayes.conf: | + backend = "{{ .Values.modules.classifier_bayes.backend }}"; + new_schema = true; + expire = 8640000; + + autolearn { + spam_threshold = {{ .Values.modules.classifier_bayes.spam_threshold }}; + ham_threshold = {{ .Values.modules.classifier_bayes.ham_threshold }}; + check_balance = true; + } + + milter_headers.conf: | + extended_headers_enabled = true; + use_mailbox = false; + + metrics_exporter.conf: | + enabled = {{ if .Values.modules.metrics_exporter.enabled }}true{{ else }}false{{ end }}; + + worker-proxy.inc: | + bind = "*:{{ .Values.service.milter.port }}"; + protocol = "milter"; + + resolv.conf: | + nameserver {{ .Values.dependencies.unbound.host }} diff --git a/charts/silver/charts/rspamd/templates/networkpolicy.yaml b/charts/silver/charts/rspamd/templates/networkpolicy.yaml new file mode 100644 index 0000000..78c6e86 --- /dev/null +++ b/charts/silver/charts/rspamd/templates/networkpolicy.yaml @@ -0,0 +1,55 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ include "rspamd.fullname" . }} + labels: + {{- include "rspamd.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + {{- include "rspamd.selectorLabels" . | nindent 6 }} + policyTypes: + - Ingress + - Egress + ingress: + - from: + - podSelector: + matchLabels: + app.kubernetes.io/name: smtp + ports: + - protocol: TCP + port: 11332 + - from: + - namespaceSelector: {} + ports: + - protocol: TCP + port: 11334 + egress: + - to: + - podSelector: + matchLabels: + app.kubernetes.io/name: redis + ports: + - protocol: TCP + port: 6379 + - to: + - podSelector: + matchLabels: + app.kubernetes.io/name: unbound + ports: + - protocol: UDP + port: 53 + - to: + - podSelector: + matchLabels: + app.kubernetes.io/name: clamav + ports: + - protocol: TCP + port: 3310 + - to: + - namespaceSelector: {} + ports: + - protocol: UDP + port: 53 + - protocol: TCP + port: 53 diff --git a/charts/silver/charts/rspamd/templates/secret-webui.yaml b/charts/silver/charts/rspamd/templates/secret-webui.yaml new file mode 100644 index 0000000..a2c11c5 --- /dev/null +++ b/charts/silver/charts/rspamd/templates/secret-webui.yaml @@ -0,0 +1,11 @@ +{{- if .Values.webui.password }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "rspamd.fullname" . }}-webui + labels: + {{- include "rspamd.labels" . | nindent 4 }} +type: Opaque +stringData: + password: {{ .Values.webui.password }} +{{- end }} diff --git a/charts/silver/charts/rspamd/templates/service.yaml b/charts/silver/charts/rspamd/templates/service.yaml new file mode 100644 index 0000000..8ede5cb --- /dev/null +++ b/charts/silver/charts/rspamd/templates/service.yaml @@ -0,0 +1,25 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "rspamd.fullname" . }} + labels: + {{- include "rspamd.labels" . | nindent 4 }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.milter.type }} + ports: + - port: {{ .Values.service.milter.port }} + targetPort: {{ .Values.service.milter.targetPort }} + protocol: TCP + name: milter + {{- if .Values.service.webui.enabled }} + - port: {{ .Values.service.webui.port }} + targetPort: {{ .Values.service.webui.targetPort }} + protocol: TCP + name: webui + {{- end }} + selector: + {{- include "rspamd.selectorLabels" . | nindent 4 }} diff --git a/charts/silver/charts/rspamd/templates/serviceaccount.yaml b/charts/silver/charts/rspamd/templates/serviceaccount.yaml new file mode 100644 index 0000000..134bb8b --- /dev/null +++ b/charts/silver/charts/rspamd/templates/serviceaccount.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "rspamd.serviceAccountName" . }} + labels: + {{- include "rspamd.labels" . | nindent 4 }} diff --git a/charts/silver/charts/rspamd/templates/statefulset.yaml b/charts/silver/charts/rspamd/templates/statefulset.yaml new file mode 100644 index 0000000..9b22a62 --- /dev/null +++ b/charts/silver/charts/rspamd/templates/statefulset.yaml @@ -0,0 +1,174 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "rspamd.fullname" . }} + labels: + {{- include "rspamd.labels" . | nindent 4 }} +spec: + serviceName: {{ include "rspamd.fullname" . }} + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "rspamd.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + checksum/config-static: {{ include (print $.Template.BasePath "/configmap-rspamd.yaml") . | sha256sum }} + checksum/config-options: {{ include (print $.Template.BasePath "/configmap-options.yaml") . | sha256sum }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "rspamd.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "rspamd.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + {{- if .Values.dependencies.strictInitChecks }} + initContainers: + - name: check-redis + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: + - /bin/sh + - -c + - | + echo "Checking Redis connectivity at {{ .Values.dependencies.redis.host }}:{{ .Values.dependencies.redis.port }}" + timeout_sec={{ .Values.dependencies.initCheckTimeout | replace "s" "" }} + interval=5 + elapsed=0 + while [ $elapsed -lt $timeout_sec ]; do + if nc -z -w 2 {{ .Values.dependencies.redis.host }} {{ .Values.dependencies.redis.port }}; then + echo "Redis is reachable!" + exit 0 + fi + echo "Waiting for Redis... ($elapsed/$timeout_sec seconds)" + sleep $interval + elapsed=$((elapsed + interval)) + done + echo "ERROR: Redis not reachable after ${timeout_sec}s" + exit 1 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + add: + - NET_RAW + - name: check-unbound + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: + - /bin/sh + - -c + - | + echo "Checking Unbound DNS at {{ .Values.dependencies.unbound.host }}:{{ .Values.dependencies.unbound.port }}" + timeout_sec={{ .Values.dependencies.initCheckTimeout | replace "s" "" }} + interval=5 + elapsed=0 + while [ $elapsed -lt $timeout_sec ]; do + if dig @{{ .Values.dependencies.unbound.host }} -p {{ .Values.dependencies.unbound.port }} example.com +short >/dev/null 2>&1; then + echo "Unbound DNS is reachable!" + exit 0 + fi + echo "Waiting for Unbound... ($elapsed/$timeout_sec seconds)" + sleep $interval + elapsed=$((elapsed + interval)) + done + echo "ERROR: Unbound not reachable after ${timeout_sec}s" + exit 1 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + {{- end }} + containers: + - name: rspamd + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: milter + containerPort: 11332 + protocol: TCP + - name: webui + containerPort: 11334 + protocol: TCP + {{- if .Values.webui.password }} + env: + - name: RSPAMD_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "rspamd.fullname" . }}-webui + key: password + {{- end }} + livenessProbe: + {{- toYaml .Values.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + - name: config-static + mountPath: /etc/rspamd/local.d/antivirus.conf + subPath: antivirus.conf + - name: config-static + mountPath: /etc/rspamd/local.d/classifier-bayes.conf + subPath: classifier-bayes.conf + - name: config-static + mountPath: /etc/rspamd/local.d/milter_headers.conf + subPath: milter_headers.conf + - name: config-static + mountPath: /etc/rspamd/local.d/metrics_exporter.conf + subPath: metrics_exporter.conf + - name: config-static + mountPath: /etc/rspamd/local.d/worker-proxy.inc + subPath: worker-proxy.inc + - name: config-static + mountPath: /etc/resolv.conf + subPath: resolv.conf + - name: config-options + mountPath: /etc/rspamd/local.d/options.inc + subPath: options.inc + - name: config-options + mountPath: /etc/rspamd/local.d/redis.conf + subPath: redis.conf + - name: data + mountPath: {{ .Values.persistence.mountPath }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: config-static + configMap: + name: {{ include "rspamd.fullname" . }}-config-static + - name: config-options + configMap: + name: {{ include "rspamd.fullname" . }}-config-options + volumeClaimTemplates: + - metadata: + name: data + spec: + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass }} + {{- end }} + accessModes: + - {{ .Values.persistence.accessMode }} + resources: + requests: + storage: {{ .Values.persistence.size }} diff --git a/charts/silver/charts/rspamd/templates/tests/test-rspamd.yaml b/charts/silver/charts/rspamd/templates/tests/test-rspamd.yaml new file mode 100644 index 0000000..c2d2211 --- /dev/null +++ b/charts/silver/charts/rspamd/templates/tests/test-rspamd.yaml @@ -0,0 +1,36 @@ +apiVersion: v1 +kind: Pod +metadata: + name: {{ include "rspamd.fullname" . }}-test + labels: + {{- include "rspamd.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: test-connectivity + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + command: + - /bin/sh + - -c + - | + echo "Testing Rspamd milter connectivity on port 11332..." + if nc -z -w 2 {{ include "rspamd.fullname" . }} 11332; then + echo "✓ Milter port 11332 is reachable" + else + echo "✗ Milter port 11332 is NOT reachable" + exit 1 + fi + + {{- if .Values.service.webui.enabled }} + echo "Testing Rspamd Web UI connectivity on port 11334..." + if nc -z -w 2 {{ include "rspamd.fullname" . }} 11334; then + echo "✓ Web UI port 11334 is reachable" + else + echo "✗ Web UI port 11334 is NOT reachable" + exit 1 + fi + {{- end }} + + echo "All Rspamd connectivity tests passed!" + restartPolicy: Never diff --git a/charts/silver/charts/rspamd/values.yaml b/charts/silver/charts/rspamd/values.yaml new file mode 100644 index 0000000..f80aa6e --- /dev/null +++ b/charts/silver/charts/rspamd/values.yaml @@ -0,0 +1,108 @@ +replicaCount: 1 + +image: + repository: rspamd/rspamd + tag: "latest" + pullPolicy: IfNotPresent + +imagePullSecrets: [] + +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + create: true + annotations: {} + name: "" + +podAnnotations: {} + +podSecurityContext: + fsGroup: 998 + runAsUser: 998 + runAsNonRoot: true + +securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + +service: + milter: + type: ClusterIP + port: 11332 + targetPort: 11332 + webui: + type: ClusterIP + port: 11334 + targetPort: 11334 + enabled: true + annotations: {} + +resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 100m + memory: 128Mi + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +persistence: + enabled: true + storageClass: "" + accessMode: ReadWriteOnce + size: 1Gi + mountPath: /var/lib/rspamd + +dependencies: + redis: + host: silver-redis + port: 6379 + unbound: + host: silver-unbound + port: 53 + clamav: + host: clamav-server + port: 3310 + strictInitChecks: true + initCheckTimeout: 60s + +modules: + antivirus: + enabled: true + clamav_action: add_header + classifier_bayes: + enabled: true + backend: redis + spam_threshold: 12.0 + ham_threshold: -2.0 + metrics_exporter: + enabled: true + +webui: + password: "" + passwordEncoding: plaintext + +livenessProbe: + tcpSocket: + port: 11334 + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 5 + failureThreshold: 3 + +readinessProbe: + tcpSocket: + port: 11332 + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 diff --git a/charts/silver/charts/unbound/Chart.yaml b/charts/silver/charts/unbound/Chart.yaml new file mode 100644 index 0000000..46c6aaa --- /dev/null +++ b/charts/silver/charts/unbound/Chart.yaml @@ -0,0 +1,13 @@ +apiVersion: v2 +name: unbound +description: Unbound DNS resolver for rspamd DNS queries +type: application +version: 0.1.0 +appVersion: "latest" +keywords: + - dns + - unbound + - resolver + - rspamd +maintainers: + - name: Silver Team diff --git a/charts/silver/charts/unbound/README.md b/charts/silver/charts/unbound/README.md new file mode 100644 index 0000000..11342b4 --- /dev/null +++ b/charts/silver/charts/unbound/README.md @@ -0,0 +1,48 @@ +# Unbound Helm Chart + +Unbound DNS resolver for rspamd DNS queries and recursive resolution. + +## Installation + +```bash +helm install unbound ./unbound \ + --set persistence.enabled=true \ + --set persistence.size=512Mi +``` + +## Configuration + +Key values: + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| `replicaCount` | int | `1` | Number of replicas | +| `image.repository` | string | `mvance/unbound` | Image repository | +| `image.tag` | string | `latest` | Image tag | +| `persistence.enabled` | bool | `true` | Enable persistent volume for cache | +| `persistence.size` | string | `512Mi` | Cache volume size | +| `service.port` | int | `53` | Service port (UDP) | +| `configuration` | string | Full unbound.conf | Custom unbound configuration | + +## Usage + +Unbound is configured for recursive DNS resolution. Access from within cluster: + +```bash +dig @-unbound example.com +``` + +For port-forward: + +```bash +kubectl port-forward svc/-unbound 53:53/udp +dig @127.0.0.1 -p 53 example.com +``` + +## Persistence + +Cache data is stored via `volumeClaimTemplates` in `/var/lib/unbound`. The PVC persists across pod restarts. + +## Dependencies + +Unbound chart has no external dependencies. diff --git a/charts/silver/charts/unbound/templates/_helpers.tpl b/charts/silver/charts/unbound/templates/_helpers.tpl new file mode 100644 index 0000000..5f26295 --- /dev/null +++ b/charts/silver/charts/unbound/templates/_helpers.tpl @@ -0,0 +1,60 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "unbound.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "unbound.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "unbound.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "unbound.labels" -}} +helm.sh/chart: {{ include "unbound.chart" . }} +{{ include "unbound.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "unbound.selectorLabels" -}} +app.kubernetes.io/name: {{ include "unbound.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "unbound.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "unbound.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/charts/silver/charts/unbound/templates/configmap.yaml b/charts/silver/charts/unbound/templates/configmap.yaml new file mode 100644 index 0000000..6b56cc8 --- /dev/null +++ b/charts/silver/charts/unbound/templates/configmap.yaml @@ -0,0 +1,11 @@ +{{- if .Values.configuration }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "unbound.fullname" . }}-config + labels: + {{- include "unbound.labels" . | nindent 4 }} +data: + unbound.conf: | + {{- .Values.configuration | nindent 4 }} +{{- end }} diff --git a/charts/silver/charts/unbound/templates/service.yaml b/charts/silver/charts/unbound/templates/service.yaml new file mode 100644 index 0000000..b6528c2 --- /dev/null +++ b/charts/silver/charts/unbound/templates/service.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "unbound.fullname" . }} + labels: + {{- include "unbound.labels" . | nindent 4 }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - name: dns-tcp + port: {{ .Values.service.port }} + targetPort: {{ .Values.service.targetPort }} + protocol: TCP + - name: dns-udp + port: {{ .Values.service.port }} + targetPort: {{ .Values.service.targetPort }} + protocol: UDP + selector: + {{- include "unbound.selectorLabels" . | nindent 4 }} diff --git a/charts/silver/charts/unbound/templates/serviceaccount.yaml b/charts/silver/charts/unbound/templates/serviceaccount.yaml new file mode 100644 index 0000000..cb04a65 --- /dev/null +++ b/charts/silver/charts/unbound/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "unbound.serviceAccountName" . }} + labels: + {{- include "unbound.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/silver/charts/unbound/templates/statefulset.yaml b/charts/silver/charts/unbound/templates/statefulset.yaml new file mode 100644 index 0000000..74f85cd --- /dev/null +++ b/charts/silver/charts/unbound/templates/statefulset.yaml @@ -0,0 +1,74 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "unbound.fullname" . }} + labels: + {{- include "unbound.labels" . | nindent 4 }} +spec: + serviceName: {{ include "unbound.fullname" . }} + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "unbound.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "unbound.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "unbound.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: unbound + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: dns + containerPort: 53 + protocol: TCP + - name: dns-udp + containerPort: 53 + protocol: UDP + livenessProbe: + {{- toYaml .Values.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + - name: cache + mountPath: {{ .Values.persistence.mountPath }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + volumeClaimTemplates: + - metadata: + name: cache + spec: + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass }} + {{- end }} + accessModes: + - {{ .Values.persistence.accessMode }} + resources: + requests: + storage: {{ .Values.persistence.size }} diff --git a/charts/silver/charts/unbound/values.yaml b/charts/silver/charts/unbound/values.yaml new file mode 100644 index 0000000..70e6ff4 --- /dev/null +++ b/charts/silver/charts/unbound/values.yaml @@ -0,0 +1,73 @@ +replicaCount: 1 + +image: + repository: mvance/unbound + tag: "latest" + pullPolicy: IfNotPresent + +imagePullSecrets: [] + +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + create: true + annotations: {} + name: "" + +podAnnotations: {} + +podSecurityContext: + runAsUser: 0 + runAsNonRoot: false + +securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + +service: + type: ClusterIP + port: 53 + targetPort: 53 + protocol: UDP + annotations: {} + +resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 100m + memory: 64Mi + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +persistence: + enabled: false + storageClass: "" + accessMode: ReadWriteOnce + size: 512Mi + mountPath: /var/lib/unbound + +livenessProbe: + tcpSocket: + port: 53 + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 5 + failureThreshold: 3 + +readinessProbe: + tcpSocket: + port: 53 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 diff --git a/charts/silver/values.yaml b/charts/silver/values.yaml index 6522bbf..3460978 100644 --- a/charts/silver/values.yaml +++ b/charts/silver/values.yaml @@ -3,3 +3,12 @@ global: opendkim: enabled: true + +redis: + enabled: false + +unbound: + enabled: false + +rspamd: + enabled: false From 34210950190fe6a8939fc7432bbabd79ed1cec2b Mon Sep 17 00:00:00 2001 From: Aravinda-HWK Date: Thu, 2 Apr 2026 16:58:52 +0530 Subject: [PATCH 4/9] feat: Update Rspamd and Unbound Helm charts for improved connectivity checks and security context --- charts/silver/charts/rspamd/templates/statefulset.yaml | 10 ++++------ charts/silver/charts/unbound/values.yaml | 8 +++----- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/charts/silver/charts/rspamd/templates/statefulset.yaml b/charts/silver/charts/rspamd/templates/statefulset.yaml index 9b22a62..e5dcea2 100644 --- a/charts/silver/charts/rspamd/templates/statefulset.yaml +++ b/charts/silver/charts/rspamd/templates/statefulset.yaml @@ -34,7 +34,7 @@ spec: image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} command: - - /bin/sh + - /bin/bash - -c - | echo "Checking Redis connectivity at {{ .Values.dependencies.redis.host }}:{{ .Values.dependencies.redis.port }}" @@ -42,7 +42,7 @@ spec: interval=5 elapsed=0 while [ $elapsed -lt $timeout_sec ]; do - if nc -z -w 2 {{ .Values.dependencies.redis.host }} {{ .Values.dependencies.redis.port }}; then + if timeout 2 bash -c "echo > /dev/tcp/{{ .Values.dependencies.redis.host }}/{{ .Values.dependencies.redis.port }}" >/dev/null 2>&1; then echo "Redis is reachable!" exit 0 fi @@ -57,13 +57,11 @@ spec: capabilities: drop: - ALL - add: - - NET_RAW - name: check-unbound image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} command: - - /bin/sh + - /bin/bash - -c - | echo "Checking Unbound DNS at {{ .Values.dependencies.unbound.host }}:{{ .Values.dependencies.unbound.port }}" @@ -71,7 +69,7 @@ spec: interval=5 elapsed=0 while [ $elapsed -lt $timeout_sec ]; do - if dig @{{ .Values.dependencies.unbound.host }} -p {{ .Values.dependencies.unbound.port }} example.com +short >/dev/null 2>&1; then + if timeout 2 bash -c "echo > /dev/tcp/{{ .Values.dependencies.unbound.host }}/{{ .Values.dependencies.unbound.port }}" >/dev/null 2>&1; then echo "Unbound DNS is reachable!" exit 0 fi diff --git a/charts/silver/charts/unbound/values.yaml b/charts/silver/charts/unbound/values.yaml index 70e6ff4..f732078 100644 --- a/charts/silver/charts/unbound/values.yaml +++ b/charts/silver/charts/unbound/values.yaml @@ -22,11 +22,9 @@ podSecurityContext: runAsNonRoot: false securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - readOnlyRootFilesystem: false + privileged: true + allowPrivilegeEscalation: true + runAsUser: 0 service: type: ClusterIP From 873c7af728b823c4cd4ee476cc2448edd6905a7f Mon Sep 17 00:00:00 2001 From: "H.W.K.Aravinda" <118424430+Aravinda-HWK@users.noreply.github.com> Date: Fri, 3 Apr 2026 09:35:25 +0530 Subject: [PATCH 5/9] Create Helm subchart for OpenDKIM service with configuration and templates (#320) * Create Helm subchart for OpenDKIM service with configuration and templates * Add detailed local testing instructions for OpenDKIM chart * Refactor OpenDKIM Helm chart: remove PVC template and adjust workload.yaml for persistence handling --- charts/silver/charts/clamav/Chart.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 charts/silver/charts/clamav/Chart.yaml diff --git a/charts/silver/charts/clamav/Chart.yaml b/charts/silver/charts/clamav/Chart.yaml new file mode 100644 index 0000000..e69de29 From 007b08ae5cce9be1333a33187c257a148636ab31 Mon Sep 17 00:00:00 2001 From: Aravinda-HWK Date: Thu, 2 Apr 2026 09:13:22 +0530 Subject: [PATCH 6/9] Create Helm subchart for OpenDKIM service with configuration and templates --- .../charts/opendkim/templates/pvc-keys.yaml | 17 +++ .../charts/opendkim/templates/workload.yaml | 119 ------------------ 2 files changed, 17 insertions(+), 119 deletions(-) create mode 100644 charts/silver/charts/opendkim/templates/pvc-keys.yaml diff --git a/charts/silver/charts/opendkim/templates/pvc-keys.yaml b/charts/silver/charts/opendkim/templates/pvc-keys.yaml new file mode 100644 index 0000000..fdbd204 --- /dev/null +++ b/charts/silver/charts/opendkim/templates/pvc-keys.yaml @@ -0,0 +1,17 @@ +{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "opendkim.fullname" . }}-keys + labels: + {{- include "opendkim.labels" . | nindent 4 }} +spec: + accessModes: + {{- toYaml .Values.persistence.accessModes | nindent 4 }} + resources: + requests: + storage: {{ .Values.persistence.size }} + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass }} + {{- end }} +{{- end }} diff --git a/charts/silver/charts/opendkim/templates/workload.yaml b/charts/silver/charts/opendkim/templates/workload.yaml index ecb98ec..e69de29 100644 --- a/charts/silver/charts/opendkim/templates/workload.yaml +++ b/charts/silver/charts/opendkim/templates/workload.yaml @@ -1,119 +0,0 @@ -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: {{ include "opendkim.fullname" . }} - labels: - {{- include "opendkim.labels" . | nindent 4 }} -spec: - serviceName: {{ include "opendkim.fullname" . }} - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: - {{- include "opendkim.selectorLabels" . | nindent 6 }} - {{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} - volumeClaimTemplates: - - metadata: - name: keys - labels: - {{- include "opendkim.labels" . | nindent 10 }} - spec: - accessModes: - {{- toYaml .Values.persistence.accessModes | nindent 10 }} - resources: - requests: - storage: {{ .Values.persistence.size }} - {{- if .Values.persistence.storageClass }} - storageClassName: {{ .Values.persistence.storageClass }} - {{- end }} - {{- end }} - template: - metadata: - labels: - {{- include "opendkim.selectorLabels" . | nindent 8 }} - {{- with .Values.podLabels }} - {{- toYaml . | nindent 8 }} - {{- end }} - annotations: - checksum/config: {{ include (print $.Template.BasePath "/configmap-opendkim.yaml") . | sha256sum }} - checksum/tables: {{ include (print $.Template.BasePath "/configmap-tables.yaml") . | sha256sum }} - checksum/silver: {{ include (print $.Template.BasePath "/secret-silver.yaml") . | sha256sum }} - {{- with .Values.podAnnotations }} - {{- toYaml . | nindent 8 }} - {{- end }} - spec: - serviceAccountName: {{ include "opendkim.serviceAccountName" . }} - {{- with .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.podSecurityContext }} - securityContext: - {{- toYaml . | nindent 8 }} - {{- end }} - containers: - - name: opendkim - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: opendkim - containerPort: {{ .Values.service.port }} - protocol: TCP - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - readinessProbe: - tcpSocket: - port: opendkim - initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }} - periodSeconds: {{ .Values.probes.readiness.periodSeconds }} - timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }} - failureThreshold: {{ .Values.probes.readiness.failureThreshold }} - livenessProbe: - tcpSocket: - port: opendkim - initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }} - periodSeconds: {{ .Values.probes.liveness.periodSeconds }} - timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }} - failureThreshold: {{ .Values.probes.liveness.failureThreshold }} - volumeMounts: - - name: silver-secret - mountPath: /etc/opendkim/silver.yaml - subPath: silver.yaml - readOnly: true - - name: config - mountPath: /etc/opendkim/opendkim.conf - subPath: opendkim.conf - readOnly: true - - name: tables - mountPath: /etc/opendkim/KeyTable - subPath: KeyTable - readOnly: true - - name: tables - mountPath: /etc/opendkim/SigningTable - subPath: SigningTable - readOnly: true - - name: tables - mountPath: /etc/opendkim/TrustedHosts - subPath: TrustedHosts - readOnly: true - - name: keys - mountPath: /etc/dkimkeys - resources: - {{- toYaml .Values.resources | nindent 12 }} - volumes: - - name: silver-secret - secret: - secretName: {{ include "opendkim.fullname" . }}-silver - - name: config - configMap: - name: {{ include "opendkim.fullname" . }}-config - - name: tables - configMap: - name: {{ include "opendkim.fullname" . }}-tables - {{- if and .Values.persistence.enabled .Values.persistence.existingClaim }} - - name: keys - persistentVolumeClaim: - claimName: {{ .Values.persistence.existingClaim }} - {{- else if not .Values.persistence.enabled }} - - name: keys - emptyDir: {} - {{- end }} From ad74d7f320c2b05e4e83e2ead3fd08714a9b3077 Mon Sep 17 00:00:00 2001 From: Aravinda-HWK Date: Thu, 2 Apr 2026 10:18:44 +0530 Subject: [PATCH 7/9] Refactor OpenDKIM Helm chart: remove PVC template and adjust workload.yaml for persistence handling --- .../charts/opendkim/templates/pvc-keys.yaml | 17 --- .../charts/opendkim/templates/workload.yaml | 120 ++++++++++++++++++ 2 files changed, 120 insertions(+), 17 deletions(-) delete mode 100644 charts/silver/charts/opendkim/templates/pvc-keys.yaml diff --git a/charts/silver/charts/opendkim/templates/pvc-keys.yaml b/charts/silver/charts/opendkim/templates/pvc-keys.yaml deleted file mode 100644 index fdbd204..0000000 --- a/charts/silver/charts/opendkim/templates/pvc-keys.yaml +++ /dev/null @@ -1,17 +0,0 @@ -{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: {{ include "opendkim.fullname" . }}-keys - labels: - {{- include "opendkim.labels" . | nindent 4 }} -spec: - accessModes: - {{- toYaml .Values.persistence.accessModes | nindent 4 }} - resources: - requests: - storage: {{ .Values.persistence.size }} - {{- if .Values.persistence.storageClass }} - storageClassName: {{ .Values.persistence.storageClass }} - {{- end }} -{{- end }} diff --git a/charts/silver/charts/opendkim/templates/workload.yaml b/charts/silver/charts/opendkim/templates/workload.yaml index e69de29..dcda280 100644 --- a/charts/silver/charts/opendkim/templates/workload.yaml +++ b/charts/silver/charts/opendkim/templates/workload.yaml @@ -0,0 +1,120 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "opendkim.fullname" . }} + labels: + {{- include "opendkim.labels" . | nindent 4 }} +spec: + serviceName: {{ include "opendkim.fullname" . }} + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "opendkim.selectorLabels" . | nindent 6 }} + {{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} + volumeClaimTemplates: + - metadata: + name: keys + labels: + {{- include "opendkim.labels" . | nindent 10 }} + spec: + accessModes: + {{- toYaml .Values.persistence.accessModes | nindent 10 }} + resources: + requests: + storage: {{ .Values.persistence.size }} + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass }} + {{- end }} + {{- end }} + template: + metadata: + labels: + {{- include "opendkim.selectorLabels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap-opendkim.yaml") . | sha256sum }} + checksum/tables: {{ include (print $.Template.BasePath "/configmap-tables.yaml") . | sha256sum }} + checksum/silver: {{ include (print $.Template.BasePath "/secret-silver.yaml") . | sha256sum }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ include "opendkim.serviceAccountName" . }} + {{- with .Values.global.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: opendkim + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: opendkim + containerPort: {{ .Values.service.port }} + protocol: TCP + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + readinessProbe: + tcpSocket: + port: opendkim + initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.readiness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.readiness.failureThreshold }} + livenessProbe: + tcpSocket: + port: opendkim + initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.liveness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.liveness.failureThreshold }} + volumeMounts: + - name: silver-secret + mountPath: /etc/opendkim/silver.yaml + subPath: silver.yaml + readOnly: true + - name: config + mountPath: /etc/opendkim/opendkim.conf + subPath: opendkim.conf + readOnly: true + - name: tables + mountPath: /etc/opendkim/KeyTable + subPath: KeyTable + readOnly: true + - name: tables + mountPath: /etc/opendkim/SigningTable + subPath: SigningTable + readOnly: true + - name: tables + mountPath: /etc/opendkim/TrustedHosts + subPath: TrustedHosts + readOnly: true + - name: keys + mountPath: /etc/dkimkeys + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumes: + - name: silver-secret + secret: + secretName: {{ include "opendkim.fullname" . }}-silver + - name: config + configMap: + name: {{ include "opendkim.fullname" . }}-config + - name: tables + configMap: + name: {{ include "opendkim.fullname" . }}-tables + {{- if and .Values.persistence.enabled .Values.persistence.existingClaim }} + - name: keys + persistentVolumeClaim: + claimName: {{ .Values.persistence.existingClaim }} + {{- else if not .Values.persistence.enabled }} + - name: keys + emptyDir: {} + {{- end }} +>>>>>>> bdb78f6 (Refactor OpenDKIM Helm chart: remove PVC template and adjust workload.yaml for persistence handling) From 6f6445faf3d2356be5dc4f290b24efe85f23cb79 Mon Sep 17 00:00:00 2001 From: Aravinda-HWK Date: Fri, 3 Apr 2026 11:44:58 +0530 Subject: [PATCH 8/9] chore: remove ClamAV Helm chart as part of cleanup --- charts/silver/charts/clamav/Chart.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 charts/silver/charts/clamav/Chart.yaml diff --git a/charts/silver/charts/clamav/Chart.yaml b/charts/silver/charts/clamav/Chart.yaml deleted file mode 100644 index e69de29..0000000 From c24362be6bba1dec14e37cc54e2386e7fba736f7 Mon Sep 17 00:00:00 2001 From: Aravinda-HWK Date: Fri, 3 Apr 2026 11:51:06 +0530 Subject: [PATCH 9/9] chore: remove emptyDir volume from OpenDKIM StatefulSet workload --- charts/silver/charts/opendkim/templates/workload.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/charts/silver/charts/opendkim/templates/workload.yaml b/charts/silver/charts/opendkim/templates/workload.yaml index dcda280..ecb98ec 100644 --- a/charts/silver/charts/opendkim/templates/workload.yaml +++ b/charts/silver/charts/opendkim/templates/workload.yaml @@ -117,4 +117,3 @@ spec: - name: keys emptyDir: {} {{- end }} ->>>>>>> bdb78f6 (Refactor OpenDKIM Helm chart: remove PVC template and adjust workload.yaml for persistence handling)