diff --git a/chart/templates/_helpers.yaml b/chart/templates/_helpers.yaml index 2100e2d4267f2..8838b837dca0f 100644 --- a/chart/templates/_helpers.yaml +++ b/chart/templates/_helpers.yaml @@ -91,13 +91,20 @@ If release name contains chart name it will be used as a full name. name: {{ template "airflow_metadata_secret" . }} key: kedaConnection {{- end }} - {{- if .Values.enableBuiltInSecretEnvVars.AIRFLOW__WEBSERVER__SECRET_KEY }} + {{- if and (semverCompare "<3.0.0" .Values.airflowVersion) .Values.enableBuiltInSecretEnvVars.AIRFLOW__WEBSERVER__SECRET_KEY }} - name: AIRFLOW__WEBSERVER__SECRET_KEY valueFrom: secretKeyRef: name: {{ template "webserver_secret_key_secret" . }} key: webserver-secret-key {{- end }} + {{- if and (semverCompare ">=3.0.0" .Values.airflowVersion) .Values.enableBuiltInSecretEnvVars.AIRFLOW__API__SECRET_KEY }} + - name: AIRFLOW__API__SECRET_KEY + valueFrom: + secretKeyRef: + name: {{ template "api_secret_key_secret" . }} + key: api-secret-key + {{- end }} {{- if and (semverCompare ">=3.0.0" .Values.airflowVersion) .Values.enableBuiltInSecretEnvVars.AIRFLOW__API_AUTH__JWT_SECRET }} - name: AIRFLOW__API_AUTH__JWT_SECRET valueFrom: @@ -411,6 +418,10 @@ If release name contains chart name it will be used as a full name. {{- default (printf "%s-webserver-secret-key" (include "airflow.fullname" .)) .Values.webserverSecretKeySecretName }} {{- end }} +{{- define "api_secret_key_secret" -}} + {{- default (printf "%s-api-secret-key" (include "airflow.fullname" .)) .Values.apiSecretKeySecretName }} +{{- end }} + {{- define "redis_password_secret" -}} {{- default (printf "%s-redis-password" .Release.Name) .Values.redis.passwordSecretName }} {{- end }} diff --git a/chart/templates/secrets/api-secret-key-secret.yaml b/chart/templates/secrets/api-secret-key-secret.yaml new file mode 100644 index 0000000000000..52e0fff689f44 --- /dev/null +++ b/chart/templates/secrets/api-secret-key-secret.yaml @@ -0,0 +1,44 @@ +{{/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/}} + +############################################ +## Airflow Api Flask Secret Key Secret +############################################ +{{- if and (semverCompare ">=3.0.0" .Values.airflowVersion) (not .Values.apiSecretKeySecretName) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "airflow.fullname" . }}-api-secret-key + labels: + tier: airflow + component: api-server + release: {{ .Release.Name }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + heritage: {{ .Release.Service }} + {{- with .Values.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.apiSecretAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +type: Opaque +data: + api-secret-key: {{ (.Values.apiSecretKey) | default (randAlphaNum 32) | b64enc | quote }} +{{- end }} diff --git a/chart/templates/secrets/fernetkey-secret.yaml b/chart/templates/secrets/fernetkey-secret.yaml index 5461b56f03c28..a9a1f6016938b 100644 --- a/chart/templates/secrets/fernetkey-secret.yaml +++ b/chart/templates/secrets/fernetkey-secret.yaml @@ -21,7 +21,6 @@ ## Airflow Fernet Key Secret ################################# {{- if not .Values.fernetKeySecretName }} -{{- $generated_fernet_key := (randAlphaNum 32 | b64enc) }} apiVersion: v1 kind: Secret metadata: @@ -43,5 +42,5 @@ metadata: {{- end }} type: Opaque data: - fernet-key: {{ (default $generated_fernet_key .Values.fernetKey) | b64enc | quote }} + fernet-key: {{ (.Values.fernetKey) | default (randAlphaNum 32) | b64enc | quote }} {{- end }} diff --git a/chart/templates/secrets/webserver-secret-key-secret.yaml b/chart/templates/secrets/webserver-secret-key-secret.yaml index 5a9025236e7af..e7803c4d84033 100644 --- a/chart/templates/secrets/webserver-secret-key-secret.yaml +++ b/chart/templates/secrets/webserver-secret-key-secret.yaml @@ -20,8 +20,7 @@ ############################################ ## Airflow Webserver Flask Secret Key Secret ############################################ -{{- if not .Values.webserverSecretKeySecretName }} -{{ $generated_secret_key := (randAlphaNum 32 | b64enc) }} +{{- if and (semverCompare "<3.0.0" .Values.airflowVersion) .Values.webserver.enabled (not .Values.webserverSecretKeySecretName) }} apiVersion: v1 kind: Secret metadata: @@ -41,5 +40,5 @@ metadata: {{- end }} type: Opaque data: - webserver-secret-key: {{ (default $generated_secret_key .Values.webserverSecretKey) | b64enc | quote }} + webserver-secret-key: {{ (.Values.webserverSecretKey) | default (randAlphaNum 32) | b64enc | quote }} {{- end }} diff --git a/chart/values.schema.json b/chart/values.schema.json index 1bfe3c9757b69..b329b4a0dde76 100644 --- a/chart/values.schema.json +++ b/chart/values.schema.json @@ -1064,6 +1064,11 @@ "type": "boolean", "default": true }, + "AIRFLOW__API__SECRET_KEY": { + "description": "Enable ``AIRFLOW__API__SECRET_KEY`` variable to be read from the Api Secret Key Secret", + "type": "boolean", + "default": true + }, "AIRFLOW__API_AUTH__JWT_SECRET": { "description": "Enable ``AIRFLOW__API_AUTH__JWT_SECRET`` variable to be read from the JWT Secret", "type": "boolean", @@ -1464,6 +1469,33 @@ "type": "string" } }, + "apiSecretKey": { + "description": "The Flask secret key for Airflow Api to encrypt browser session.", + "type": [ + "string", + "null" + ], + "x-docsSection": "Common", + "default": null + }, + "apiSecretAnnotations": { + "description": "Annotations to add to the Api secret.", + "type": "object", + "x-docsSection": "Common", + "default": {}, + "additionalProperties": { + "type": "string" + } + }, + "apiSecretKeySecretName": { + "description": "The Secret name containing Flask secret_key for the Api.", + "type": [ + "string", + "null" + ], + "x-docsSection": "Airflow", + "default": null + }, "jwtSecret": { "description": "Secret key used to encode and decode JWTs to authenticate to public and private APIs (can only be set during install, not upgrade).", "type": [ diff --git a/chart/values.yaml b/chart/values.yaml index 5d85210d419f1..cff157e6d50ae 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -407,6 +407,7 @@ enableBuiltInSecretEnvVars: AIRFLOW__CORE__SQL_ALCHEMY_CONN: true AIRFLOW__DATABASE__SQL_ALCHEMY_CONN: true AIRFLOW_CONN_AIRFLOW_DB: true + AIRFLOW__API__SECRET_KEY: true AIRFLOW__API_AUTH__JWT_SECRET: true AIRFLOW__WEBSERVER__SECRET_KEY: true AIRFLOW__CELERY__CELERY_RESULT_BACKEND: true @@ -551,11 +552,11 @@ fernetKeySecretName: ~ # Add custom annotations to the fernet key secret fernetKeySecretAnnotations: {} -# Flask secret key for Airflow Webserver: `[webserver] secret_key` in airflow.cfg -webserverSecretKey: ~ -# Add custom annotations to the webserver secret -webserverSecretAnnotations: {} -webserverSecretKeySecretName: ~ +# Flask secret key for Airflow 3+ Api: `[api] secret_key` in airflow.cfg +apiSecretKey: ~ +# Add custom annotations to the api secret +apiSecretAnnotations: {} +apiSecretKeySecretName: ~ # Secret key used to encode and decode JWTs: `[api_auth] jwt_secret` in airflow.cfg jwtSecret: ~ @@ -563,6 +564,12 @@ jwtSecret: ~ jwtSecretAnnotations: {} jwtSecretName: ~ +# Flask secret key for Airflow <3 Webserver: `[webserver] secret_key` in airflow.cfg +webserverSecretKey: ~ +# Add custom annotations to the webserver secret +webserverSecretAnnotations: {} +webserverSecretKeySecretName: ~ + # In order to use kerberos you need to create secret containing the keytab file # The secret name should follow naming convention of the application where resources are # name {{ .Release-name }}-. In case of the keytab file, the postfix is "kerberos-keytab" 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 e6b247afb1cd0..f4ad5b6e170e3 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 @@ -323,6 +323,7 @@ def test_should_disable_some_variables(self): "enableBuiltInSecretEnvVars": { "AIRFLOW__CORE__SQL_ALCHEMY_CONN": False, "AIRFLOW__DATABASE__SQL_ALCHEMY_CONN": False, + "AIRFLOW__API__SECRET_KEY": False, "AIRFLOW__API_AUTH__JWT_SECRET": False, "AIRFLOW__WEBSERVER__SECRET_KEY": False, # the following vars only appear if remote logging is set, so disabling them in this test is kind of a no-op @@ -370,7 +371,7 @@ def test_have_all_variables(self): "AIRFLOW__CORE__SQL_ALCHEMY_CONN", "AIRFLOW__DATABASE__SQL_ALCHEMY_CONN", "AIRFLOW_CONN_AIRFLOW_DB", - "AIRFLOW__WEBSERVER__SECRET_KEY", + "AIRFLOW__API__SECRET_KEY", "AIRFLOW__API_AUTH__JWT_SECRET", "AIRFLOW__CELERY__BROKER_URL", ] diff --git a/helm-tests/tests/helm_tests/airflow_aux/test_basic_helm_chart.py b/helm-tests/tests/helm_tests/airflow_aux/test_basic_helm_chart.py index a3cc64b96688e..46f617943a884 100644 --- a/helm-tests/tests/helm_tests/airflow_aux/test_basic_helm_chart.py +++ b/helm-tests/tests/helm_tests/airflow_aux/test_basic_helm_chart.py @@ -38,7 +38,6 @@ ("Secret", "test-basic-airflow-metadata"), ("Secret", "test-basic-broker-url"), ("Secret", "test-basic-fernet-key"), - ("Secret", "test-basic-airflow-webserver-secret-key"), ("Secret", "test-basic-redis-password"), ("Secret", "test-basic-postgresql"), ("ConfigMap", "test-basic-airflow-config"), @@ -71,6 +70,7 @@ ("Deployment", "test-basic-airflow-dag-processor"), ("ServiceAccount", "test-basic-airflow-api-server"), ("ServiceAccount", "test-basic-airflow-dag-processor"), + ("Secret", "test-basic-airflow-api-secret-key"), ("Secret", "test-basic-jwt-secret"), } ) @@ -82,6 +82,7 @@ ("Service", "test-basic-airflow-webserver"), ("Deployment", "test-basic-airflow-webserver"), ("ServiceAccount", "test-basic-airflow-webserver"), + ("Secret", "test-basic-airflow-webserver-secret-key"), } ) @@ -137,7 +138,6 @@ def test_basic_deployments(self, version): ("Secret", "test-basic-metadata"), ("Secret", "test-basic-broker-url"), ("Secret", "test-basic-fernet-key"), - ("Secret", "test-basic-webserver-secret-key"), ("Secret", "test-basic-postgresql"), ("Secret", "test-basic-redis-password"), ("ConfigMap", "test-basic-config"), @@ -171,6 +171,7 @@ def test_basic_deployments(self, version): ("ServiceAccount", "test-basic-api-server"), ("ServiceAccount", "test-basic-dag-processor"), ("Service", "test-basic-triggerer"), + ("Secret", "test-basic-api-secret-key"), ("Secret", "test-basic-jwt-secret"), ) ) @@ -180,6 +181,7 @@ def test_basic_deployments(self, version): ("Deployment", "test-basic-webserver"), ("Service", "test-basic-webserver"), ("ServiceAccount", "test-basic-webserver"), + ("Secret", "test-basic-webserver-secret-key"), ) ) if version == "default": @@ -238,7 +240,6 @@ def test_basic_deployment_with_standalone_dag_processor(self, version): ("Secret", "test-basic-metadata"), ("Secret", "test-basic-broker-url"), ("Secret", "test-basic-fernet-key"), - ("Secret", "test-basic-webserver-secret-key"), ("Secret", "test-basic-postgresql"), ("Secret", "test-basic-redis-password"), ("ConfigMap", "test-basic-config"), @@ -271,6 +272,7 @@ def test_basic_deployment_with_standalone_dag_processor(self, version): ("Deployment", "test-basic-api-server"), ("Service", "test-basic-api-server"), ("ServiceAccount", "test-basic-api-server"), + ("Secret", "test-basic-api-secret-key"), ("Secret", "test-basic-jwt-secret"), } ) @@ -280,6 +282,7 @@ def test_basic_deployment_with_standalone_dag_processor(self, version): ("Service", "test-basic-webserver"), ("Deployment", "test-basic-webserver"), ("ServiceAccount", "test-basic-webserver"), + ("Secret", "test-basic-webserver-secret-key"), } ) assert list_of_kind_names_tuples == expected @@ -447,7 +450,6 @@ def test_labels_are_valid(self, airflow_version): (f"{release_name}-statsd", "Deployment", "statsd"), (f"{release_name}-statsd", "Service", "statsd"), (f"{release_name}-statsd-policy", "NetworkPolicy", "statsd-policy"), - (f"{release_name}-webserver-secret-key", "Secret", "webserver"), (f"{release_name}-worker", "Service", "worker"), (f"{release_name}-worker", "StatefulSet", "worker"), (f"{release_name}-worker-policy", "NetworkPolicy", "airflow-worker-policy"), @@ -460,14 +462,17 @@ def test_labels_are_valid(self, airflow_version): if self._is_airflow_3_or_above(airflow_version): kind_names_tuples += [ (f"{release_name}-api-server", "Service", "api-server"), - (f"{release_name}-api-server-policy", "NetworkPolicy", "airflow-api-server-policy"), (f"{release_name}-api-server", "Deployment", "api-server"), + (f"{release_name}-airflow-api-server", "ServiceAccount", "api-server"), + (f"{release_name}-api-secret-key", "Secret", "api-server"), + (f"{release_name}-api-server-policy", "NetworkPolicy", "airflow-api-server-policy"), ] else: kind_names_tuples += [ (f"{release_name}-airflow-webserver", "ServiceAccount", "webserver"), (f"{release_name}-webserver", "Deployment", "webserver"), (f"{release_name}-webserver", "Service", "webserver"), + (f"{release_name}-webserver-secret-key", "Secret", "webserver"), (f"{release_name}-webserver-policy", "NetworkPolicy", "airflow-webserver-policy"), (f"{release_name}-ingress", "Ingress", "airflow-ingress"), ] diff --git a/helm-tests/tests/helm_tests/apiserver/test_apiserver.py b/helm-tests/tests/helm_tests/apiserver/test_apiserver.py index 08796bde0c855..2db007234fed1 100644 --- a/helm-tests/tests/helm_tests/apiserver/test_apiserver.py +++ b/helm-tests/tests/helm_tests/apiserver/test_apiserver.py @@ -90,3 +90,19 @@ def test_should_add_annotations_to_jwt_secret(self): assert "annotations" in jmespath.search("metadata", docs) assert jmespath.search("metadata.annotations", docs)["test_annotation"] == "test_annotation_value" + + +class TestApiSecretKeySecret: + """Tests api secret key secret.""" + + def test_should_add_annotations_to_api_secret_key_secret(self): + docs = render_chart( + values={ + "airflowVersion": "3.0.0", + "apiSecretAnnotations": {"test_annotation": "test_annotation_value"}, + }, + show_only=["templates/secrets/api-secret-key-secret.yaml"], + )[0] + + assert "annotations" in jmespath.search("metadata", docs) + assert jmespath.search("metadata.annotations", docs)["test_annotation"] == "test_annotation_value" diff --git a/helm-tests/tests/helm_tests/security/test_rbac.py b/helm-tests/tests/helm_tests/security/test_rbac.py index 3a4476644409a..aa93a91b691fe 100644 --- a/helm-tests/tests/helm_tests/security/test_rbac.py +++ b/helm-tests/tests/helm_tests/security/test_rbac.py @@ -47,7 +47,6 @@ ("Secret", "test-rbac-broker-url"), ("Secret", "test-rbac-fernet-key"), ("Secret", "test-rbac-redis-password"), - ("Secret", "test-rbac-webserver-secret-key"), ("Job", "test-rbac-create-user"), ("Job", "test-rbac-run-airflow-migrations"), ("CronJob", "test-rbac-cleanup"), @@ -121,6 +120,7 @@ def _get_object_tuples(self, version, sa: bool = True): ("Service", "test-rbac-api-server"), ("Deployment", "test-rbac-api-server"), ("Deployment", "test-rbac-dag-processor"), + ("Secret", "test-rbac-api-secret-key"), ("Secret", "test-rbac-jwt-secret"), ) ) @@ -132,6 +132,7 @@ def _get_object_tuples(self, version, sa: bool = True): ( ("Service", "test-rbac-webserver"), ("Deployment", "test-rbac-webserver"), + ("Secret", "test-rbac-webserver-secret-key"), ) ) if sa: diff --git a/helm-tests/tests/helm_tests/webserver/test_webserver.py b/helm-tests/tests/helm_tests/webserver/test_webserver.py index d4b2a3d18873a..c70844555a2e3 100644 --- a/helm-tests/tests/helm_tests/webserver/test_webserver.py +++ b/helm-tests/tests/helm_tests/webserver/test_webserver.py @@ -1258,6 +1258,7 @@ class TestWebserverSecretKeySecret: def test_should_add_annotations_to_webserver_secret_key_secret(self): docs = render_chart( values={ + "airflowVersion": "2.10.5", "webserverSecretAnnotations": {"test_annotation": "test_annotation_value"}, }, show_only=["templates/secrets/webserver-secret-key-secret.yaml"],