From 158265ea08bccc47cdc1692b54c2cd3b0faf7741 Mon Sep 17 00:00:00 2001 From: Steven Anton Date: Thu, 13 Jun 2019 13:36:17 -0700 Subject: [PATCH] [stable/airflow] Improve handling of existing secrets (#14715) * Cleaning up the existing-secret handling for Airflow Signed-off-by: Steven Anton * Bumping chart version Signed-off-by: Steven Anton * Cleaning up the existing-secret handling for Airflow Signed-off-by: Steven Anton * Bumping chart version Signed-off-by: Steven Anton * Update the README Signed-off-by: Steven Anton * A major overhaul of how secrets are handled Signed-off-by: Steven Anton * Major version bump; adding documentation Signed-off-by: Steven Anton --- stable/airflow/Chart.yaml | 2 +- stable/airflow/README.md | 87 ++++++++++++++----- stable/airflow/templates/_helpers.tpl | 73 +++++++++------- stable/airflow/templates/configmap-env.yaml | 2 +- .../airflow/templates/deployments-flower.yaml | 1 - .../templates/deployments-scheduler.yaml | 1 - stable/airflow/templates/deployments-web.yaml | 1 - stable/airflow/templates/secret-env.yaml | 16 ---- .../templates/statefulsets-workers.yaml | 1 - stable/airflow/values.yaml | 62 ++++++------- 10 files changed, 132 insertions(+), 114 deletions(-) delete mode 100644 stable/airflow/templates/secret-env.yaml diff --git a/stable/airflow/Chart.yaml b/stable/airflow/Chart.yaml index 5d74119bd1..b85dc89caf 100644 --- a/stable/airflow/Chart.yaml +++ b/stable/airflow/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v1 description: Airflow is a platform to programmatically author, schedule and monitor workflows name: airflow -version: 2.8.8 +version: 3.0.0 appVersion: 1.10.2 icon: https://airflow.apache.org/_images/pin_large.png home: https://airflow.apache.org/ diff --git a/stable/airflow/README.md b/stable/airflow/README.md index 9d08bb8e4e..6788ad85d6 100644 --- a/stable/airflow/README.md +++ b/stable/airflow/README.md @@ -184,30 +184,50 @@ $ kubectl create secret generic redshift-user --from-file=redshift-user=~/secret ``` Where `redshift-user.txt` contains the user secret as a single text string. -### Use precreated secret for airflow secrets or environment variables +### Database connection credentials -You can use a precreated secret for the connection credentials, or general environment variables. To do -so specify in values.yaml `existingAirflowSecret`, where the value is the name of the secret which has -postgresUser, postgresPassword, and redisPassword etc. is defined. If not specified, it will fall back to using -`secrets.yaml` to store the connection credentials by default. +In this chart, postgres is used as the database backing Airflow. +Additionally, if you're using the `CeleryExecutor` then redis is used. +By default, insecure username/password combinations are used. -Map each specific secret to specific environment variables in your values.yaml. Where envVar is the airflow environment -variable to populate and secretKey is the key that contains your secret value in your kubernetes secret: +For a real production deployment, it's a good idea to create secure credentials before installing the Helm chart. +For example, from the command line, run: +```bash +kubectl create secret generic airflow-postgres --from-literal=postgres-password=$(openssl rand -base64 13) +kubectl create secret generic airflow-redis --from-literal=redis-password=$(openssl rand -base64 13) +``` +Next, you can use those secrets with the Helm chart: ```yaml -existingAirflowSecret: my-airflow-secrets -airflow: - secretsMapping: - - envVar: AIRFLOW__LDAP__BIND_PASSWORD - secretKey: ldapBindPassword +# values.yaml - - envVar: POSTGRES_USER - secretKey: airflowPostgresUser +postgres: + existingSecret: airflow-postgres - - envVar: POSTGRES_PASSWORD - secretKey: airflowPostgresPassword +redis: + existingSecret: airflow-redis +``` +This approach has the additional advantage of keeping secrets outside of the Helm upgrade process. - - envVar: REDIS_PASSWORD - secretKey: airflowRedisPassword +### Additional environment variables + +It is possible to specify additional environment variables using the same format as in a pod's `.spec.containers.env` definition. +These environment variables will be mounted in the web, scheduler, and worker pods. +You can use this feature to pass additional secret environment variables to Airflow. + +Here is a simple example showing how to pass in a Fernet key and LDAP password. +Of course, for this example to work, both the `airflow` and `ldap` Kubernetes secrets must already exist in the proper namespace; be sure to create those before running Helm. +```yaml +extraEnv: + - name: AIRFLOW__CORE__FERNET_KEY + valueFrom: + secretKeyRef: + name: airflow + key: fernet-key + - name: AIRFLOW__LDAP__BIND_PASSWORD + valueFrom: + secretKeyRef: + name: ldap + key: password ``` ### Local binaries @@ -322,7 +342,7 @@ The following table lists the configurable parameters of the Airflow chart and t | `airflow.webReplicas` | how many replicas for web server | `1` | | `airflow.config` | custom airflow configuration env variables | `{}` | | `airflow.podDisruptionBudget` | control pod disruption budget | `{'maxUnavailable': 1}` | -| `airflow.secretsMapping` | override any environment variable with a secret | | +| `airflow.extraEnv` | specify additional environment variables to mount | `{}` | | `airflow.extraConfigmapMounts` | Additional configMap volume mounts on the airflow pods. | `[]` | | `airflow.podAnnotations` | annotations for scheduler, worker and web pods | `{}` | | `airflow.extraContainers` | additional containers to run in the scheduler, worker & web pods | `[]` | @@ -343,7 +363,6 @@ The following table lists the configurable parameters of the Airflow chart and t | `workers.podAnnotations` | annotations for the worker pods | `{}` | | `workers.secretsDir` | directory in which to mount secrets on worker nodes | /var/airflow/secrets | | `workers.secrets` | secrets to mount as volumes on worker nodes | [] | -| `existingAirflowSecret` | secret to use for postgres and redis connection | | | `nodeSelector` | Node labels for pod assignment | `{}` | | `affinity` | Affinity labels for pod assignment | `{}` | | `tolerations` | Toleration labels for pod assignment | `[]` | @@ -386,6 +405,7 @@ The following table lists the configurable parameters of the Airflow chart and t | `serviceAccount.create` | create a service account | `true` | | `serviceAccount.name` | the service account name | `` | | `postgresql.enabled` | create a postgres server | `true` | +| `postgresql.existingSecret` | The name of an existing secret with a key `postgresql-password` to use as the password | `nil` | | `postgresql.uri` | full URL to custom postgres setup | (undefined) | | `postgresql.portgresHost` | PostgreSQL Hostname | (undefined) | | `postgresql.postgresUser` | PostgreSQL User | `postgres` | @@ -395,6 +415,7 @@ The following table lists the configurable parameters of the Airflow chart and t | `postgresql.persistance.storageClass` | Persistant class | (undefined) | | `postgresql.persistance.accessMode` | Access mode | `ReadWriteOnce` | | `redis.enabled` | Create a Redis cluster | `true` | +| `redis.existingSecret` | The name of an existing secret with a key `redis-password` to use as the password | `nil` | | `redis.redisHost` | Redis Hostname | (undefined) | | `redis.password` | Redis password | `airflow` | | `redis.master.persistence.enabled` | Enable Redis PVC | `false` | @@ -411,8 +432,30 @@ The following table lists the configurable parameters of the Airflow chart and t Full and up-to-date documentation can be found in the comments of the `values.yaml` file. ## Upgrading + +### To 3.0.0 +This version introduces a simplified way of managing secrets, including the database credentials to postgres and redis. +With the default settings in prior versions, database credentials were generated and stored in an Airflow-managed Kubernetes secret. +However, these credentials were also stored in postgres- and redis-managed secrets (created by the respective subcharts), leading to duplication. +Moreover, it was tricky to bring your own passwords and to load additional secrets as environment variables. + +To deal with these issues, we've removed the Airflow-managed Kubernetes secret (`templates/secret-env.yaml`). +If your deployment was called `airflow`, this upgrade will delete the `airflow-env` secret. +Instead, the pods now source the database secrets from the postgres- and redis-managed secrets, i.e. the postgres password is in the `airflow-postgres` secret. +This upgrade _shouldn't_ break the deployment, but you may need to make some adjustments if you were doing something nonstandard. + +For production, it's better create random passwords before installing the Helm chart. +You can use these passwords by specifying the newly added `postgres.existingSecret` and `redis.existingSecret` parameters. + +We've also added `airflow.extraEnv`, which provides a flexible way to inject environment variables into your pods. +This parameter is great for things like the Fernet key and LDAP password. + +The following parameters are no longer necessary and have been removed: `airflow.defaultSecretsMapping`, `airflow.secretsMapping`, `airflow.existingAirflowSecret`. +If you were using them, you'll have to migrate your settings to `postgres.existingSecret`, `redis.existingSecret`, and `airflow.extraEnv`, which are described in greater depth in the documentation above. + +### To 2.8.3+ +The parameter `airflow.service.type` no longer applies to the Flower service, but the default of `ClusterIP` has been maintained. If using a custom values file and have changed the service type, also specify `flower.service.type`. + ### To 2.0.0 The parameter `workers.pod.annotations` has been renamed to `workers.podAnnotations`. If using a custom values file, rename this parameter. -### To 2.8.3+ -The parameter `airflow.service.type` no longer applies to the Flower service, but the default of `ClusterIP` has been maintained. If using a custom values file and have changed the service type, also specify `flower.service.type`. diff --git a/stable/airflow/templates/_helpers.tpl b/stable/airflow/templates/_helpers.tpl index 48b6180bc4..f71296215f 100644 --- a/stable/airflow/templates/_helpers.tpl +++ b/stable/airflow/templates/_helpers.tpl @@ -45,11 +45,31 @@ We truncate at 63 chars because some Kubernetes name fields are limited to this {{- end -}} {{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +NOTE: This is copied from the redis sub-chart and modified slightly: +*/}} +{{- define "airflow.redis.fullname" -}} +{{- if .Values.redis.fullnameOverride -}} + {{- .Values.redis.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} + {{- $name := default "redis" .Values.redis.nameOverride -}} + {{- if contains $name .Release.Name -}} + {{- .Release.Name | trunc 63 | trimSuffix "-" -}} + {{- else -}} + {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} + {{- end -}} +{{- end -}} +{{- end -}} + {{/* Create a default fully qualified redis cluster name or use the `redisHost` value if defined We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). */}} -{{- define "airflow.redis.fullname" -}} +{{- define "airflow.redis.host" -}} {{- if .Values.redis.redisHost }} {{- .Values.redis.redisHost -}} {{- else }} @@ -70,39 +90,26 @@ Create a random string if the supplied key does not exist {{- end -}} {{/* -Create the name for the airflow secret. +Create a set of environment variables to be mounted in web, scheduler, and woker pods. +For the database passwords, we actually use the secretes created by the postgres and redis sub-charts. +Note that the environment variables themselves are determined by the puckel/docker-airflow image. +See script/entrypoint.sh in that repo for more info. +The key names for postgres and redis are fixed, which is consistent with the subcharts. */}} -{{- define "airflow.secret" -}} - {{- if .Values.existingAirflowSecret -}} - {{- .Values.existingAirflowSecret -}} - {{- else -}} - {{ template "airflow.fullname" . }} - {{- end -}} -{{- end -}} - -{{/* -Map environment vars to secrets -*/}} -{{- define "airflow.mapenvsecrets" -}} - {{- $secretName := printf "%s-env" (include "airflow.fullname" .) }} - {{- $mapping := .Values.airflow.defaultSecretsMapping }} - {{- if .Values.existingAirflowSecret }} - {{- $secretName := .Values.existingAirflowSecret }} - {{- if .Values.airflow.secretsMapping }} - {{- $mapping := .Values.airflow.secretsMapping }} - {{- end }} - {{- end }} - {{- range $val := $mapping }} - {{- if $val }} - - name: {{ $val.envVar }} +{{- define "airflow.mapenvsecrets" }} + - name: POSTGRES_USER + value: {{ default "postgres" .Values.postgresql.postgresUser | quote }} + - name: POSTGRES_PASSWORD valueFrom: secretKeyRef: - {{- if $val.secretName }} - name: {{ $val.secretName }} - {{- else }} - name: {{ $secretName }} - {{- end }} - key: {{ $val.secretKey }} - {{- end }} - {{- end }} + name: {{ default (include "airflow.postgresql.fullname" .) .Values.postgresql.existingSecret }} + key: postgres-password + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ default (include "airflow.redis.fullname" .) .Values.redis.existingSecret }} + key: redis-password + {{- if .Values.airflow.extraEnv }} +{{ toYaml .Values.airflow.extraEnv | indent 2 }} + {{- end }} {{- end }} diff --git a/stable/airflow/templates/configmap-env.yaml b/stable/airflow/templates/configmap-env.yaml index cdd0fb9497..21c6cc5ef5 100644 --- a/stable/airflow/templates/configmap-env.yaml +++ b/stable/airflow/templates/configmap-env.yaml @@ -15,7 +15,7 @@ data: POSTGRES_PORT: "{{ .Values.postgresql.service.port }}" POSTGRES_DB: "{{ .Values.postgresql.postgresDatabase }}" ## Redis DB configuration - REDIS_HOST: "{{ template "airflow.redis.fullname" . }}" + REDIS_HOST: "{{ template "airflow.redis.host" . }}" REDIS_PORT: "{{ .Values.redis.master.port }}" ## Flower PORT FLOWER_PORT: "5555" diff --git a/stable/airflow/templates/deployments-flower.yaml b/stable/airflow/templates/deployments-flower.yaml index 5f27520018..fea951eb02 100644 --- a/stable/airflow/templates/deployments-flower.yaml +++ b/stable/airflow/templates/deployments-flower.yaml @@ -26,7 +26,6 @@ spec: metadata: annotations: checksum/config-env: {{ include (print $.Template.BasePath "/configmap-env.yaml") . | sha256sum }} - checksum/secret-env: {{ include (print $.Template.BasePath "/secret-env.yaml") . | sha256sum }} labels: app: {{ template "airflow.name" . }} component: flower diff --git a/stable/airflow/templates/deployments-scheduler.yaml b/stable/airflow/templates/deployments-scheduler.yaml index 791343bf20..603dbc7f38 100644 --- a/stable/airflow/templates/deployments-scheduler.yaml +++ b/stable/airflow/templates/deployments-scheduler.yaml @@ -30,7 +30,6 @@ spec: checksum/config-scripts: {{ include (print $.Template.BasePath "/configmap-scripts.yaml") . | sha256sum }} checksum/config-variables-pools: {{ include (print $.Template.BasePath "/configmap-variables-pools.yaml") . | sha256sum }} checksum/secret-connections: {{ include (print $.Template.BasePath "/secret-connections.yaml") . | sha256sum }} - checksum/secret-env: {{ include (print $.Template.BasePath "/secret-env.yaml") . | sha256sum }} {{- if .Values.airflow.podAnnotations }} {{ toYaml .Values.airflow.podAnnotations | indent 8 }} {{- end }} diff --git a/stable/airflow/templates/deployments-web.yaml b/stable/airflow/templates/deployments-web.yaml index 3227870edd..27ea950ba1 100644 --- a/stable/airflow/templates/deployments-web.yaml +++ b/stable/airflow/templates/deployments-web.yaml @@ -28,7 +28,6 @@ spec: checksum/config-env: {{ include (print $.Template.BasePath "/configmap-env.yaml") . | sha256sum }} checksum/config-git-clone: {{ include (print $.Template.BasePath "/configmap-git-clone.yaml") . | sha256sum }} checksum/config-scripts: {{ include (print $.Template.BasePath "/configmap-scripts.yaml") . | sha256sum }} - checksum/secret-env: {{ include (print $.Template.BasePath "/secret-env.yaml") . | sha256sum }} {{- if .Values.airflow.podAnnotations }} {{ toYaml .Values.airflow.podAnnotations | indent 8 }} {{- end }} diff --git a/stable/airflow/templates/secret-env.yaml b/stable/airflow/templates/secret-env.yaml deleted file mode 100644 index f03edc4b6e..0000000000 --- a/stable/airflow/templates/secret-env.yaml +++ /dev/null @@ -1,16 +0,0 @@ -{{- if not .Values.existingAirflowSecret -}} -apiVersion: v1 -kind: Secret -metadata: - name: {{ template "airflow.fullname" . }}-env - labels: - app: {{ template "airflow.fullname" . }} - chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - release: "{{ .Release.Name }}" - heritage: "{{ .Release.Service }}" -type: Opaque -data: - postgresUser: {{ .Values.postgresql.postgresUser | b64enc | quote }} - postgresPassword: {{ .Values.postgresql.postgresPassword | b64enc | quote }} - redisPassword: {{ .Values.redis.password | b64enc | quote }} -{{- end -}} diff --git a/stable/airflow/templates/statefulsets-workers.yaml b/stable/airflow/templates/statefulsets-workers.yaml index aaef7ceed3..a29f296da1 100644 --- a/stable/airflow/templates/statefulsets-workers.yaml +++ b/stable/airflow/templates/statefulsets-workers.yaml @@ -32,7 +32,6 @@ spec: checksum/config-env: {{ include (print $.Template.BasePath "/configmap-env.yaml") . | sha256sum }} checksum/config-git-clone: {{ include (print $.Template.BasePath "/configmap-git-clone.yaml") . | sha256sum }} checksum/config-scripts: {{ include (print $.Template.BasePath "/configmap-scripts.yaml") . | sha256sum }} - checksum/secret-env: {{ include (print $.Template.BasePath "/secret-env.yaml") . | sha256sum }} {{- if .Values.airflow.podAnnotations }} {{ toYaml .Values.airflow.podAnnotations | indent 8 }} {{- end }} diff --git a/stable/airflow/values.yaml b/stable/airflow/values.yaml index 37254955d4..7d414e9256 100644 --- a/stable/airflow/values.yaml +++ b/stable/airflow/values.yaml @@ -17,41 +17,21 @@ airflow: # subPath: file.yaml - ## When existingAirflowSecret is defined, secretsMapping can be - ## overridden. When no secretName is given then the value of - ## existingAirflowSecret is assumed. - ## secretsMapping: - ## - envVar: AIRFLOW__LDAP__BIND_PASSWORD - ## secretName: ldap - ## secretKey: ldapBindPassword - ## - envVar: AIRFLOW__ATLAS__PASSWORD - ## secretKey: atlasPassword - ## - envVar: AIRFLOW__SMTP__PASSWORD - ## secretKey: smtpPassword - ## - envVar: AIRFLOW__KUBERNETES__GIT_PASSWORD - ## secretKey: kubernetesGitPassword - ## - envVar: POSTGRES_USER - ## secretName: postgres - ## secretKey: postgresUser - ## - envVar: POSTGRES_PASSWORD - ## secretName: postgres - ## secretKey: postgresPassword - ## - envVar: REDIS_PASSWORD - ## secretName: redis - ## secretKey: redisPassword - secretsMapping: + ## + ## Extra environment variables to mount in the web, scheduler, and worker pods: + extraEnv: + # - name: AIRFLOW__CORE__FERNET_KEY + # valueFrom: + # secretKeyRef: + # name: airflow + # key: fernet_key + # - name: AIRFLOW__LDAP__BIND_PASSWORD + # valueFrom: + # secretKeyRef: + # name: ldap + # key: password - ## Used only when existingAirflowSecret is null, in which case - ## a secret will be created with a default name and the following mapping. - defaultSecretsMapping: - - envVar: POSTGRES_USER - secretKey: postgresUser - - envVar: POSTGRES_PASSWORD - secretKey: postgresPassword - - envVar: REDIS_PASSWORD - secretKey: redisPassword - ## ## You will need to define your fernet key: ## Generate fernetKey with: @@ -468,10 +448,6 @@ serviceAccount: ## If not set and create is true, a name is generated using the fullname template name: -## -## Define existing secret for postgresql and redis -## If not specified, creates secret with postgres and redis values -existingAirflowSecret: "" ## ## Configuration values for the postgresql dependency. @@ -481,6 +457,12 @@ postgresql: ## Use the PostgreSQL chart dependency. ## Set to false if bringing your own PostgreSQL. enabled: true + + ## + ## The name of an existing secret that contains the postgres password. + ## Note that the secret must have a key called postgres-password. + existingSecret: + ## ## If you are bringing your own PostgreSQL, you should set postgresHost and ## also probably service.port, postgresUser, postgresPassword, and postgresDatabase @@ -520,6 +502,12 @@ redis: ## Use the redis chart dependency. ## Set to false if bringing your own redis. enabled: true + + ## + ## The name of an existing secret that contains the redis password. + ## Note that the secret must have a key called redis-password. + existingSecret: + ## ## If you are bringing your own redis, you can set the host in redisHost. ## redisHost: