diff --git a/chart/templates/_helpers.yaml b/chart/templates/_helpers.yaml index 04f5516567a15..655374282ec5a 100644 --- a/chart/templates/_helpers.yaml +++ b/chart/templates/_helpers.yaml @@ -114,13 +114,6 @@ If release name contains chart name it will be used as a full name. name: {{ template "api_secret_key_secret" . }} key: api-secret-key {{- end }} - {{- if and .IncludeJwtSecret (semverCompare ">=3.0.0" .Values.airflowVersion) .Values.enableBuiltInSecretEnvVars.AIRFLOW__API_AUTH__JWT_SECRET }} - - name: AIRFLOW__API_AUTH__JWT_SECRET - valueFrom: - secretKeyRef: - name: {{ template "jwt_secret" . }} - key: jwt-secret - {{- end }} {{- if or (contains "CeleryExecutor" .Values.executor) (contains "CeleryKubernetesExecutor" .Values.executor) }} {{- if and .Values.enableBuiltInSecretEnvVars.AIRFLOW__CELERY__RESULT_BACKEND (or .Values.data.resultBackendSecretName .Values.data.resultBackendConnection) }} - name: AIRFLOW__CELERY__RESULT_BACKEND @@ -171,6 +164,17 @@ If release name contains chart name it will be used as a full name. {{- end }} {{- end }} +{{/* JWT signing and validation secret, only needed by the API server and the scheduler */}} +{{- define "jwt_secret_environment" }} + {{- if and (semverCompare ">=3.0.0" .Values.airflowVersion) .Values.enableBuiltInSecretEnvVars.AIRFLOW__API_AUTH__JWT_SECRET }} + - name: AIRFLOW__API_AUTH__JWT_SECRET + valueFrom: + secretKeyRef: + name: {{ template "jwt_secret" . }} + key: jwt-secret + {{- end }} +{{- end }} + {{/* User defined Airflow environment variables */}} {{- define "custom_airflow_environment" }} # Dynamically created environment variables diff --git a/chart/templates/api-server/api-server-deployment.yaml b/chart/templates/api-server/api-server-deployment.yaml index af9c38e15cdbc..dafe2a78eb811 100644 --- a/chart/templates/api-server/api-server-deployment.yaml +++ b/chart/templates/api-server/api-server-deployment.yaml @@ -151,7 +151,7 @@ spec: envFrom: {{- include "custom_airflow_environment_from" . | default "\n []" | indent 10 }} env: {{- include "custom_airflow_environment" . | indent 10 }} - {{- include "standard_airflow_environment" (merge (dict "IncludeJwtSecret" false) .) | indent 10 }} + {{- include "standard_airflow_environment" . | indent 10 }} {{- if .Values.apiServer.waitForMigrations.env }} {{- tpl (toYaml .Values.apiServer.waitForMigrations.env) $ | nindent 12 }} {{- end }} @@ -225,7 +225,8 @@ spec: envFrom: {{- include "custom_airflow_environment_from" . | default "\n []" | indent 10 }} env: {{- include "custom_airflow_environment" . | indent 10 }} - {{- include "standard_airflow_environment" (merge (dict "IncludeJwtSecret" true) .) | indent 10 }} + {{- include "standard_airflow_environment" . | indent 10 }} + {{- include "jwt_secret_environment" . | indent 10 }} {{- include "container_extra_envs" (list . .Values.apiServer.env) | indent 10 }} {{- if .Values.apiServer.extraContainers }} {{- tpl (toYaml .Values.apiServer.extraContainers) . | nindent 8 }} diff --git a/chart/templates/dag-processor/dag-processor-deployment.yaml b/chart/templates/dag-processor/dag-processor-deployment.yaml index 9496ca9636692..35c7215b47974 100644 --- a/chart/templates/dag-processor/dag-processor-deployment.yaml +++ b/chart/templates/dag-processor/dag-processor-deployment.yaml @@ -136,7 +136,7 @@ spec: envFrom: {{- include "custom_airflow_environment_from" . | default "\n []" | indent 10 }} env: {{- include "custom_airflow_environment" . | indent 10 }} - {{- include "standard_airflow_environment" (merge (dict "IncludeJwtSecret" false) .) | indent 10 }} + {{- include "standard_airflow_environment" . | indent 10 }} {{- if .Values.dagProcessor.waitForMigrations.env }} {{- tpl (toYaml .Values.dagProcessor.waitForMigrations.env) $ | nindent 12 }} {{- end }} @@ -181,7 +181,7 @@ spec: envFrom: {{- include "custom_airflow_environment_from" . | default "\n []" | indent 10 }} env: {{- include "custom_airflow_environment" . | indent 10 }} - {{- include "standard_airflow_environment" (merge (dict "IncludeJwtSecret" false) .) | indent 10 }} + {{- include "standard_airflow_environment" . | indent 10 }} {{- include "container_extra_envs" (list . .Values.dagProcessor.env) | indent 10 }} livenessProbe: initialDelaySeconds: {{ .Values.dagProcessor.livenessProbe.initialDelaySeconds }} diff --git a/chart/templates/scheduler/scheduler-deployment.yaml b/chart/templates/scheduler/scheduler-deployment.yaml index 41e9085919376..9b353fed7249f 100644 --- a/chart/templates/scheduler/scheduler-deployment.yaml +++ b/chart/templates/scheduler/scheduler-deployment.yaml @@ -173,7 +173,7 @@ spec: envFrom: {{- include "custom_airflow_environment_from" . | default "\n []" | indent 10 }} env: {{- include "custom_airflow_environment" . | indent 10 }} - {{- include "standard_airflow_environment" (merge (dict "IncludeJwtSecret" false) .) | indent 10 }} + {{- include "standard_airflow_environment" . | indent 10 }} {{- if .Values.scheduler.waitForMigrations.env }} {{- tpl (toYaml .Values.scheduler.waitForMigrations.env) $ | nindent 12 }} {{- end }} @@ -201,7 +201,8 @@ spec: envFrom: {{- include "custom_airflow_environment_from" . | default "\n []" | indent 10 }} env: {{- include "custom_airflow_environment" . | indent 10 }} - {{- include "standard_airflow_environment" (merge (dict "IncludeJwtSecret" true) .) | indent 10 }} + {{- include "standard_airflow_environment" . | indent 10 }} + {{- include "jwt_secret_environment" . | indent 10 }} {{- include "container_extra_envs" (list . .Values.scheduler.env) | indent 10 }} livenessProbe: initialDelaySeconds: {{ .Values.scheduler.livenessProbe.initialDelaySeconds }} diff --git a/chart/templates/triggerer/triggerer-deployment.yaml b/chart/templates/triggerer/triggerer-deployment.yaml index 9f1b763f98bc9..24414a575f834 100644 --- a/chart/templates/triggerer/triggerer-deployment.yaml +++ b/chart/templates/triggerer/triggerer-deployment.yaml @@ -158,7 +158,7 @@ spec: envFrom: {{- include "custom_airflow_environment_from" . | default "\n []" | indent 10 }} env: {{- include "custom_airflow_environment" . | indent 10 }} - {{- include "standard_airflow_environment" (merge (dict "IncludeJwtSecret" false) .) | indent 10 }} + {{- include "standard_airflow_environment" . | indent 10 }} {{- if .Values.triggerer.waitForMigrations.env }} {{- tpl (toYaml .Values.triggerer.waitForMigrations.env) $ | nindent 12 }} {{- end }} @@ -206,7 +206,7 @@ spec: envFrom: {{- include "custom_airflow_environment_from" . | default "\n []" | indent 10 }} env: {{- include "custom_airflow_environment" . | indent 10 }} - {{- include "standard_airflow_environment" (merge (dict "IncludeJwtSecret" false) .) | indent 10 }} + {{- include "standard_airflow_environment" . | indent 10 }} {{- include "container_extra_envs" (list . .Values.triggerer.env) | nindent 10 }} livenessProbe: initialDelaySeconds: {{ .Values.triggerer.livenessProbe.initialDelaySeconds }} diff --git a/chart/templates/workers/worker-deployment.yaml b/chart/templates/workers/worker-deployment.yaml index a551152caa0c1..dfbc044bf5108 100644 --- a/chart/templates/workers/worker-deployment.yaml +++ b/chart/templates/workers/worker-deployment.yaml @@ -225,7 +225,7 @@ spec: - name: KRB5CCNAME value: {{ include "kerberos_ccache_path" . | quote }} {{- include "custom_airflow_environment" . | indent 10 }} - {{- include "standard_airflow_environment" (merge (dict "IncludeJwtSecret" false) .) | indent 10 }} + {{- include "standard_airflow_environment" . | indent 10 }} {{- end }} {{- if .Values.workers.waitForMigrations.enabled }} - name: wait-for-airflow-migrations @@ -253,7 +253,7 @@ spec: envFrom: {{- include "custom_airflow_environment_from" . | default "\n []" | indent 10 }} env: {{- include "custom_airflow_environment" . | indent 10 }} - {{- include "standard_airflow_environment" (merge (dict "IncludeJwtSecret" false) .) | indent 10 }} + {{- include "standard_airflow_environment" . | indent 10 }} {{- if .Values.workers.waitForMigrations.env }} {{- tpl (toYaml .Values.workers.waitForMigrations.env) $ | nindent 12 }} {{- end }} @@ -339,7 +339,7 @@ spec: - name: DUMB_INIT_SETSID value: "0" {{- include "custom_airflow_environment" . | indent 10 }} - {{- include "standard_airflow_environment" (merge (dict "IncludeJwtSecret" false) .) | indent 10 }} + {{- include "standard_airflow_environment" . | indent 10 }} {{- include "container_extra_envs" (list . .Values.workers.env) | indent 10 }} {{- if .Values.workers.kerberosSidecar.enabled }} - name: KRB5_CONFIG @@ -451,7 +451,7 @@ spec: - name: KRB5CCNAME value: {{ include "kerberos_ccache_path" . | quote }} {{- include "custom_airflow_environment" . | indent 10 }} - {{- include "standard_airflow_environment" (merge (dict "IncludeJwtSecret" false) .) | indent 10 }} + {{- include "standard_airflow_environment" . | indent 10 }} {{- end }} {{- if .Values.workers.extraContainers }} {{- tpl (toYaml .Values.workers.extraContainers) . | nindent 8 }} diff --git a/helm-tests/tests/helm_tests/airflow_aux/test_airflow_common.py b/helm-tests/tests/helm_tests/airflow_aux/test_airflow_common.py index 0f786a77fba5f..958c9cf3b02e2 100644 --- a/helm-tests/tests/helm_tests/airflow_aux/test_airflow_common.py +++ b/helm-tests/tests/helm_tests/airflow_aux/test_airflow_common.py @@ -448,8 +448,8 @@ def test_have_all_variables(self): "AIRFLOW__DATABASE__SQL_ALCHEMY_CONN", "AIRFLOW_CONN_AIRFLOW_DB", "AIRFLOW__API__SECRET_KEY", - "AIRFLOW__API_AUTH__JWT_SECRET", "AIRFLOW__CELERY__BROKER_URL", + "AIRFLOW__API_AUTH__JWT_SECRET", ] expected_vars_no_jwt = [ "AIRFLOW_HOME", @@ -468,6 +468,65 @@ def test_have_all_variables(self): f"Wrong vars in {component}" ) + def test_jwt_secret_injected_into_api_server_and_scheduler(self): + docs = render_chart( + show_only=[ + "templates/api-server/api-server-deployment.yaml", + "templates/scheduler/scheduler-deployment.yaml", + ], + ) + + for doc in docs: + component = doc["metadata"]["labels"]["component"] + env_names = jmespath.search( + f"spec.template.spec.containers[?name=='{component}'].env[].name", doc + ) + assert env_names.count("AIRFLOW__API_AUTH__JWT_SECRET") == 1, ( + f"JWT secret missing from {component}" + ) + + # it must not leak into the sidecars or init containers of those same pods + other_env_names = jmespath.search( + f"[spec.template.spec.containers[?name!='{component}'], " + "spec.template.spec.initContainers][][].env[].name", + doc, + ) + assert "AIRFLOW__API_AUTH__JWT_SECRET" not in other_env_names, ( + f"JWT secret leaked into a non-main container of {component}" + ) + + def test_jwt_secret_not_injected_into_other_components(self): + docs = render_chart( + show_only=[ + "templates/workers/worker-deployment.yaml", + "templates/triggerer/triggerer-deployment.yaml", + "templates/dag-processor/dag-processor-deployment.yaml", + ], + ) + + for doc in docs: + component = doc["metadata"]["labels"]["component"] + env_names = jmespath.search( + "[spec.template.spec.containers, spec.template.spec.initContainers][][].env[].name", doc + ) + assert "AIRFLOW__API_AUTH__JWT_SECRET" not in env_names, f"JWT secret leaked into {component}" + + def test_jwt_secret_can_be_disabled(self): + docs = render_chart( + values={"enableBuiltInSecretEnvVars": {"AIRFLOW__API_AUTH__JWT_SECRET": False}}, + show_only=[ + "templates/api-server/api-server-deployment.yaml", + "templates/scheduler/scheduler-deployment.yaml", + ], + ) + + for doc in docs: + component = doc["metadata"]["labels"]["component"] + env_names = jmespath.search( + f"spec.template.spec.containers[?name=='{component}'].env[].name", doc + ) + assert "AIRFLOW__API_AUTH__JWT_SECRET" not in env_names, f"Wrong vars in {component}" + def test_have_all_config_mounts_on_init_containers(self): docs = render_chart( values={