[stable/airflow] Improve handling of existing secrets (#14715)

* Cleaning up the existing-secret handling for Airflow

Signed-off-by: Steven Anton <steven.m.anton@gmail.com>

* Bumping chart version

Signed-off-by: Steven Anton <steven.m.anton@gmail.com>

* Cleaning up the existing-secret handling for Airflow

Signed-off-by: Steven Anton <steven.m.anton@gmail.com>

* Bumping chart version

Signed-off-by: Steven Anton <steven.m.anton@gmail.com>

* Update the README

Signed-off-by: Steven Anton <steven.m.anton@gmail.com>

* A major overhaul of how secrets are handled

Signed-off-by: Steven Anton <steven.m.anton@gmail.com>

* Major version bump; adding documentation

Signed-off-by: Steven Anton <steven.m.anton@gmail.com>
This commit is contained in:
Steven Anton
2019-06-13 13:36:17 -07:00
committed by Kubernetes Prow Robot
parent bf53797903
commit 158265ea08
10 changed files with 132 additions and 114 deletions
+1 -1
View File
@@ -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/
+65 -22
View File
@@ -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`.
+40 -33
View File
@@ -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 }}
+1 -1
View File
@@ -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"
@@ -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
@@ -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 }}
@@ -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 }}
-16
View File
@@ -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 -}}
@@ -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 }}
+25 -37
View File
@@ -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: