From 1e01060aa9dd4f55829f4a4ccf49864d235ab430 Mon Sep 17 00:00:00 2001 From: jonasschultheiss Date: Tue, 9 Jun 2026 09:10:28 +0200 Subject: [PATCH 1/2] feat(helm): add Gateway API HTTPRoute and External Secrets Operator support Add HTTPRoute template as an alternative to the classic Ingress resource for users running Kubernetes Gateway API controllers. The template supports parentRefs, hostnames, flexible match rules with filters, and automatically selects port 80/443 based on the existing nginx TLS configuration. Add ExternalSecret template for integrating with the External Secrets Operator (ESO), allowing users to sync secrets from external stores (e.g. HashiCorp Vault, AWS Secrets Manager, Azure Key Vault) instead of managing them directly in Helm values or Kubernetes Secrets. Both features are disabled by default and fully opt-in. --- .../templates/django-httproute.yaml | 55 +++++++++++++++++++ .../defectdojo/templates/external-secret.yaml | 37 +++++++++++++ helm/defectdojo/values.yaml | 37 +++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 helm/defectdojo/templates/django-httproute.yaml create mode 100644 helm/defectdojo/templates/external-secret.yaml diff --git a/helm/defectdojo/templates/django-httproute.yaml b/helm/defectdojo/templates/django-httproute.yaml new file mode 100644 index 00000000000..c04106ce7ff --- /dev/null +++ b/helm/defectdojo/templates/django-httproute.yaml @@ -0,0 +1,55 @@ +{{- if .Values.django.httpRoute.enabled -}} +{{- $fullName := include "defectdojo.fullname" . -}} +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ $fullName }} + namespace: {{ .Release.Namespace }} + labels: + defectdojo.org/component: django + app.kubernetes.io/name: {{ include "defectdojo.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + helm.sh/chart: {{ include "defectdojo.chart" . }} + {{- range $key, $value := .Values.extraLabels }} + {{ $key }}: {{ quote $value }} + {{- end }} + {{- with .Values.django.httpRoute.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if or .Values.extraAnnotations .Values.django.httpRoute.annotations }} + annotations: + {{- range $key, $value := .Values.extraAnnotations }} + {{ $key }}: {{ quote $value }} + {{- end }} + {{- with .Values.django.httpRoute.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- end }} +spec: + {{- with .Values.django.httpRoute.parentRefs }} + parentRefs: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.django.httpRoute.hostnames }} + hostnames: + {{- toYaml . | nindent 4 }} + {{- end }} + rules: + {{- range .Values.django.httpRoute.rules }} + {{- with .matches }} + - matches: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .filters }} + filters: + {{- toYaml . | nindent 8 }} + {{- end }} + backendRefs: + - group: "" + kind: Service + name: {{ $fullName }}-django + port: {{ $.Values.django.nginx.tls.enabled | ternary 443 80 }} + weight: 1 + {{- end }} +{{- end }} diff --git a/helm/defectdojo/templates/external-secret.yaml b/helm/defectdojo/templates/external-secret.yaml new file mode 100644 index 00000000000..4f3070430cc --- /dev/null +++ b/helm/defectdojo/templates/external-secret.yaml @@ -0,0 +1,37 @@ +{{- if .Values.externalSecret.enabled }} +{{- $fullName := include "defectdojo.fullname" . -}} +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: {{ $fullName }} + namespace: {{ .Release.Namespace }} + labels: + defectdojo.org/component: django + app.kubernetes.io/name: {{ include "defectdojo.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + helm.sh/chart: {{ include "defectdojo.chart" . }} + {{- range $key, $value := .Values.extraLabels }} + {{ $key }}: {{ quote $value }} + {{- end }} + {{- with .Values.externalSecret.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if or .Values.extraAnnotations .Values.externalSecret.annotations }} + annotations: + {{- range $key, $value := .Values.extraAnnotations }} + {{ $key }}: {{ quote $value }} + {{- end }} + {{- with .Values.externalSecret.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- end }} +spec: + refreshInterval: {{ .Values.externalSecret.refreshInterval | default "1h0m0s" }} + secretStoreRef: + {{- toYaml .Values.externalSecret.secretStoreRef | nindent 4 }} + target: + name: {{ .Values.externalSecret.targetSecretName }} + data: + {{- toYaml .Values.externalSecret.data | nindent 4 }} +{{- end }} diff --git a/helm/defectdojo/values.yaml b/helm/defectdojo/values.yaml index f59981dc463..0d521783448 100644 --- a/helm/defectdojo/values.yaml +++ b/helm/defectdojo/values.yaml @@ -13,6 +13,28 @@ createSecret: false createValkeySecret: false # -- create postgresql secret in defectdojo chart, outside of postgresql chart createPostgresqlSecret: false + +# -- External Secrets Operator integration +externalSecret: + # -- Enable the ExternalSecret resource + enabled: false + labels: {} + annotations: {} + # -- How often to refresh the secret from the external store + refreshInterval: 1h0m0s + # -- Reference to a SecretStore or ClusterSecretStore + secretStoreRef: + name: "" + kind: "" + # -- Name of the Kubernetes Secret created by ESO + targetSecretName: "" + # -- Data mappings from the external store + data: [] + # - secretKey: DD_ADMIN_PASSWORD + # remoteRef: + # key: my-vault-path + # property: admin-password + # -- Track configuration (trackConfig): will automatically respin application pods in case of config changes detection # can be: # 1. disabled (default) @@ -372,6 +394,21 @@ django: # `nginx.ingress.kubernetes.io/proxy-read-timeout: "1800"` # `nginx.ingress.kubernetes.io/proxy-send-timeout: "1800"` annotations: {} + # -- Kubernetes Gateway API HTTPRoute configuration (alternative to Ingress) + httpRoute: + enabled: false + labels: {} + annotations: {} + parentRefs: [] + # - name: my-gateway + # namespace: default + hostnames: [] + # - defectdojo.example.com + rules: [] + # - matches: + # - path: + # type: PathPrefix + # value: / nginx: # -- If empty, uses values from images.nginx.image image: From 73fad5c44c89e3c296a48c8befe914f1e2500201 Mon Sep 17 00:00:00 2001 From: jonasschultheiss Date: Fri, 3 Jul 2026 13:52:25 +0200 Subject: [PATCH 2/2] fix(helm): address review feedback and CI failures - Fix HTTPRoute template YAML structure: emit list dash unconditionally per rule so match-less catch-all rules produce valid YAML arrays - Update artifacthub.io/changes annotation in Chart.yaml - Regenerate README.md via helm-docs - Add httpRoute and externalSecret fields to values.schema.json --- helm/defectdojo/Chart.yaml | 6 +- helm/defectdojo/README.md | 7 ++ .../templates/django-httproute.yaml | 14 ++-- helm/defectdojo/values.schema.json | 67 +++++++++++++++++++ 4 files changed, 86 insertions(+), 8 deletions(-) diff --git a/helm/defectdojo/Chart.yaml b/helm/defectdojo/Chart.yaml index ce9941e9d73..db46a0bfd45 100644 --- a/helm/defectdojo/Chart.yaml +++ b/helm/defectdojo/Chart.yaml @@ -34,4 +34,8 @@ dependencies: # description: Critical bug annotations: artifacthub.io/prerelease: "true" - artifacthub.io/changes: "- kind: changed\n description: chore(deps)_ update valkey _ tag from 0.20.2 to v0.22.1 (_/defect_/chart.yaml)\n- kind: changed\n description: Update gcr.io/cloudsql__/gce_proxy Docker tag from 1.38.0 to v1.38.1 (_/defect_/values.yaml)\n" + artifacthub.io/changes: | + - kind: added + description: Add Gateway API HTTPRoute support as an alternative to Ingress + - kind: added + description: Add External Secrets Operator (ESO) support for external secret stores diff --git a/helm/defectdojo/README.md b/helm/defectdojo/README.md index 05cfdade000..bd19c5e0550 100644 --- a/helm/defectdojo/README.md +++ b/helm/defectdojo/README.md @@ -618,6 +618,7 @@ A Helm chart for Kubernetes to install DefectDojo | django.extraInitContainers | list | `[]` | A list of additional initContainers to run before the uwsgi and nginx containers. | | django.extraVolumeMounts | list | `[]` | Array of additional volume mount points common to all containers and initContainers. | | django.extraVolumes | list | `[]` | A list of extra volumes to mount. | +| django.httpRoute | object | `{"annotations":{},"enabled":false,"hostnames":[],"labels":{},"parentRefs":[],"rules":[]}` | Kubernetes Gateway API HTTPRoute configuration (alternative to Ingress) | | django.ingress.activateTLS | bool | `true` | | | django.ingress.annotations | object | `{}` | Restricts the type of ingress controller that can interact with our chart (nginx, traefik, ...) `kubernetes.io/ingress.class: nginx` Depending on the size and complexity of your scans, you might want to increase the default ingress timeouts if you see repeated 504 Gateway Timeouts `nginx.ingress.kubernetes.io/proxy-read-timeout: "1800"` `nginx.ingress.kubernetes.io/proxy-send-timeout: "1800"` | | django.ingress.enabled | bool | `true` | | @@ -683,6 +684,12 @@ A Helm chart for Kubernetes to install DefectDojo | django.uwsgi.startupProbe.periodSeconds | int | `5` | | | django.uwsgi.startupProbe.successThreshold | int | `1` | | | django.uwsgi.startupProbe.timeoutSeconds | int | `1` | | +| externalSecret | object | `{"annotations":{},"data":[],"enabled":false,"labels":{},"refreshInterval":"1h0m0s","secretStoreRef":{"kind":"","name":""},"targetSecretName":""}` | External Secrets Operator integration | +| externalSecret.data | list | `[]` | Data mappings from the external store | +| externalSecret.enabled | bool | `false` | Enable the ExternalSecret resource | +| externalSecret.refreshInterval | string | `"1h0m0s"` | How often to refresh the secret from the external store | +| externalSecret.secretStoreRef | object | `{"kind":"","name":""}` | Reference to a SecretStore or ClusterSecretStore | +| externalSecret.targetSecretName | string | `""` | Name of the Kubernetes Secret created by ESO | | extraAnnotations | object | `{}` | Annotations globally added to all resources | | extraConfigs | object | `{}` | To add extra variables not predefined by helm config it is possible to define in extraConfigs block, e.g. below: NOTE Do not store any kind of sensitive information inside of it ``` DD_SOCIAL_AUTH_AUTH0_OAUTH2_ENABLED: 'true' DD_SOCIAL_AUTH_AUTH0_KEY: 'dev' DD_SOCIAL_AUTH_AUTH0_DOMAIN: 'xxxxx' ``` | | extraEnv | list | `[]` | To add (or override) extra variables which need to be pulled from another configMap, you can use extraEnv. For example: ``` - name: DD_DATABASE_HOST valueFrom: configMapKeyRef: name: my-other-postgres-configmap key: cluster_endpoint ``` | diff --git a/helm/defectdojo/templates/django-httproute.yaml b/helm/defectdojo/templates/django-httproute.yaml index c04106ce7ff..e426d786eb8 100644 --- a/helm/defectdojo/templates/django-httproute.yaml +++ b/helm/defectdojo/templates/django-httproute.yaml @@ -37,14 +37,14 @@ spec: {{- end }} rules: {{- range .Values.django.httpRoute.rules }} - {{- with .matches }} - - matches: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .filters }} + - {{- with .matches }} + matches: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .filters }} filters: - {{- toYaml . | nindent 8 }} - {{- end }} + {{- toYaml . | nindent 8 }} + {{- end }} backendRefs: - group: "" kind: Service diff --git a/helm/defectdojo/values.schema.json b/helm/defectdojo/values.schema.json index 54120f850bf..fc08c4c95b9 100644 --- a/helm/defectdojo/values.schema.json +++ b/helm/defectdojo/values.schema.json @@ -388,6 +388,46 @@ "description": "create valkey secret in defectdojo chart, outside of valkey chart", "type": "boolean" }, + "externalSecret": { + "description": "External Secrets Operator integration", + "type": "object", + "properties": { + "enabled": { + "description": "Enable the ExternalSecret resource", + "type": "boolean" + }, + "labels": { + "type": "object" + }, + "annotations": { + "type": "object" + }, + "refreshInterval": { + "description": "How often to refresh the secret from the external store", + "type": "string" + }, + "secretStoreRef": { + "description": "Reference to a SecretStore or ClusterSecretStore", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "kind": { + "type": "string" + } + } + }, + "targetSecretName": { + "description": "Name of the Kubernetes Secret created by ESO", + "type": "string" + }, + "data": { + "description": "Data mappings from the external store", + "type": "array" + } + } + }, "dbMigrationChecker": { "type": "object", "properties": { @@ -511,6 +551,33 @@ "description": "A list of extra volumes to mount.", "type": "array" }, + "httpRoute": { + "description": "Kubernetes Gateway API HTTPRoute configuration (alternative to Ingress)", + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "labels": { + "type": "object" + }, + "annotations": { + "type": "object" + }, + "parentRefs": { + "type": "array" + }, + "hostnames": { + "type": "array", + "items": { + "type": "string" + } + }, + "rules": { + "type": "array" + } + } + }, "ingress": { "type": "object", "properties": {