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..85d9ec75d 100644 --- a/charts/authelia/templates/_helpers.tpl +++ b/charts/authelia/templates/_helpers.tpl @@ -31,6 +31,55 @@ app.kubernetes.io/name: {{ include "authelia.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end -}} +{{- 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 -}} +{{- 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 (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) -}} +{{- 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..1e1434d3f 100644 --- a/charts/authelia/tests/secret_test.yaml +++ b/charts/authelia/tests/secret_test.yaml @@ -95,6 +95,16 @@ tests: - hasDocuments: count: 3 + - it: should keep ntp startup check enabled with a Kubernetes-friendly drift window + documentIndex: 0 + asserts: + - matchRegex: + path: stringData["configuration.yml"] + pattern: "max_desync: 30s" + - matchRegex: + path: stringData["configuration.yml"] + pattern: "disable_startup_check: false" + - 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..956a76e22 --- /dev/null +++ b/charts/authelia/tests/validation_test.yaml @@ -0,0 +1,103 @@ +# 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 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 + 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 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 + 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 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: + 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..a56182d1d 100644 --- a/charts/authelia/values.yaml +++ b/charts/authelia/values.yaml @@ -75,7 +75,8 @@ config: ntp: address: "udp://time.cloudflare.com:123" version: 4 - max_desync: 3s + # -- Maximum tolerated clock drift during the startup check. + max_desync: 30s disable_startup_check: false # -- Authentication backend (file or ldap - mutually exclusive)