Apache Airflow holds the credentials for almost every system a data platform touches, and anyone who can write a DAG can run code that reads them. Connections store database passwords, cloud keys and API tokens; variables hold more; and the DAG code that uses them is not sandboxed. This guide is for data platform engineers, deployment managers and security teams running Airflow 3 (with notes where Airflow 2 differs). It covers Airflow's security model and user types, admin roles, connections and secrets backends, API and execution tokens, audit logs and detection, a hardening baseline and common misconfigurations. Pipeline-wide controls are in the ETL and ELT Pipeline Security Guide; this page is about Airflow itself.
Key takeaways
- DAG authors are trusted code runners. Airflow's security model states that DAG code is neither verified nor sandboxed, so DAG authors can run arbitrary code on workers, the DAG File Processor and the Triggerer, and can reach the credentials that code uses.
- Connections are the crown jewels. Connection passwords and sensitive variables are encrypted with a Fernet key in the metadata database; the key, the database and any secrets backend must be protected as one unit.
- Use a secrets backend. An external backend is searched before environment variables and the metastore, moving credentials into a vault with its own access control, rotation and audit trail.
- Admin and Op are near-equivalent. Op users have the same access as Admin except managing permissions and reading audit logs; both can configure connections that can lead to code execution on workers.
- Isolation is per executor, not per team. Airflow does not yet provide task-level isolation between groups of DAG authors; the multi-team feature is experimental, so separate trust levels into separate deployments.
How identity and access work in Apache Airflow
Airflow's own security model defines three user types, and the distinction matters more than any setting:
| User type | What they can do | Trust required |
|---|---|---|
| Deployment Managers | Install and configure Airflow, choose executors and secrets backends, hold all credentials, can delete the installation | Highest; equivalent to platform root |
| DAG authors | Create, change and delete DAG files in a DAG bundle; their code runs on workers, the DAG File Processor and the Triggerer | High; they can read any credential their code can reach and modify any DAG's state |
| Authenticated UI users | Use the UI and REST API with permissions from their roles, scoped from a single DAG up to Admin | Depends on role |
Airflow does not support unauthenticated users by default. The exceptions are the /api/v2/monitor/health endpoint and /login. Authentication and roles are handled by an auth manager; the Flask AppBuilder (FAB) auth manager ships five default roles, each including the permissions of the one before: Public, Viewer, User, Op and Admin. Airflow recommends leaving default roles unaltered and creating custom roles instead, including DAG-level permissions such as DAG:example_dag_id.can_read.
Where isolation comes from
In Airflow 3, task code on workers talks to the API server only through the Execution API and does not receive metadata database credentials. The DAG File Processor and Triggerer still run as the same Unix user as their parent processes, which hold database credentials, so a deliberately malicious DAG author can potentially retrieve them. The executor decides the rest:
| Executor | Where DAG code runs | Isolation between tasks |
|---|---|---|
| Local executor | On the scheduler machine | None; DAG authors can affect the scheduler and the whole installation |
| Celery executor | On Celery workers | None on a shared worker unless tasks are separated by queues using cluster policies |
| Kubernetes executor | One pod per task | Pod-level isolation, the strongest of the three |
| Triggerer | Shared process for deferrable tasks | None; one instance handles triggers from all teams by default |
Admin roles and privileged access
- Admin: manages users and permissions, sees and changes sensitive credentials, and by default is the only role that can read audit logs. Airflow notes Admins can potentially execute code on workers through connection configuration.
- Op: the same access as Admin except managing permissions and audit logs. Treat it as privileged.
- Connection configuration users: in Airflow 3 they have write-only access to sensitive connection fields and cannot view them; in Airflow 2 they could reveal them. Airflow still warns that wrongly configured connections can lead to remote code execution with some providers.
- User and Viewer: User can view and edit DAGs, task instances and DAG runs and read task logs; Viewer is read-only and cannot see audit logs. Task logs can still contain data, so review who has them.
- Audit log access:
Audit Logs.can_readshows every entry regardless of DAG-level rights; grant it only to admins and the SOC. - Deployment access: whoever can write to the DAG bundle (Git repository, shared volume or object store) is a DAG author. Protect that repository with branch protection and code review; it is the real admin boundary.
Use SSO through your IdP for UI users, keep Admin to two or three named people, and give automation its own accounts. The Privileged Access Management Guide covers break-glass handling.
Integrations, API tokens and service accounts
Connections and variables
A connection stores host, login, password and an extra JSON field for one external system. Connections can come from the metadata database, from environment variables named AIRFLOW_CONN_{CONN_ID} (JSON or URI format), or from a secrets backend. Variables work the same way for arbitrary values.
- Fernet encryption: connection passwords and variables in the metastore are encrypted with the
[core] fernet_key(orAIRFLOW__CORE__FERNET_KEY). Anyone with the key and a database dump can decrypt every stored credential. - Fernet rotation: set
fernet_keytonew_key,old_key, runairflow rotate-fernet-keyto re-encrypt, then set it tonew_keyonly. Changing the key without this sequence breaks decryption of existing credentials. - Masking: Airflow masks connection passwords, sensitive variables and
extrakeys whose names contain keywords such aspassword,secret,token,api_key,private_key,authorization,keyfile_dictandservice_account. Extend the list with[core] sensitive_var_conn_names. Masking is value-based and triggered by connection or variable access, so a secret passed through XCom or another side channel is not masked downstream. - Test connection: disabled by default across the UI, API and CLI for security reasons (
AIRFLOW__CORE__TEST_CONNECTION). Keep it disabled in production.
Secrets backends
By default Airflow looks up connections and variables in environment variables, then the metastore. With an alternative secrets backend configured ([secrets] backend and backend_kwargs), the backend is searched first, then environment variables, then the metastore, and this order cannot be changed. A workers secrets backend, if defined, takes priority on workers. Watch for key collisions: reads return the first match from the backend, but writes from the UI go to the metastore, so a UI edit can silently fail to change the value tasks actually use.
| Storage option | Strengths | Weaknesses | Use when |
|---|---|---|---|
| Metastore (Fernet) | Simple, UI editable | Key and data often sit together; rotation is manual; every Admin can change values | Development and small single-team deployments |
| Environment variables | No database copy | Visible to anything that can read the process environment; hard to rotate | Bootstrap values only |
| Secrets backend (Vault, cloud secrets managers) | Central access control, audit, rotation, short-lived credentials | More setup; Airflow's own credential to the backend becomes a key secret | Any production deployment |
API and execution tokens
Airflow 3 issues JWTs for the REST API and the Execution API. Signing is either symmetric with a shared [api_auth] jwt_secret (HS512) or asymmetric with a private key at [api_auth] jwt_private_key_path. Defaults are jwt_expiration_time 86,400 seconds (24 hours) for the REST API, jwt_cli_expiration_time 3,600 seconds and jwt_leeway 10 seconds. Prefer asymmetric signing, so only the issuing components hold the private key, and shorten the REST API lifetime where your UI and integrations allow. Note that the execution token issued to a running task carries no per-DAG authorisation: a task with a valid token can call Execution API endpoints that read or write variables, connections and XComs for any DAG.
Service accounts used by CI/CD to deploy DAGs or call the REST API should be dedicated, scoped by custom role, and stored in a secrets manager. See the Secrets Management Guide and the Dynamic Secrets and Ephemeral Credentials Guide for replacing static connection passwords with short-lived credentials.
Logging and detection
Airflow keeps an audit log of user and system events, viewable under Browse > Audit Logs and through the /eventLogs REST endpoint. Combine it with component logs, DAG repository history and secrets backend audit logs.
| Signal | Source | Why it matters |
|---|---|---|
| Connection or variable created, edited or deleted | Audit log, secrets backend audit | Credential changes and redirection of data to attacker hosts |
| Role or user permission changes | Audit log | Privilege escalation to Admin or Op |
| New or changed DAG files, especially outside reviewed merges | Git history, DAG bundle storage logs | Code execution with every credential the workers can reach |
| Manual triggers and clears of production DAGs | Audit log | Running a pipeline out of schedule to exfiltrate or alter data |
| Secret reads by unusual DAGs or at unusual volume | Secrets backend audit log | A task enumerating connections it does not normally use |
| Outbound connections from workers to new destinations | Network or egress proxy logs | Exfiltration from task code |
Metrics
- Share of production connections served from a secrets backend rather than the metastore (target 100%).
- Days since last Fernet key rotation, and since last rotation of each high-value connection.
- Number of Admin and Op users, reviewed monthly.
- Share of DAG changes merged through reviewed pull requests.
- Audit log events reaching the SIEM within 15 minutes.
Hardening baseline
- Separate trust levels: run separate Airflow deployments for teams that should not see each other's credentials; do not rely on the experimental
[core] multi_teamfeature for isolation. - Executor choice: prefer the Kubernetes executor for production so each task runs in its own pod with its own service account; avoid the Local executor beyond development.
- Secrets backend: move production connections and variables to a secrets backend, give each deployment its own narrowly scoped credential to it, and remove copies from the metastore.
- Fernet key: generate a unique key per environment, store it outside the database host, and rotate it with the documented sequence.
- Authentication: SSO for UI users, no public role permissions, custom least-privilege roles, two or three Admins.
- Tokens: asymmetric JWT signing, shorter REST API token lifetimes, dedicated API service accounts.
- DAG supply chain: deploy DAGs only from a protected repository with code review and code owners, pin provider and library versions, and scan DAG code for secrets.
- Workload controls: if you use
run_as_userimpersonation, note that Airflow requires sudo and theairflowuser must be treated like root; setdefault_impersonationso tasks do not run with sudo by default. - Network: keep the API server behind SSO or a VPN, restrict metadata database access to Airflow components, and route worker egress through a proxy with an allow list.
- Test and roll back: trial each change in a staging deployment with representative DAGs, keep the previous configuration and Fernet key list until all credentials decrypt, and switch production connections to the backend one group at a time.
Framework mapping
| Framework | Reference | Airflow control |
|---|---|---|
| NIST CSF 2.0 | PR.AA-05, PR.DS-01, PR.PS-01, PR.PS-04, DE.CM-03 | Least privilege roles, encrypted credentials, configuration management, audit logging, activity monitoring |
| NIST SP 800-53 Rev. 5 | AC-3, AC-6, IA-5, SC-12, SC-28, CM-5, CM-7, SA-11, AU-2, AU-6 | Access enforcement, least privilege, authenticator and key management, change control for DAGs, least functionality, developer testing, logging and review |
| MITRE ATT&CK v20 | T1059.006, T1552, T1552.001, T1078, T1098, T1195.002 | Python execution through DAGs, credential access from connections and files, valid accounts, role changes, poisoned DAG supply chain |
Common misconfigurations
- Treating DAG authors as ordinary users: giving many contributors write access to the DAG repository is the same as giving them every credential the workers can reach.
- Fernet key next to the database: the key in
airflow.cfgon the same host or image as the database backup means one breach yields every password in clear. - Secrets in variables without sensitive names: a variable called
snowflake_connholding a password is not masked, because masking relies on keywords in the name. - Passing secrets through XCom: values passed between tasks this way are not masked and are stored in the metadata database.
- One Celery worker pool for all teams: tasks from different teams share hosts and can read each other's environment and files.
- API server on the internet with local accounts: no SSO, no MFA and long-lived JWTs make a phished password enough to change connections.
Practitioner checklist
- Map who can write DAGs and treat that group as privileged.
- Use the Kubernetes executor or separate worker queues for different trust levels.
- Move production connections and variables to a secrets backend.
- Use a unique Fernet key per environment and rotate it with
airflow rotate-fernet-key. - Extend
sensitive_var_conn_namesto cover your naming conventions. - Keep test connection disabled and restrict connection editing to a small role.
- Limit Admin and Op to named people with SSO and MFA.
- Use asymmetric JWT signing and shorten token lifetimes.
- Stream audit logs and secrets backend logs to the SIEM and alert on connection and role changes.
- Protect the DAG repository with review, code owners and secret scanning.
Standards and references
- Apache Airflow: Airflow Security Model
- Apache Airflow: Fernet
- Apache Airflow: Masking sensitive data
- Apache Airflow: Secrets Backend
- Apache Airflow: Audit Logs
- Apache Airflow: Access Control (FAB auth manager)
- NIST: SP 800-53 Rev. 5
- MITRE ATT&CK: Unsecured Credentials
Related NHI Mgmt Group resources: ETL and ELT Pipeline Security Guide · Secrets Management Guide · Cloud Secrets Managers Guide · Kubernetes Secrets Security Guide