JWT 令牌身份验证

本文档描述了 Apache Airflow 如何为公共 REST API(核心 API)以及供 Worker 使用的内部执行 API(Execution API)进行 JWT(JSON Web Token)身份验证。

概述

Airflow 使用 JWT 令牌作为其 API 的主要身份验证机制。存在两种不同的 JWT 身份验证流程:

  1. REST API(核心 API) — 由 UI 用户、CLI 工具和外部客户端使用,以与 Airflow 公共 API 交互。

  2. 执行 API (Execution API) — 由 Worker、DAG 文件处理器和触发器 (Triggerer) 内部使用,用于通信任务状态并检索运行时数据(连接、变量、XCom)。

两种流程共享相同的基础 JWT 基础设施(位于 airflow.api_fastapi.auth.tokens 中的 JWTGeneratorJWTValidator 类),但在受众、令牌生命周期、主体声明和作用域语义上有所不同。

签名与加密

Airflow 支持两种互斥的签名模式:

对称加密(共享密钥)

使用预共享的密钥 ([api_auth] jwt_secret) 和 HS512 算法。所有生成或验证令牌的组件必须共享同一个密钥。如果未配置密钥,Airflow 将在启动时自动生成一个随机的 16 字节密钥,但该密钥是临时的,且在不同进程中不同,这将导致多组件部署中的身份验证失败。部署管理员必须显式配置此值。

非对称加密(公钥/私钥对)

使用 PEM 编码的私钥 ([api_auth] jwt_private_key_path) 进行签名,并使用相应的公钥进行验证。支持的算法:RS256 (RSA) 和 EdDSA (Ed25519)。当 [api_auth] jwt_algorithm 设置为 GUESS(默认值)时,算法会根据密钥类型自动检测。

验证可以使用以下任一方式:

  • 通过 [api_auth] trusted_jwks_url 配置的 JWKS (JSON Web Key Set) 端点(本地文件或远程 HTTP/HTTPS URL,会定期轮询更新)。

  • 从配置的私钥中派生出的公钥(当未设置 trusted_jwks_url 时的自动回退方案)。

REST API 身份验证流程

令牌获取

  1. 客户端发送 POST 请求至 /auth/token,并在 JSON 请求体中携带凭据(例如用户名和密码)。

  2. 身份验证管理器验证凭据并创建一个用户对象。

  3. 身份验证管理器将用户序列化为 JWT 声明,并调用 JWTGenerator.generate()

  4. 生成的令牌以 access_token 的形式返回在响应中。

对于基于 UI 的身份验证,令牌存储在安全且仅限 HTTP 访问的 cookie (_token) 中,并设置 SameSite=Lax

CLI 使用单独的端点 (/auth/token/cli),其过期时间较短。

令牌结构 (REST API)

声明

描述

jti

唯一令牌标识符(UUID4 十六进制)。用于令牌撤销。

iss

颁发者(来自 [api_auth] jwt_issuer)。

aud

受众(来自 [api_auth] jwt_audience)。

sub

用户标识符(由身份验证管理器序列化)。

iat

颁发时间戳(Unix 时间戳,秒)。

nbf

生效前时间戳(与 iat 相同)。

exp

过期时间戳(iat + jwt_expiration_time)。

令牌验证 (REST API)

在每次 API 请求中,令牌会按以下优先级顺序提取:

  1. Authorization: Bearer <token> 请求头。

  2. OAuth2 查询参数。

  3. _token cookie。

JWTValidator 会验证签名、有效期 (exp)、生效前时间 (nbf)、颁发时间 (iat)、受众和颁发者声明。可配置的余量([api_auth] jwt_leeway,默认 10 秒)用于抵消时钟偏移。

令牌撤销 (仅限 REST API)

令牌撤销仅适用于 REST API 和 UI 令牌 — 它适用于颁发给 Worker 的执行 API 令牌。

已撤销的令牌通过其 jti 声明在 revoked_token 数据库表中进行跟踪。在登出或显式撤销时,令牌的 jtiexp 会被插入到此表中。过期的条目会自动以 jwt_expiration_time 的频率进行清理。

令牌刷新 (REST API)

JWTRefreshMiddleware 在 UI 请求上运行。当中间件检测到当前令牌的 _token cookie 即将过期时,它会调用 auth_manager.refresh_user() 生成新令牌,并将其设置为更新后的 cookie。

默认计时设置 (REST API)

设置

默认值

[api_auth] jwt_expiration_time

86400 秒(24 小时)

[api_auth] jwt_cli_expiration_time

3600 秒(1 小时)

[api_auth] jwt_leeway

10 秒

执行 API 身份验证流程

执行 API 是 Airflow 自身使用的一种 API(非第三方调用者),用于报告和设置任务状态转换、发送心跳,并在任务运行时检索连接、变量和 XCom,以及触发执行和 DAG 解析。

令牌生成 (执行 API)

  1. 调度器 (Scheduler) 在将任务实例分发给 Worker 之前(通过执行器)会为其生成 JWT。执行器的 jwt_generator 属性会创建一个配置了 [execution_api] 设置的 JWTGenerator

  2. 令牌的 sub (主体) 声明被设置为任务实例 UUID

  3. 令牌被嵌入在发送给 Worker 进程的工作负载 JSON 有效负载(BaseWorkloadSchema.token 字段)中。

令牌结构 (执行 API)

声明

描述

jti

唯一令牌标识符(UUID4 十六进制)。

iss

颁发者(来自 [api_auth] jwt_issuer)。

aud

受众(来自 [execution_api] jwt_audience,默认:urn:airflow.apache.org:task)。

sub

任务实例 UUID — 工作负载的身份。

scope

令牌作用域:"execution""workload"

iat

颁发时间戳。

nbf

生效前时间戳。

exp

过期时间戳(iat + [execution_api] jwt_expiration_time)。

令牌作用域 (执行 API)

执行 API 定义了两种令牌作用域:

workload

一种受限作用域,仅在通过 Security(require_auth, scopes=["token:workload"]) 显式启用的端点上被接受。用于管理任务状态转换的端点。

execution

所有执行 API 端点均接受此作用域。这是用于 Worker 通信的标准作用域,允许访问所有功能。

为了向后兼容,缺少 scope 声明的令牌默认为 "execution"

令牌分发至 Worker

令牌在执行堆栈中的流动流程如下:

  1. 调度器生成令牌并将其嵌入到传递给执行器的工作负载 JSON 有效负载中。

  2. 工作负载 JSON 被传递给 Worker 进程(通过执行器特定的机制:Celery 消息、Kubernetes Pod 规范、本地子进程参数等)。

  3. Worker 的 execute_workload() 函数读取工作负载 JSON 并提取令牌。

  4. supervise() 函数接收该令牌,并创建一个带有 BearerAuth(token)httpx.Client 实例,用于所有执行 API HTTP 请求。

  5. 令牌包含在每个请求的 Authorization: Bearer <token> 请求头中。

令牌验证 (执行 API)

JWTBearer 安全依赖项会对每个请求进行一次令牌验证:

  1. Authorization: Bearer 请求头中提取令牌。

  2. 通过 JWTValidator 执行加密签名验证。

  3. 验证标准声明(expiataud — 如果已配置则验证 nbfiss)。

  4. 如果缺少 scope 声明,则默认为 "execution"

  5. 创建带有任务实例 ID 和声明的 TIToken 对象。

  6. 将验证后的令牌缓存在 ASGI 请求范围内,以供整个请求生命周期使用。

路由级别的强制执行由 require_auth 处理:

  • 对照路由的 allowed_token_types 检查令牌的 scope(在路由注册时由 ExecutionAPIRoute 根据 token:* 安全作用域预先计算)。

  • 强制执行 ti:self 作用域 — 验证令牌的 sub 声明是否与 {task_instance_id} 路径参数匹配,从而防止 Worker 访问其他任务的端点。

令牌刷新 (执行 API)

JWTReissueMiddleware 会自动刷新接近过期的有效令牌:

  1. 在每次响应后,中间件会检查令牌的剩余有效期。

  2. 如果剩余总有效期的 20% 以下(至少 30 秒),服务器将生成一个新令牌,保留所有原始声明(包括 scopesub)。

  3. 刷新后的令牌会返回在 Refreshed-API-Token 响应头中。

  4. 客户端的 _update_auth() 钩子会检测到此请求头,并透明地为后续请求更新 BearerAuth 实例。

此机制确保长时间运行的任务不会因令牌过期而丢失 API 访问权限,无需 Worker 重新进行身份验证。

无令牌撤销 (执行 API)

执行 API 令牌不受撤销机制影响。它们是短生命周期的(默认 10 分钟),并由 JWTReissueMiddleware 自动刷新,因此撤销不属于执行 API 安全模型的一部分。一旦执行 API 令牌颁发给 Worker,它在过期前将一直有效。

默认计时设置 (执行 API)

设置

默认值

[execution_api] jwt_expiration_time

600 秒(10 分钟)

[execution_api] jwt_audience

urn:airflow.apache.org:task

令牌刷新阈值

剩余有效期 20%(至少 30 秒,即在默认 600 秒令牌生命周期下,过期前约 120 秒)

DAG 文件处理器与触发器

DAG 文件处理器触发器是内部 Airflow 组件,它们也与执行 API 交互,但使用的是进程内 (in-process) 传输 (InProcessExecutionAPI),而不是网络传输。这种进程内 API:

  • 使用 ASGI/WSGI 网桥直接在同一进程内运行执行 API 应用程序。

  • 潜在地绕过了 JWT 身份验证 — JWT 承载依赖项被重写,以始终返回具有 "execution" 作用域的合成 TIToken,从而实际上绕过了令牌验证。

  • 同样潜在地绕过了针对特定资源的访问控制(对连接、变量和 XCom 的访问检查被重写为始终允许)。

Airflow 实现了软件防护机制,防止在这些组件中从 DAG 作者代码中进行意外的数据库直接访问。然而,由于解析 DAG 文件和执行触发器代码的子进程以与父进程相同的 Unix 用户身份运行,这些防护无法防范蓄意攻击。恶意 DAG 作者可能会检索父进程的数据库凭据(通过 /proc/<PID>/environ、配置文件或密钥管理器访问),并获得对元数据数据库和所有执行 API 操作的完全读写权限 — 无需有效的 JWT 令牌。

这与 Worker/任务执行不同,后者在部署级别实现隔离 — 敏感的数据库凭据配置在 Worker 部署配置中根本不存在,且它们仅通过执行 API 进行通信。

在默认部署中,单个 DAG 文件处理器实例为所有团队解析 DAG 文件,单个触发器实例处理所有团队的所有触发器。这意味着来自不同团队的 DAG 作者代码在同一进程中执行,并潜在地共享对进程内执行 API 和元数据数据库的访问权限。

对于需要隔离的多团队部署,部署管理员必须采取部署级别的措施,为每个团队运行单独的 DAG 文件处理器和触发器实例 — Airflow 不提供内置的每团队 DFP 或触发器实例支持。即使有单独的实例,它们也保持与父进程相同的 Unix 用户身份。为防止凭据检索,部署管理员必须实现 Unix 用户级别的隔离(以不同的低特权用户运行子进程)或网络级别的限制。

请参阅 Airflow 安全模型 了解完整的安全影响、部署加固指南以及计划的战略和战术改进。

工作负载隔离与当前限制

有关工作负载隔离保护、当前限制和计划改进的详细讨论,请参阅 工作负载隔离与当前限制

配置参考

所有 JWT 相关配置参数:

参数

默认值

描述

[api_auth] jwt_secret

如果缺失则自动生成

用于对令牌进行签名的对称密钥。必须在所有组件中保持一致。与 jwt_private_key_path 互斥。

[api_auth] jwt_private_key_path

PEM 编码私钥(RSAEd25519)的路径。与 jwt_secret 互斥。

[api_auth] jwt_algorithm

GUESS

签名算法。根据密钥类型自动检测:对称密钥为 HS512RSARS256Ed25519EdDSA

[api_auth] jwt_kid

自动 (RFC 7638 指纹)

放置在令牌头中的密钥 ID。对于对称密钥将被忽略。

[api_auth] jwt_issuer

颁发者声明 (iss)。建议每个部署唯一。

[api_auth] jwt_audience

REST API 令牌的受众声明 (aud)。

[api_auth] jwt_expiration_time

86400 (24h)

REST API 令牌生命周期(秒)。

[api_auth] jwt_cli_expiration_time

3600 (1h)

CLI 令牌生命周期(秒)。

[api_auth] jwt_leeway

10

用于令牌验证的时钟偏移容差(秒)。

[api_auth] trusted_jwks_url

用于令牌验证的 JWKS 端点 URL 或本地文件路径。与 jwt_secret 互斥。

[execution_api] jwt_expiration_time

600 (10 min)

执行 API 令牌生命周期(秒)。

[execution_api] jwt_audience

urn:airflow.apache.org:task

执行 API 令牌的受众声明。

重要提示

所有 Airflow 组件的时间同步至关重要。请使用 NTP(例如 ntpdchrony)保持时钟同步。超过配置的 jwt_leeway 的时钟偏移将导致身份验证失败。

此条目是否有帮助?