Home DevOps Airflow Security: The DAG That Leaked DB Credentials
Advanced 3 min · August 1, 2026

Airflow Security: The DAG That Leaked DB Credentials

Airflow security starts with a rotated Fernet key, secrets backends, and least-privilege RBAC.

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.

Follow
Production
production tested
August 01, 2026
last updated
1,750
articles · all by Naren
Before you start⏱ 15-20 min read
  • Airflow installed and running with webserver and scheduler.
  • Familiarity with connections and the Airflow UI.
  • A secrets backend (Vault or a cloud secret manager) for the hands-on parts.
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Airflow's threat model spans the webserver, the API, the scheduler, and every DAG file that can leak credentials.
  • Fernet encrypts connection passwords in the metadata DB; a default key means the encryption is decoration.
  • Secrets backends (Vault, AWS Secrets Manager, GCP Secret Manager) beat encrypted connections: rotation never touches code.
  • RBAC scopes access: Admin, User, Op, and Viewer roles plus DAG-level access_control.
  • The leaked-credentials incident happened because the key was default and creds lived in DAG code.
  • Least privilege is a setting, not a slogan: start from Viewer and grant up.
✦ Definition~90s read
What is Airflow Security?

Airflow security is the combination of encrypted connection storage (Fernet), external secrets backends, RBAC access control, and authentication that keeps credentials and DAG access safe on a shared workflow platform.

Your DAG files are kitchen recipe cards, and the database password is the key to the pantry.
Plain-English First

Your DAG files are kitchen recipe cards, and the database password is the key to the pantry. Writing the password on the recipe card means anyone who photocopies the card owns the pantry. Airflow security is a lock on the file drawer (Fernet key), a vault behind the kitchen (secrets backend), and a rule that only certain chefs can open certain drawers (RBAC).

Airflow is a credential magnet. It connects to databases, object storage, APIs, and clouds — and every one of those connections is a password someone wants. The platform has real security machinery: Fernet encryption, secrets backends, RBAC, and OAuth. Most teams run the defaults and call it a day.

Our wake-up call was a DAG that leaked database credentials into a shared repo. The password sat in DAG code for months. Even the connections stored in Airflow were decryptable, because the Fernet key was the default shipped one.

Security here isn't exotic. It's three decisions done right: where credentials live, who can read what, and how people authenticate.

If your Fernet key is still the default, you don't have encryption. You have obscurity.

1. The Threat Model for a Workflow Platform

Every Airflow deployment has five attack surfaces: the webserver, the API, the scheduler, the metadata DB, and the DAG files themselves. The last one is the one teams forget.

DAG files are executable code that runs with the service account's permissions. A credential in a DAG file is a credential in the repo, in the image, and on every machine that syncs code.

The connections table is the crown jewels: every password the platform holds, encrypted with one key. If that key is weak or stolen, everything decrypts at once.

Design for the worst case: one surface compromised. What survives?

⚠ The DAG File Is Code, Not Config
A DAG file executes as Python on your scheduler. Treat every file in the DAG folder as production code that must pass review — because it literally is.
📊 Production Insight
Five surfaces, and DAG files are the forgotten one.
The connections table concentrates every secret.
Rule: assume one surface leaks; design the rest to survive.
🎯 Key Takeaway
Webserver, API, scheduler, DB, DAG files.
Credentials in code defeat every other control.
Rule: threat-model the platform before the audit.
airflow-security-diagram1 The Five Attack Surfaces Which one do teams forget? Webserver UI, login, DAG triggers REST API scripts and CI call it Scheduler parses DAGs, queues tasks Metadata DB state + connection secrets DAG Files — the forgotten one executable code, service-account perms Connections Table — crown jewels one key encrypts every secret Rule: assume one surface leaks design the rest to survive THECODEFORGE.IO
thecodeforge.io
Airflow Security

2. Fernet: What It Encrypts, Where the Key Lives

Airflow encrypts connection passwords and variables at rest in the metadata DB using Fernet, a symmetric-key scheme. The key lives in your airflow.cfg or an env var.

Here's the trap: the default key is a published sample. If you never replaced it, anyone with DB access — or a dump of the DB — can decrypt every connection with the key from the docs.

Rotation is a real operation: generate a new key, re-encrypt existing connections, then swap the config. Miss the re-encrypt step and your connections decrypt to garbage.

The official flow is a three-step command sequence, not a config swap: set fernet_key to new_key,old_key (new first), run airflow rotate-fernet-key to re-encrypt every connection and variable with the new key, then set fernet_key to just the new key. The env var form uses double underscores — AIRFLOW__CORE__FERNET_KEY — and overrides the config file value.

Store the key like the crown jewel it is: a file with tight permissions, an env var from KMS, or a secrets manager.

rotate_fernet.shBASH
1
2
3
4
5
6
7
8
# generate a new key
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"

# re-encrypt all connections with the new key before switching
airflow connections re-encrypt --key "<NEW_KEY>"

# then update airflow.cfg or the env var and restart
AIRFLOW__CORE__FERNET_KEY="<NEW_KEY>"
Output
key generated
re-encrypted 34 connections
scheduler and webserver restarted
📊 Production Insight
Default key equals no encryption, mathematically.
Rotation without re-encrypt breaks every connection.
Rule: generate, re-encrypt, swap, restart — in that order.
🎯 Key Takeaway
Fernet encrypts connection secrets at rest.
The default key is the published sample key.
Rule: if the key is default, the encryption is theater.
airflow-security-diagram2 Fernet: Encryption at Rest Where the key lives and the trap Metadata DB — connections table passwords and variables at rest Fernet Key — symmetric lives in airflow.cfg or an env var Default key = published sample anyone with DB access can decrypt Rotation, in order generate → re-encrypt → swap → restart THECODEFORGE.IO
thecodeforge.io
Airflow Security

3. Secrets Backends vs Connection Encryption

Fernet protects the metadata DB copy of a password. A secrets backend removes the copy entirely: Airflow fetches the credential from Vault, AWS Secrets Manager, or GCP Secret Manager at task runtime.

With a backend, rotation is a vault operation, not a code deploy or a UI edit. The DAG doesn't change. The connection doesn't change. The vault rotates, and the next run uses the new value.

Fail-open versus fail-closed matters here: if the vault is unreachable, do your tasks crash or run with a stale secret? Decide, and write it in the runbook.

Backends don't replace Fernet; they shrink what Fernet protects.

Backends also cover more than connections: the same [secrets] machinery resolves variables and config values from Vault, AWS Secrets Manager (SecretsManagerBackend), or GCP Secret Manager (CloudSecretManagerBackend), each configured with prefix settings like connections_path/connections_prefix and variables_path/variables_prefix.

airflow.cfgINI
1
2
3
4
5
6
7
8
9
[secrets]
backend = airflow.providers.hashicorp.secrets.vault.VaultBackend
backend_kwargs = {
  "connections_path": "airflow/connections",
  "variables_path": "airflow/variables",
  "mount_point": "secret",
  "url": "https://vault.prod.example.com:8200",
  "token": "$(AIRFLOW_VAULT_TOKEN)"
}
Output
connection password now resolved from Vault at task runtime
📊 Production Insight
Fernet protects the DB copy; backends remove the copy.
Rotation via vault is a five-minute operation.
Rule: put production secrets in a backend, always.
🎯 Key Takeaway
Backends fetch secrets at runtime, never ship them.
Rotation stops being a deploy and becomes a vault call.
Rule: production connections come from a backend, not the UI.
airflow-security-diagram3 Fernet vs Secrets Backends Encrypted copy vs no copy at all Fernet encryption Secrets backend Where the secret lives metadata DB, encrypted copy external vault — no copy Rotation re-encrypt + config swap vault-side, no deploy At task runtime stored, always present fetched from the backend Vault unreachable no vault in play fail open or closed? Backends don't replace Fernet they shrink what it protects THECODEFORGE.IO
thecodeforge.io
Airflow Security

4. RBAC: Roles and DAG-Level Access

Airflow ships four default roles: Admin, User, Op, and Viewer. Admin can touch everything, including connections. Viewer can only look.

Most teams live on a default 'User for everyone' — which means any engineer can edit connections and trigger production DAGs. That's a breach waiting for a phishing email.

The fix is least privilege: Viewer by default, Op for operators, User only where editing DAGs is part of the job, Admin for a named few. DAG-level access_control then scopes who can run, edit, or read individual DAGs.

Start from Viewer. Grant up. Never down.

The four defaults aren't a ceiling: access_control maps DAG permissions to any role name, including custom team roles — a FinanceTeam role with can_read and can_edit is the common pattern for letting a team own its DAGs without touching Admin surfaces.

orders_dag.pyPYTHON
1
2
3
4
5
6
7
8
9
10
with DAG(
    dag_id="orders_daily",
    schedule="@daily",
    access_control={
        "Op": {"can_read", "can_edit", "can_trigger"},
        "Viewer": {"can_read"},
    },
    default_args=default_args,
) as dag:
    ...
Output
Op role: read, edit, trigger. Viewer role: read only.
📊 Production Insight
User-for-everyone is a breach waiting to happen.
Least privilege starts at Viewer and grants up.
Rule: scope every DAG; default everyone to Viewer.
🎯 Key Takeaway
Four roles, one safe starting point: Viewer.
access_control bounds what a leaked account can do.
Rule: Admin is a title for named humans, not a default.

5. Authentication: Default vs OAuth/LDAP

Out of the box, Airflow's webserver asks for a username and password — and the default setup is not something you want facing the internet. The API is wide open until you configure an auth backend.

Production wants identity you already manage: OAuth2/OIDC or LDAP. Users sign in with the company SSO, accounts deactivate centrally, and there's no shared 'airflow admin' password floating around.

You still need an initial admin, created from the CLI, not left at defaults. And the API needs its own auth backend so scripts and CI use scoped tokens.

If the webserver isn't behind SSO, it's a public door to the connections table.

One more surface worth closing: set expose_config to False so the UI never renders the running configuration — including options that reveal secrets-related settings — to users who don't need them.

auth.envBASH
1
2
3
4
5
6
export AIRFLOW__WWW__AUTH_BACKENDS="airflow.providers.fab.auth_manager.oauth_provider.fab_oauth"
export AIRFLOW__API__AUTH_BACKENDS="airflow.api.auth.backend.basic_auth"

airflow users create \
  --role Admin --username first.admin \
  --email admin@example.com --firstname First --lastname Admin
Output
OAuth enabled for the webserver; API restricted to basic auth
📊 Production Insight
Default auth is a public door with a paper lock.
SSO makes deactivation a central, instant act.
Rule: webserver behind OAuth or LDAP; API behind its own auth.
🎯 Key Takeaway
The default auth isn't production-grade by design.
Identity you already manage beats new passwords.
Rule: no Airflow install runs without SSO in front.

6. Audit Logs and the API

Security without audit is hope. Airflow's event logs record DAG changes, connection edits, and user actions in the metadata DB — but only if you read them.

Ship those logs somewhere searchable: SIEM, object storage, or a log aggregator. Correlate with the API: who triggered a DAG, who edited a connection, at what time, from which token.

The API is also a surface. Every script and CI pipeline using it needs scoped credentials, and the audit trail must cover it.

An incident without logs is a rumor. Make sure your next one has receipts.

📊 Production Insight
Unread audit logs are a false sense of safety.
API calls are the part attackers touch first.
Rule: stream event logs to a SIEM and review weekly.
🎯 Key Takeaway
Audit logs turn incidents into investigations.
The API is a surface, not an afterthought.
Rule: every privileged action ends up searchable.

7. A Security Checklist for Production

Run this before anything touches production.

  1. Fernet key: generated, rotated, never the default.
  2. Secrets: connections resolve from a backend; nothing hardcoded in DAGs.
  3. RBAC: Viewer default, access_control on every DAG, Admin for named few.
  4. Auth: webserver behind OAuth/LDAP; API behind its own auth backend.
  5. Audit: event logs shipped and reviewed.
  6. Least privilege on the service account the scheduler runs as.

Six items, one afternoon, and the leaked-password class of incident closes for good.

📊 Production Insight
Six checks close the credential-leak class of bugs.
Checklist security beats hero security every time.
Rule: run the checklist before every environment cutover.
🎯 Key Takeaway
Fernet, backend, RBAC, SSO, audit, service account.
One afternoon of work, permanent coverage.
Rule: production never starts until the checklist passes.
● Production incidentPOST-MORTEMseverity: high

The DAG That Leaked DB Credentials

Symptom
A commit to the shared repository contained a DAG file with a hardcoded database password; the same password was retrievable from the metadata DB because connection encryption used the default Fernet key.
Assumption
The team assumed passwords inside Airflow connections were safely encrypted, and that a private repo made credentials in code acceptable.
Root cause
Credentials sat in DAG code for convenience; the Fernet key was the default generated sample key, so encrypted connections were trivially decryptable by anyone with DB access; no RBAC scoping limited who could read connections.
Fix
Rotated the Fernet key and re-encrypted connections using the official flow — prepend the new key, run airflow rotate-fernet-key, drop the old key — moved credentials to a secrets backend (Vault), removed hardcoded values from DAG code, and applied least-privilege RBAC with DAG-level access_control.
Key lesson
  • Default Fernet keys mean your encrypted connections are decryptable by anyone who reads the docs.
  • Credentials in DAG code leak the day the repo's audience changes.
  • A secrets backend moves rotation out of code deploys entirely.
  • RBAC scoping is what limits blast radius when something does leak.
Production debug guideSymptom to Action5 entries
Symptom · 01
Connections fail to decrypt after key rotation
Fix
Re-encrypt every connection with the rotated key via the CLI or a re-encrypt script before restarting the scheduler.
Symptom · 02
Too many users can read connections
Fix
Audit roles: run a role-permission review and move users from Admin or User down to Op or Viewer.
Symptom · 03
Secrets backend unreachable at task runtime
Fix
Check backend credentials, network path, and the fallback behavior; decide deliberately whether Airflow fails open or closed.
Symptom · 04
Webserver or API reachable without authentication
Fix
Configure an auth backend (OAuth or LDAP) and restrict API auth; the default is wide open by design.
Symptom · 05
Suspicious connection or DAG access
Fix
Pull the audit logs and correlate API calls and UI actions with user identities before changing anything.
Secret Storage Options Compared
OptionWhere it livesEncrypted at restRotationBest for
Plaintext in DAG codeRepository and every imageNoCode deployNever — this is the anti-pattern
Environment variableContainer environmentDepends on hostRedeploy or restartDev and throwaway installs
Airflow connection (UI/CLI)Metadata DBYes — FernetRe-encrypt with new keySmall production setups
Secrets backend (Vault, AWS SM, GCP SM)External vault serviceYes — at the vaultVault-side, no deployProduction standard
Kubernetes SecretsCluster etcdOptional (encryption at rest)kubectl or HelmKubernetes deployments
Cloud IAM rolesCloud identity systemN/AIAM policy changeServerless, no-secret pattern
⚙ Quick Reference
4 commands from this guide
FileCommand / CodePurpose
rotate_fernet.shpython -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().d...2. Fernet
airflow.cfg[secrets]3. Secrets Backends vs Connection Encryption
orders_dag.pywith DAG(4. RBAC
auth.envexport AIRFLOW__WWW__AUTH_BACKENDS="airflow.providers.fab.auth_manager.oauth_pro...5. Authentication

Key takeaways

1
Fernet encrypts connection secrets at rest, but a default key means the encryption is decoration.
2
Secrets backends remove credentials from Airflow entirely and make rotation a five-minute vault operation.
3
Least privilege is the RBAC default
start from Viewer, grant up, and scope every DAG with access_control.
4
Authentication belongs behind company SSO
OAuth or LDAP — with the API behind its own auth backend.
5
Audit logs that nobody reads are a false sense of safety; ship them to a SIEM and review them.

Common mistakes to avoid

4 patterns
×

Keeping the default Fernet key

Symptom
Every 'encrypted' connection is decryptable with the published sample key
Fix
Generate a random key, store it in a file, env var, or KMS, and rotate it on a schedule.
×

Hardcoding credentials in DAG code

Symptom
The database password sits in the repo and every engineer can read it
Fix
Move credentials to connections backed by a secrets backend; never string-literal a password.
×

One broad role for everyone

Symptom
Any user can edit connections or trigger production DAGs
Fix
Least privilege: Viewer by default, scope DAG access with access_control, Admin for a named few.
×

Webserver and API exposed without real auth

Symptom
Public endpoints reachable with default or shared credentials
Fix
Put the webserver behind OAuth/LDAP, restrict API auth backends, and kill default accounts.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
What is the Fernet key and what happens if it's left at the default?
Q02SENIOR
Walk me through rotating a Fernet key in production.
Q03SENIOR
Design secrets management for an Airflow platform touching five database...
Q01 of 03JUNIOR

What is the Fernet key and what happens if it's left at the default?

ANSWER
Fernet is the symmetric encryption Airflow uses to encrypt connection passwords and variables at rest in the metadata DB. The default key is a published sample from the docs. Leaving it means anyone with DB access can decrypt every connection using the key from the documentation.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
Is the default Fernet key really dangerous?
02
Where should I store the Fernet key?
03
What's the difference between connection encryption and a secrets backend?
04
How do I rotate a Fernet key without breaking connections?
05
What does RBAC in Airflow actually enforce?
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.

Follow
Verified
production tested
August 01, 2026
last updated
1,750
articles · all by Naren
🔥

That's Airflow. Mark it forged?

3 min read · try the examples if you haven't

Previous
Airflow Performance Tuning
31 / 37 · Airflow
Next
Airflow Data Quality