Apache Airflow 接入 AWS Secrets Manager 完整指南:SecretsManagerBackend 配置、连接存储与多团队隔离
【免费下载链接】airflowApache Airflow - A platform to programmatically author, schedule, and monitor workflows项目地址: https://gitcode.com/GitHub_Trending/ai/airflow
Apache Airflow 内置的 Secrets Backend 抽象允许将连接(Connections)、变量(Variables)与配置项(Configurations)从数据库迁移到外部密钥管理系统。本文以 Apache Airflow 仓库中providers/amazon的 AWS Secrets Manager 集成文档(aws-secrets-manager.rst)为骨架,结合 SecretsManagerBackend 源码 与单元测试(test_secrets_manager.py),系统讲解如何启用该后端、以 URI 或 JSON 两种方式存储连接、按前缀与正则模式过滤查找范围,以及多团队模式下基于--分隔符的密钥隔离机制,读完即可在真实 Airflow 环境中落地配置。
一、SecretsManagerBackend 是什么
AWS Secrets Manager 是 AWS 提供的托管密钥服务,支持密钥轮换、细粒度 IAM 权限与审计。Airflow 通过BaseSecretsBackend抽象(定义于 airflow-core/src/airflow/secrets/base_secrets.py)统一了连接、变量与配置的获取接口,任何实现该基类的后端都可以挂载到[secrets]配置节。
SecretsManagerBackend正是这一抽象在 AWS Secrets Manager 上的实现,类定义于 providers/amazon/src/airflow/providers/amazon/aws/secrets/secrets_manager.py。它实现了三个核心查询方法,对应 Airflow 的三类敏感数据:
get_conn_value(conn_id):按连接 ID 读取连接,支持 URI 与 JSON 两种存储格式;get_variable(key):按变量名读取 Variable 值;get_config(key):按配置键读取 Airflow 配置项(例如将sql_alchemy_conn这类敏感配置托管到 Secrets Manager)。
从源码可以看到,这三个方法均先检查对应前缀是否为None,再检查密钥 ID 是否命中了保留的多团队命名空间,最后统一走_get_secret()完成实际的 Secret 读取,整体链路清晰且职责单一。
二、在 airflow.cfg 中启用后端
启用方式是在airflow.cfg的[secrets]节将backend指定为 SecretsManagerBackend 的完整类路径,并通过backend_kwargs传入参数。需要注意:backend_kwargs按 JSON 解析,因此 Python 侧的False或None等字面量会被忽略,这些参数将回退到后端的默认值。正确的写法是使用 JSON 的false、null。
基础示例配置如下:
[secrets] backend = airflow.providers.amazon.aws.secrets.secrets_manager.SecretsManagerBackend backend_kwargs = { "connections_prefix": "airflow/connections", "connections_lookup_pattern": null, "variables_prefix": "airflow/variables", "variables_lookup_pattern": null, "config_prefix": "airflow/config", "config_lookup_pattern": null, "profile_name": "default" }该配置启用连接、变量、配置三类密钥的查找,并使用本地默认 AWS 配置文件进行认证。
认证方式
SecretsManagerBackend本身不实现 AWS 认证,而是复用 Amazon Provider 的连接体系。从源码 client 属性 可以看到,它内部以SecretsManagerBackend__connection为连接 ID 构造AwsConnectionWrapper,再经SessionFactory创建 boto3 session 与secretsmanagerclient。因此可以通过两种途径认证:
- 传入 AWS Connection Extra 配置参数:
backend_kwargs中直接写入region_name、role_arn、use_ssl、verify、endpoint_url等 AWS 连接参数(详见仓库文档 configuring-the-connection); - 设置 AWS 标准环境变量:如
AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、AWS_DEFAULT_REGION等,由 boto3 的默认凭证链解析。
使用角色扮演(role assumption)的示例:
[secrets] backend = airflow.providers.amazon.aws.secrets.secrets_manager.SecretsManagerBackend backend_kwargs = { "connections_prefix": "airflow/connections", "variables_prefix": "airflow/variables", "config_prefix": "airflow/config", "role_arn": "arn:aws:iam::123456789098:role/role-name" }单元测试 test_passing_client_kwargs 验证了这些参数确实被透传给了SessionFactory与 boto3 client:role_arn与region_name会进入连接包装器,use_ssl=False会体现在最终 client 调用中。
backend_kwargs 参数总览
以下参数在__init__中定义(源码 L120-L158):
| 参数 | 默认值 | 说明 |
|---|---|---|
connections_prefix | "airflow/connections" | 连接密钥的前缀;设为null则完全不向 Secrets Manager 发起连接查询 |
connections_lookup_pattern | null | 连接 ID 需匹配的正则,仅当前缀非空时生效 |
variables_prefix | "airflow/variables" | 变量密钥的前缀;设为null则不查询变量 |
variables_lookup_pattern | null | 变量名需匹配的正则 |
config_prefix | "airflow/config" | 配置密钥的前缀;设为null则不查询配置 |
config_lookup_pattern | null | 配置键需匹配的正则 |
sep | "/" | 前缀与密钥 ID 之间的分隔符,默认/ |
extra_conn_words | null | 扩展连接字段别名,JSON 对象,键可选user、password、host、schema、conn_type |
**kwargs | — | 透传给 AWS 连接体系的认证参数,如profile_name、role_arn、region_name、use_ssl等 |
源码对前缀做了rstrip(sep)处理(L133-L144),因此前缀末尾写不写/都等价。
三、存储与读取连接(Connections)
连接在 Secrets Manager 中有两种存储方式:连接 URI与JSON 字段。
方式一:以连接 URI 存储
将连接编码为 Airflow 连接 URI(如postgresql://user:pass@host:5432/db)存入密钥即可:
注意:以 URI 存储时,每个字段默认按 URL 编码处理。例如要表示密码my password,URI 中必须写作my%20password。
对应的单测 test_get_conn_value_full_url_mode 展示了最小闭环:以airflow/connections/test_postgres为密钥名存入postgresql://airflow:airflow@host:5432/airflow后,get_conn_value("test_postgres")返回同一 URI。
方式二:以 JSON 字段存储
也可以把连接的各个组成部分作为 JSON 的键值对存储。为了兼容不同的命名习惯,后端允许为每个字段使用别名,_standardize_secret_keys(源码 L181-L202)会把别名统一映射为 Airflow 标准字段:
| 连接字段 | 允许的别名 |
|---|---|
| 连接类型 | conn_type、conn_id、connection_type、engine |
| 用户名 | login、user、username、user_name |
| 密码 | password、pass、key |
| 主机 | host、remote_host、server |
| 端口 | port |
| 扩展信息 | extra(必须是合法 JSON) |
默认词表之外,还可以通过extra_conn_words参数追加别名。该参数必须是字典,键从user、password、host、schema、conn_type中选取,值为字符串列表;为兼容历史配置,user会映射到login字段。
命名规则与 CLI 实操
连接 ID 为smtp_default、前缀为airflow/connections时,密钥应存储在airflow/connections/smtp_default(路径拼接逻辑见 build_path:f"{path_prefix}{sep}{secret_id}")。既可以在 AWS 控制台创建,也可以用 AWS CLI:
aws secretsmanager put-secret-value \ --secret-id airflow/connections/smtp_default \ --secret-string '{"login": "nice_user", "password": "this_is_the_password", "host": "ec2.8399.com", "port": "999"}'随后验证密钥已写入:
❯ aws secretsmanager get-secret-value --secret-id airflow/connections/smtp_default { "ARN": "arn:aws:secretsmanager:us-east-2:314524341751:secret:airflow/connections/smtp_default-7meuul", "Name": "airflow/connections/smtp_default", "VersionId": "34f90eff-ea21-455a-9c8f-5ee74b21be672", "SecretString": "{\n \"login\":\"nice_user\",\n \"password\":\"this_is_the_password\"\n, \n \"host\":\"ec2.8399.com\"\n,\n \"port\":\"999\"\n}\n", "VersionStages": [ "AWSCURRENT" ], "CreatedDate": "2020-04-08T02:10:35.132000+01:00" }如果不想使用任何连接前缀,将connections_prefix设为空字符串""即可——注意空字符串与null语义不同:空字符串表示“前缀为空但依然查询”,null表示“完全不查询”。
关于 JSON 存储还有一个实现细节:后端发现 Secret 内容以{开头时会反序列化 JSON、经_standardize_secret_keys统一字段名后再重新序列化(源码 L222-L236),这是为了兼容 Airflow 2.3 之前该后端对 JSON 别名的扩展支持。
四、存储与读取变量(Variables)
变量(Airflow Variable)的规则与连接完全对称:设variables_prefix为airflow/variables后,变量hello应存储在airflow/variables/hello。DAG 中Variable.get("hello")即可读到 Secrets Manager 中的值,对应单测 test_get_variable。
五、可选查找:关闭某类查询或按正则过滤
按类型关闭查询
连接、变量、配置三类密钥可以独立开启或关闭,从而避免为不需要的类型向 AWS 发送请求。将不需要类型的*_prefix设为null即可:
[secrets] backend = airflow.providers.amazon.aws.secrets.secrets_manager.SecretsManagerBackend backend_kwargs = { "connections_prefix": "airflow/connections", "variables_prefix": null, "config_prefix": null, "profile_name": "default" }上面的配置只查询连接,不再向 Secrets Manager 发送变量与配置请求。源码 L211-L213、L247-L248、L263-L264 三个方法在对应前缀为None时直接返回None;单测 test_connection_prefix_none_value 还验证了此时_get_secret完全不会被调用。
按正则过滤查询范围
只想让部分 ID 走 Secrets Manager 时,使用对应的*_lookup_pattern参数,值为正则字符串。例如只查询以m开头的连接:
[secrets] backend = airflow.providers.amazon.aws.secrets.secrets_manager.SecretsManagerBackend backend_kwargs = { "connections_prefix": "airflow/connections", "connections_lookup_pattern": "^m", "profile_name": "default" }_get_secret中的匹配实现(源码 L368-L374)使用re.match(lookup_pattern, secret_id, re.IGNORECASE)——注意是re.match而非re.search,因此模式需从头匹配,且不区分大小写。参数化单测 test_connection_lookup_pattern 给出了一组可复现的行为矩阵:模式为test、.*、T.*、null时各发起 1 次 client 调用;模式为dummy-pattern时调用次数为 0,即被过滤掉。
六、多团队支持(Multi-Team Support)
当 Airflow 以多团队模式运行时,团队作用域的密钥使用--作为团队名与密钥 ID 之间的分隔符(常量TEAM_SEP定义于 secrets_manager.py L33):
- 团队
marketing拥有的连接smtp_default→ 存储于airflow/connections/marketing--smtp_default; - 同一团队拥有的变量
hello→ 存储于airflow/variables/marketing--hello。
任务作者在 DAG 中依然按普通 ID 请求连接与变量,无需感知团队前缀。查找顺序为:先尝试团队作用域路径(<prefix>/<team>--<id>),若未命中再回退到全局路径(如airflow/connections/smtp_default),该回退逻辑见 源码 L375-L388。
保留命名空间与防串团队设计
两点安全约束需要特别留意:
- 含
--的 ID 是保留命名空间:多团队模式下,任何无团队上下文、且 ID 本身包含--的请求会直接返回None,以防止跨团队读取。原因在_names_a_team_namespace的注释中解释得很清楚:<team>--<id>的拼接存在歧义(团队a请求b--c与团队a--b请求c会拼出完全相同的字符串),而 ID 中没有任何信息可以区分这两种解读,因此所有 getter——连接、变量、配置——都会拒绝这类请求并记录 WARNING 日志(_log_refusal)。相关测试包括 test_global_caller_cannot_access_team_scoped_connection、test_another_teams_secret_is_not_reachable 等。 - 该检查仅在多团队模式生效:
_names_a_team_namespace首先检查配置项core.multi_team是否为真,未开启时普通含--的 ID 照常解析,见 test_ambiguous_id_resolves_when_multi_team_is_disabled。
多团队回退到全局路径的行为由 test_team_caller_falls_back_to_global_connection 覆盖:即使指定了不存在的团队名,只要全局路径存在密钥,依然能取回连接。
七、实战示例:把 Google Cloud 连接存入 AWS Secrets Manager
以 Google Cloud 连接为例,说明如何把第三方云连接完整托管到 AWS Secrets Manager。由于 Google 连接的所有认证信息都必须放在extra字段中,因此 JSON 存储时须把全部字段包裹进extra:
使用服务账号密钥文件:
{"extra": {"key_path": "/opt/airflow/service_account.json", "scope": "https://www.googleapis.com/auth/devstorage.read_only"}}使用服务账号密钥字典(直接内嵌 JSON 内容):
{"extra": {"keyfile_dict": "<copy & paste the service account json here>", "scope": "https://www.googleapis.com/auth/devstorage.read_only"}}两种方式都可以直接在 AWS 控制台以 Key/value 形式编辑extra下的各个子字段,无需手写整段 JSON。
八、FAQ 与排错要点
- 为什么我传了 Python 的
False/None却没生效?因为backend_kwargs是 JSON 解析的,请改用 JSON 字面量false/null。 - 前缀末尾带不带
/?都可以,源码会统一rstrip("/")。 - 连接查不到,如何定位?后端对
ResourceNotFoundException、InvalidParameterException、InvalidRequestException、DecryptionFailure、InternalServiceError五类异常均以 DEBUG 日志记录后返回None(源码 L272-L320),可以在 DEBUG 日志级别观察“Secret xxx not found”类信息;多团队模式下的拒绝会输出 WARNING 级日志。 - 本地调试需要真实 AWS 吗?不需要。仓库测试使用
moto的mock_aws装饰器模拟 Secrets Manager 服务,例如 test_get_conn_value_full_url_mode,可作为本地复现行为的参考。
延伸阅读
- 后端实现:providers/amazon/src/airflow/providers/amazon/aws/secrets/secrets_manager.py
- 单元测试:providers/amazon/tests/unit/amazon/aws/secrets/test_secrets_manager.py
- 姊妹后端(AWS Systems Manager Parameter Store):providers/amazon/docs/secrets-backends/aws-ssm-parameter-store.rst
- Secrets 后端索引:providers/amazon/docs/secrets-backends/index.rst
- 基类与查找路径:airflow-core/src/airflow/secrets/base_secrets.py
- 路径拼接工具:airflow-core/src/airflow/_shared/secrets_backend/base.py
【免费下载链接】airflowApache Airflow - A platform to programmatically author, schedule, and monitor workflows项目地址: https://gitcode.com/GitHub_Trending/ai/airflow
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考