---
name: audit-airflow-config
description: >
  Audit a self-hosted Airflow deployment for security issues. Use when the user says
  "check my Airflow fernet key", "audit Airflow default credentials", "verify webserver auth",
  "check if Airflow secrets are hardcoded", "review Airflow security", "audit Airflow config",
  or asks to review a client Airflow repo before a security report.
argument-hint: "[path-to-airflow-repo-root]"
---

# Audit Airflow Config

Audit a self-hosted Airflow deployment for Airflow-specific security issues. Produce a
prioritized list of findings with severity, location, and remediation snippets.

## Arguments

`$ARGUMENTS` — optional path to the Airflow repo root. Defaults to the current working directory.

```
/audit-airflow-config /path/to/client-airflow-repo
/audit-airflow-config                                 # uses cwd
```

## Step 1 — Determine the repo root

Use `$ARGUMENTS` as the repo root if provided; otherwise use the current working directory.
Confirm the path exists before continuing.

## Step 2 — Auto-detect deployment type and Airflow files

Gemma deploys Airflow in two patterns. Detect which applies:

### Pattern A: Direct Docker Compose (legacy/simple)
Files present in the repo root:
| File pattern | What it tells us |
|---|---|
| `docker-compose.yml` / `docker-compose.yaml` | Main config source for env vars, ports, volumes |
| `Dockerfile` / `Dockerfile.*` | Check for secrets baked into image layers |
| `airflow.cfg` | Static Airflow config overrides |
| `.env` / `.env.*` (if not git-ignored) | Inline secrets — critical if committed |
| `.github/workflows/*.yml` | May contain secrets passed as build args |

### Pattern B: OpenTofu + Ansible (modern/saxonia pattern)
Files present in an `ansible/` or `server/ansible/` subdirectory:
| File pattern | What it tells us |
|---|---|
| `roles/airflow/tasks/main.yml` | Deployment steps — check for `op read`, `no_log: true`, secret handling |
| `roles/airflow/templates/.env.j2` | Rendered env file template — check for hardcoded values vs `{{ var }}` references |
| `roles/airflow/templates/docker-compose.yml.j2` | Compose template — check port bindings, secrets usage |
| `roles/airflow/defaults/main.yml` | Role defaults — flag any hardcoded secret values |

**When Pattern B is detected**: the security model is different. Secrets should come from `op read` (1Password), `ansible-vault`, or similar at deploy time — NOT from hardcoded values in defaults or templates. A value like `{{ airflow_fernet_key.stdout }}` in a template is good (resolved at deploy time). A literal key value in `defaults/main.yml` is Critical.

If neither pattern is found, report that no Airflow-related files were detected and stop.

## Step 3 — Read the checklist

Read `${CLAUDE_SKILL_DIR}/references/checklist.md` in full before scanning. It contains checks
covering fernet key handling, JWT secret (Airflow 3), auth manager, expose_config, default
credentials, DockerOperator socket, port binding, API auth, secrets backend, remote logging,
EOL status, and Ansible/IaC deployment secrets. Apply all relevant checks based on the detected
deployment pattern from Step 2.

## Step 4 — Apply each check

Apply each check from the checklist to all detected files. For each check, record:
- Finding status: pass / fail / not-applicable
- File and approximate location where the issue appears
- Severity from the checklist

### Procedural notes not covered by the checklist

**`:-airflow` fallback rule (Critical):** For the pattern
`${_AIRFLOW_WWW_USER_PASSWORD:-airflow}` in docker-compose files, the `:-airflow` fallback
is itself a Critical finding — if the environment variable is unset, Docker silently uses
the weak default password with no warning. Remove the fallback entirely; a missing variable
should cause the deployment to fail loudly.

**Dual-stage build cross-reference:** For dual-stage build secret leakage patterns
(`dev_build` → `prod_build`), see `audit-dockerfile` Check 1 and Check 6. If a `Dockerfile`
with multiple build stages is present, apply those checks in addition to the Airflow-specific
checks here.

**Never display actual secret values.** Only report: where each key is set, how it is set
(hardcoded / ARG→ENV / env_file reference / absent), and whether that method is secure.

## Step 5 — Format findings

Group findings by severity: Critical → Important → Recommended.

For each finding:

```
### [AIRFLOW-N] <short title>
- **Severity**: Critical / Important / Recommended
- **Location**: `<filename>` (line ~N or "environment block" if not line-specific)
- **Issue**: <one-sentence description>
- **Risk**: <what an attacker could do if exploited>
- **Remediation**:
  <before/after snippet from the checklist, adapted to the specific file>
```

For each check that passed, add a one-liner:
`- [Pass] <check name> — <brief note on how it is configured correctly>`

## Step 6 — Produce the summary

```
## Summary

| Severity | Count |
|---|---|
| Critical | N |
| Important | N |
| Recommended | N |

## Checks Passed
- [list of passed checks]

## Files Scanned
- [list of files that were read]
```

## Important rules

- Never display actual secret values — only patterns, locations, and whether a value is
  hardcoded vs referenced from an env file.
- If a file cannot be read, note it as "not found — skipped" and continue.
- If the deployment appears to be a development or demo environment (repo name contains
  `demo`, `test`, or `dev`, or the compose file has a comment indicating non-production),
  note this in the summary — some findings (e.g. default credentials) are acceptable in
  dev but not production.
- Do not auto-fix anything. This is a read-only audit.
