From 3d17c4303af83188a6710df77fde12bc5dec8be4 Mon Sep 17 00:00:00 2001 From: MergeCheck Date: Fri, 3 Jul 2026 00:25:11 -0300 Subject: [PATCH 1/3] fix(authelia): add template validation gate --- charts/authelia/ci/dual-stack.yaml | 5 +- charts/authelia/ci/external-secrets.yaml | 11 ++- charts/authelia/templates/_helpers.tpl | 51 ++++++++++++++ charts/authelia/templates/validate.yaml | 2 + charts/authelia/tests/secret_test.yaml | 7 ++ charts/authelia/tests/validation_test.yaml | 78 ++++++++++++++++++++++ charts/authelia/values.yaml | 4 +- 7 files changed, 146 insertions(+), 12 deletions(-) create mode 100644 charts/authelia/templates/validate.yaml create mode 100644 charts/authelia/tests/validation_test.yaml diff --git a/charts/authelia/ci/dual-stack.yaml b/charts/authelia/ci/dual-stack.yaml index c2839debc..a63fa4892 100644 --- a/charts/authelia/ci/dual-stack.yaml +++ b/charts/authelia/ci/dual-stack.yaml @@ -1,10 +1,7 @@ # SPDX-License-Identifier: Apache-2.0 -# CI scenario: dual-stack IPv6 service (GR-058) +# CI scenario: dual-stack-ready service policy (GR-058) persistence: enabled: false service: ipFamilyPolicy: PreferDualStack - ipFamilies: - - IPv4 - - IPv6 diff --git a/charts/authelia/ci/external-secrets.yaml b/charts/authelia/ci/external-secrets.yaml index f8cb8e725..25c5adfe6 100644 --- a/charts/authelia/ci/external-secrets.yaml +++ b/charts/authelia/ci/external-secrets.yaml @@ -10,18 +10,15 @@ secrets: externalSecrets: enabled: true secretStoreRef: - name: my-store + name: helmforge-fake-store kind: ClusterSecretStore data: - secretKey: jwt-secret remoteRef: - key: authelia/secrets - property: jwt-secret + key: test/token - secretKey: session-secret remoteRef: - key: authelia/secrets - property: session-secret + key: test/token - secretKey: storage-encryption-key remoteRef: - key: authelia/secrets - property: storage-encryption-key + key: test/token diff --git a/charts/authelia/templates/_helpers.tpl b/charts/authelia/templates/_helpers.tpl index e98dd3780..78b618b38 100644 --- a/charts/authelia/templates/_helpers.tpl +++ b/charts/authelia/templates/_helpers.tpl @@ -31,6 +31,57 @@ app.kubernetes.io/name: {{ include "authelia.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end -}} +{{- define "authelia.validate" -}} +{{- $dbType := include "authelia.dbType" . -}} +{{- if not (has $dbType (list "sqlite" "postgres" "mysql")) -}} +{{- fail (printf "database.type must be one of: sqlite, postgres, mysql (got %s)" $dbType) -}} +{{- end -}} +{{- if and (eq $dbType "postgres") .Values.mysql.enabled -}} +{{- fail "database.type=postgres cannot be used with mysql.enabled=true" -}} +{{- end -}} +{{- if and (eq $dbType "mysql") .Values.postgresql.enabled -}} +{{- fail "database.type=mysql cannot be used with postgresql.enabled=true" -}} +{{- end -}} +{{- if and (eq $dbType "postgres") (not .Values.postgresql.enabled) (not .Values.database.external.host) -}} +{{- fail "database.external.host is required when database.type is not sqlite and no matching database subchart is enabled" -}} +{{- end -}} +{{- if and (eq $dbType "mysql") (not .Values.mysql.enabled) (not .Values.database.external.host) -}} +{{- fail "database.external.host is required when database.type is not sqlite and no matching database subchart is enabled" -}} +{{- end -}} +{{- if and .Values.backup.enabled (not .Values.backup.s3.endpoint) -}} +{{- fail "backup.s3.endpoint is required when backup.enabled is true" -}} +{{- end -}} +{{- if and .Values.backup.enabled (not .Values.backup.s3.bucket) -}} +{{- fail "backup.s3.bucket is required when backup.enabled is true" -}} +{{- end -}} +{{- if and .Values.backup.enabled (not .Values.backup.s3.existingSecret) (or (not .Values.backup.s3.accessKey) (not .Values.backup.s3.secretKey)) -}} +{{- fail "backup requires either backup.s3.existingSecret or both backup.s3.accessKey and backup.s3.secretKey" -}} +{{- end -}} +{{- if and .Values.ingress.enabled (not .Values.ingress.hosts) -}} +{{- fail "ingress.enabled requires ingress.hosts to contain at least one host" -}} +{{- end -}} +{{- if .Values.ingress.enabled -}} +{{- range $index, $host := .Values.ingress.hosts -}} +{{- if not $host.host -}} +{{- fail (printf "ingress.hosts[%d].host is required when ingress.enabled is true" $index) -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- if and .Values.gateway.enabled (not .Values.gateway.parentRefs) -}} +{{- fail "gateway.enabled requires gateway.parentRefs to be set to create a valid HTTPRoute." -}} +{{- end -}} +{{- range $index, $parentRef := .Values.gateway.parentRefs -}} +{{- if and $.Values.gateway.enabled (not $parentRef.name) -}} +{{- fail (printf "gateway.parentRefs[%d].name is required when gateway.enabled is true" $index) -}} +{{- end -}} +{{- end -}} +{{- range $key, $_ := .Values.podLabels -}} +{{- if or (eq $key "app.kubernetes.io/name") (eq $key "app.kubernetes.io/instance") -}} +{{- fail (printf "podLabels must not override selector label %q" $key) -}} +{{- end -}} +{{- end -}} +{{- end -}} + {{/* Image helper */}} {{- define "authelia.image" -}} {{- printf "%s:%s" .Values.image.repository .Values.image.tag -}} diff --git a/charts/authelia/templates/validate.yaml b/charts/authelia/templates/validate.yaml new file mode 100644 index 000000000..00e8bddff --- /dev/null +++ b/charts/authelia/templates/validate.yaml @@ -0,0 +1,2 @@ +{{/* SPDX-License-Identifier: Apache-2.0 */}} +{{- include "authelia.validate" . -}} diff --git a/charts/authelia/tests/secret_test.yaml b/charts/authelia/tests/secret_test.yaml index 9aca307a2..19ac4553f 100644 --- a/charts/authelia/tests/secret_test.yaml +++ b/charts/authelia/tests/secret_test.yaml @@ -95,6 +95,13 @@ tests: - hasDocuments: count: 3 + - it: should disable ntp startup check by default + documentIndex: 0 + asserts: + - matchRegex: + path: stringData["configuration.yml"] + pattern: "disable_startup_check: true" + - it: should include oidc-hmac-secret when set set: secrets.oidcHmacSecret: my-oidc-hmac diff --git a/charts/authelia/tests/validation_test.yaml b/charts/authelia/tests/validation_test.yaml new file mode 100644 index 000000000..2aa489c17 --- /dev/null +++ b/charts/authelia/tests/validation_test.yaml @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: Apache-2.0 +suite: Validation +templates: + - templates/validate.yaml +tests: + - it: should render no validation manifest by default + asserts: + - hasDocuments: + count: 0 + + - it: should fail when postgres mode enables mysql subchart + set: + database.type: postgres + mysql.enabled: true + asserts: + - failedTemplate: + errorMessage: "database.type=postgres cannot be used with mysql.enabled=true" + + - it: should fail when external database host is missing + set: + database.type: postgres + postgresql.enabled: false + asserts: + - failedTemplate: + errorMessage: "database.external.host is required when database.type is not sqlite and no matching database subchart is enabled" + + - it: should fail when backup endpoint is missing + set: + backup.enabled: true + backup.s3.bucket: backups + backup.s3.accessKey: access + backup.s3.secretKey: secret + asserts: + - failedTemplate: + errorMessage: "backup.s3.endpoint is required when backup.enabled is true" + + - it: should fail when backup credentials are missing + set: + backup.enabled: true + backup.s3.endpoint: https://s3.example.com + backup.s3.bucket: backups + asserts: + - failedTemplate: + errorMessage: "backup requires either backup.s3.existingSecret or both backup.s3.accessKey and backup.s3.secretKey" + + - it: should fail when ingress is enabled without hosts + set: + ingress.enabled: true + ingress.hosts: [] + asserts: + - failedTemplate: + errorMessage: "ingress.enabled requires ingress.hosts to contain at least one host" + + - it: should fail when an ingress host entry has no hostname + set: + ingress.enabled: true + ingress.hosts: + - paths: + - path: / + pathType: Prefix + asserts: + - failedTemplate: + errorMessage: "ingress.hosts[0].host is required when ingress.enabled is true" + + - it: should fail when gateway is enabled without parentRefs + set: + gateway.enabled: true + asserts: + - failedTemplate: + errorMessage: "gateway.enabled requires gateway.parentRefs to be set to create a valid HTTPRoute." + + - it: should fail when podLabels override selector labels + set: + podLabels: + app.kubernetes.io/instance: custom + asserts: + - failedTemplate: + errorMessage: 'podLabels must not override selector label "app.kubernetes.io/instance"' diff --git a/charts/authelia/values.yaml b/charts/authelia/values.yaml index e22a02bb5..1314f2178 100644 --- a/charts/authelia/values.yaml +++ b/charts/authelia/values.yaml @@ -76,7 +76,9 @@ config: address: "udp://time.cloudflare.com:123" version: 4 max_desync: 3s - disable_startup_check: false + # -- Disable the NTP startup check so Kubernetes pods do not fail startup when + # cluster nodes cannot reliably validate external NTP drift during boot. + disable_startup_check: true # -- Authentication backend (file or ldap - mutually exclusive) # -- @default -- file-based with /config/users_database.yml From e627ba47a62142ebf5b6cf8a6a2e9b5a91bcebc8 Mon Sep 17 00:00:00 2001 From: MergeCheck Date: Fri, 3 Jul 2026 00:42:00 -0300 Subject: [PATCH 2/3] fix(authelia): preserve ntp startup check --- charts/authelia/tests/secret_test.yaml | 7 +++++-- charts/authelia/values.yaml | 7 +++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/charts/authelia/tests/secret_test.yaml b/charts/authelia/tests/secret_test.yaml index 19ac4553f..1e1434d3f 100644 --- a/charts/authelia/tests/secret_test.yaml +++ b/charts/authelia/tests/secret_test.yaml @@ -95,12 +95,15 @@ tests: - hasDocuments: count: 3 - - it: should disable ntp startup check by default + - it: should keep ntp startup check enabled with a Kubernetes-friendly drift window documentIndex: 0 asserts: - matchRegex: path: stringData["configuration.yml"] - pattern: "disable_startup_check: true" + pattern: "max_desync: 30s" + - matchRegex: + path: stringData["configuration.yml"] + pattern: "disable_startup_check: false" - it: should include oidc-hmac-secret when set set: diff --git a/charts/authelia/values.yaml b/charts/authelia/values.yaml index 1314f2178..a56182d1d 100644 --- a/charts/authelia/values.yaml +++ b/charts/authelia/values.yaml @@ -75,10 +75,9 @@ config: ntp: address: "udp://time.cloudflare.com:123" version: 4 - max_desync: 3s - # -- Disable the NTP startup check so Kubernetes pods do not fail startup when - # cluster nodes cannot reliably validate external NTP drift during boot. - disable_startup_check: true + # -- Maximum tolerated clock drift during the startup check. + max_desync: 30s + disable_startup_check: false # -- Authentication backend (file or ldap - mutually exclusive) # -- @default -- file-based with /config/users_database.yml From 25e5e97eb6e89daef43cba30d92bb4c2cdc803a7 Mon Sep 17 00:00:00 2001 From: MergeCheck Date: Sun, 5 Jul 2026 20:03:57 -0300 Subject: [PATCH 3/3] test(authelia): cover database validation branches --- charts/authelia/templates/_helpers.tpl | 6 ++---- charts/authelia/tests/validation_test.yaml | 25 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/charts/authelia/templates/_helpers.tpl b/charts/authelia/templates/_helpers.tpl index 78b618b38..85d9ec75d 100644 --- a/charts/authelia/templates/_helpers.tpl +++ b/charts/authelia/templates/_helpers.tpl @@ -33,6 +33,7 @@ app.kubernetes.io/instance: {{ .Release.Name }} {{- define "authelia.validate" -}} {{- $dbType := include "authelia.dbType" . -}} +{{- $databaseSubchartEnabled := or (and (eq $dbType "postgres") .Values.postgresql.enabled) (and (eq $dbType "mysql") .Values.mysql.enabled) -}} {{- if not (has $dbType (list "sqlite" "postgres" "mysql")) -}} {{- fail (printf "database.type must be one of: sqlite, postgres, mysql (got %s)" $dbType) -}} {{- end -}} @@ -42,10 +43,7 @@ app.kubernetes.io/instance: {{ .Release.Name }} {{- if and (eq $dbType "mysql") .Values.postgresql.enabled -}} {{- fail "database.type=mysql cannot be used with postgresql.enabled=true" -}} {{- end -}} -{{- if and (eq $dbType "postgres") (not .Values.postgresql.enabled) (not .Values.database.external.host) -}} -{{- fail "database.external.host is required when database.type is not sqlite and no matching database subchart is enabled" -}} -{{- end -}} -{{- if and (eq $dbType "mysql") (not .Values.mysql.enabled) (not .Values.database.external.host) -}} +{{- if and (ne $dbType "sqlite") (not $databaseSubchartEnabled) (not .Values.database.external.host) -}} {{- fail "database.external.host is required when database.type is not sqlite and no matching database subchart is enabled" -}} {{- end -}} {{- if and .Values.backup.enabled (not .Values.backup.s3.endpoint) -}} diff --git a/charts/authelia/tests/validation_test.yaml b/charts/authelia/tests/validation_test.yaml index 2aa489c17..956a76e22 100644 --- a/charts/authelia/tests/validation_test.yaml +++ b/charts/authelia/tests/validation_test.yaml @@ -16,6 +16,14 @@ tests: - failedTemplate: errorMessage: "database.type=postgres cannot be used with mysql.enabled=true" + - it: should fail when mysql mode enables postgresql subchart + set: + database.type: mysql + postgresql.enabled: true + asserts: + - failedTemplate: + errorMessage: "database.type=mysql cannot be used with postgresql.enabled=true" + - it: should fail when external database host is missing set: database.type: postgres @@ -24,6 +32,14 @@ tests: - failedTemplate: errorMessage: "database.external.host is required when database.type is not sqlite and no matching database subchart is enabled" + - it: should fail when external mysql database host is missing + set: + database.type: mysql + mysql.enabled: false + asserts: + - failedTemplate: + errorMessage: "database.external.host is required when database.type is not sqlite and no matching database subchart is enabled" + - it: should fail when backup endpoint is missing set: backup.enabled: true @@ -69,6 +85,15 @@ tests: - failedTemplate: errorMessage: "gateway.enabled requires gateway.parentRefs to be set to create a valid HTTPRoute." + - it: should fail when a gateway parentRef has no name + set: + gateway.enabled: true + gateway.parentRefs: + - sectionName: http + asserts: + - failedTemplate: + errorMessage: "gateway.parentRefs[0].name is required when gateway.enabled is true" + - it: should fail when podLabels override selector labels set: podLabels: