Apache Airflow CLI 敏感值保护:connections list与variables list默认隐藏密码与 URI 凭据
【免费下载链接】airflowApache Airflow - A platform to programmatically author, schedule, and monitor workflows项目地址: https://gitcode.com/GitHub_Trending/ai/airflow
本文介绍 Apache Airflow 对 CLI 输出敏感信息的一项安全增强:airflow connections list与airflow variables list两个子命令默认不再展示连接密码、URI 凭据与变量值,只有显式传入--show-values才显示完整明细,并新增--hide-sensitive用于在展示时对密码、URI、extras 等敏感字段做打码处理。读完本文,你将掌握这两个命令的三种输出模式、底层掩码实现原理、参数约束以及如何在实际运维中安全地使用它们。
变更背景:为什么默认要隐藏敏感值
在 Apache Airflow 的日常运维中,connections list和variables list是排查连接配置与全局变量的高频命令。过去这类命令会直接输出密码、URI 中的账号口令以及extra扩展字段,一旦终端日志被采集、粘贴到工单或分享到协作工具中,极易造成凭据泄露。
本次变更将"安全默认值"确立为 CLI 行为规范:
airflow connections list默认只输出连接 ID(conn_id)与连接类型(conn_type);airflow variables list默认只输出变量名(key),不显示变量值;- 需要完整明细时,显式使用
--show-values; - 需要"显示结构但不泄露内容"时,组合使用
--show-values --hide-sensitive对密码、URI 凭据、extras 等字段打码。
这一设计原则与 Airflow 现有的敏感字段保护机制一脉相承。在 airflow-core/src/airflow/config_templates/config.yml 中,core.hide_sensitive_var_conn_fields(自 2.1.0 起默认True)负责在 UI 与任务日志中隐藏敏感 Variables 和 Connection extra JSON 键;本次 CLI 增强则是把同类保护延伸到终端输出。
connections list:三种输出模式
命令入口位于 airflow-core/src/airflow/cli/commands/connection_command.py,参数定义在 airflow-core/src/airflow/cli/cli_config.py。
模式一:默认输出(仅 ID 与类型)
不附加任何参数时,通过ConnectionDisplayMapper.ids_only输出,只包含conn_id与conn_type:
$ airflow connections list conn_id conn_type --------- ---------- my_pg postgres my_redis redis源码中ids_only返回的字典只有两个字段,从结构上保证密码、URI 等字段根本不会进入输出流:
@staticmethod def ids_only(conn: Connection) -> dict[str, Any]: """Return only connection identifiers (no sensitive values). Used by list by default.""" return { "conn_id": conn.conn_id, "conn_type": conn.conn_type, }模式二:--show-values显示完整明细
显式确认需要完整信息时,使用--show-values,此时映射器切换为ConnectionDisplayMapper.full_details,输出包含id、conn_id、conn_type、description、host、schema、login、password、port、is_encrypted、is_extra_encrypted、extra_dejson以及get_uri等全部字段。这是唯一一种会明文输出密码与 URI 的模式,请仅在可信终端使用。
$ airflow connections list --show-values模式三:--show-values --hide-sensitive结构完整、内容打码
同时传入两个参数时,映射器切换为ConnectionDisplayMapper.masked_sensitive:
@staticmethod def masked_sensitive(conn: Connection) -> dict[str, Any]: """Return full connection structure with password, extra, and URI credentials masked.""" return { "id": conn.id, "conn_id": conn.conn_id, "conn_type": conn.conn_type, "description": conn.description, "host": conn.host, "schema": conn.schema, "login": conn.login, "password": SENSITIVE_PLACEHOLDER if conn.password else conn.password, "port": conn.port, "is_encrypted": conn.is_encrypted, "is_extra_encrypted": conn.is_extra_encrypted, "extra_dejson": SENSITIVE_PLACEHOLDER if conn.extra_dejson else conn.extra_dejson, "get_uri": _mask_uri_credentials(conn.get_uri()), }打码规则有三点,值得展开:
- password 字段:非空密码一律替换为占位符
***(SENSITIVE_PLACEHOLDER定义于 airflow-core/src/airflow/cli/utils.py),空密码保持原样; - extra_dejson 字段:只要存在
extra扩展内容,整个 JSON 以***隐藏(extras 中常存放令牌、密钥等敏感项,因此采用整体隐藏策略); - get_uri 字段:不做整串替换,而是由
_mask_uri_credentials仅掩码凭据部分,保留连接结构可读性(见下文)。
URI 凭据掩码算法
_mask_uri_credentials的实现位于 connection_command.py,基于urllib.parse.urlsplit / urlunsplit完成"保留结构、掩码凭据":
def _mask_uri_credentials(uri: str) -> str: if not uri: return uri try: parsed = urlsplit(uri) if not parsed.scheme: return SENSITIVE_PLACEHOLDER if "@" in parsed.netloc: _creds, host_port = parsed.netloc.split("@", 1) masked_netloc = f"{SENSITIVE_PLACEHOLDER}:{SENSITIVE_PLACEHOLDER}@{host_port}" return urlunsplit((parsed.scheme, masked_netloc, parsed.path, parsed.query, parsed.fragment)) return uri except Exception: return SENSITIVE_PLACEHOLDER典型效果(由单元测试 airflow-core/tests/unit/cli/commands/test_connection_command.py 的参数化用例直接佐证):
| 输入 URI | 掩码后输出 |
|---|---|
postgresql://user:pass@host:5432/db | postgresql://***:***@host:5432/db |
mysql://admin:secret@localhost:3306/test | mysql://***:***@localhost:3306/test |
http://api:key123@api.example.com:8080/v1 | http://***:***@api.example.com:8080/v1 |
sqlite:///tmp/test.db(无凭据) | 原样保留 |
redis://localhost:6379/0(无凭据) | 原样保留 |
| 空字符串 | 原样返回 |
invalid-uri(解析失败) | 整体替换为*** |
边界行为非常明确:无凭据的 URI 原样输出(没有可泄露内容),解析失败的 URI 整体打码(宁可多掩码也不冒险)。
variables list:默认只显键名,显值时整体打码
变量子命令实现于 airflow-core/src/airflow/cli/commands/variable_command.py,参数定义在 cli_config.py。
$ airflow variables list key ---------- api_endpoint db_password与连接不同,变量名本身无法被自动归类为"敏感"或"非敏感"(一个叫api_endpoint的变量和叫db_password的变量在系统层面没有区别),因此variables list的隐藏策略更加保守:
- 默认:只查
Variable.key的去重列表,值完全不进入查询与输出路径; --show-values:查询全部变量,映射器输出key与val;--show-values --hide-sensitive:所有变量值一律替换为***,不做任何个别判断:
@staticmethod def with_values(var, hide_sensitive: bool = False) -> dict[str, str]: """Return variable with value, optionally masked.""" key = var.key if hasattr(var, "key") else var["key"] raw = var.val if hasattr(var, "val") else var.get("val", var.get("_val")) val = "" if raw is None else str(raw) if hide_sensitive: val = SENSITIVE_PLACEHOLDER return {"key": key, "val": val}这正是变量场景下的正确取舍:既然无法自动区分敏感变量,--hide-sensitive就把所有值都打码,由使用者自行决定是否进一步查看。
参数约束:--hide-sensitive不能单独使用
两个命令都实现了相同的参数校验,单独传入--hide-sensitive而无--show-values时会直接报错退出:
$ airflow connections list --hide-sensitive --hide-sensitive can only be used with --show-values$ airflow variables list --hide-sensitive --hide-sensitive can only be used with --show-values校验逻辑位于两处命令实现中(connection_command.py#L162-L163 与 variable_command.py#L80-L81),单元测试也分别覆盖了这一行为(test_connection_command.py#L144-L148、test_variable_command.py#L275-L278)。语义上--hide-sensitive是对"展示行为"的修饰,因此必须与--show-values搭配才有意义。
同族增强:config list也具备相同参数
本次安全默认值设计并非只落在连接与变量上。cli_config.py 中,airflow config list同样定义了--show-values与--hide-sensitive:
- 默认只显示配置项名称,值(含潜在敏感项)一律隐藏;
--show-values显示配置值;- 组合
--hide-sensitive时,密码、密钥、令牌等敏感配置值被隐藏,仅展示非敏感配置。
可以看到,"默认隐藏、显式放行"已成为 Airflow CLI 输出层的一致策略,运维脚本在迁移时应同步检查对config list的解析逻辑。
源码级验证:测试如何锁定新行为
单元测试对默认行为与两种显式模式做了完整断言,可当作行为规格书阅读:
- 默认模式不泄露字段(test_connection_command.py#L100-L109):断言输出中不出现
get_uri与password,只出现conn_id与conn_type; --show-values输出完整明细(#L111-L118):断言get_uri出现在输出中;--show-values --hide-sensitive打码(#L120-L142):断言输出包含"password": "***",且get_uri凭据被选择性掩码;- 变量掩码行为(test_variable_command.py#L237-L278):分别覆盖默认仅键名、
--show-values显值、组合参数全量打码、非法组合报错四种场景。
升级注意事项与最佳实践
- 默认行为的破坏性变更:依赖
airflow connections list/variables list明文输出做自动化解析的脚本,升级后默认拿到的字段会变少。请改为显式传入--show-values,并对输出做好落盘权限与日志脱敏管理。 - 打码不是加密:
--hide-sensitive仅做展示层隐藏,数据库中的连接密码与变量值并未被修改,也不应被视为访问控制手段。 - 审计分享用打码模式:需要把连接/变量清单贴进工单或协作群时,优先使用
--show-values --hide-sensitive,保证"结构可见、内容不泄"。 - 注意 CLI 迁移方向:源码中
connections_list与variables_list均带有@deprecated_for_airflowctl("airflowctl connections list")/("airflowctl variables list")装饰器(见 connection_command.py#L149、variable_command.py#L66),表明 Airflow 正引导用户迁移到新一代airflowctl管理工具(源码位于 airflow-ctl/src/airflowctl)。新项目建议直接评估 airflowctl 的对应能力。 - 与配置项协同:结合 config.yml 中的
hide_sensitive_var_conn_fields(默认True)与sensitive_var_conn_names(可扩展敏感关键词列表),可以在 UI、日志、CLI 三个层面形成统一的敏感信息防护体系。
小结
通过本次变更,airflow connections list与airflow variables list建立了"默认最小化披露、显式--show-values放行、--hide-sensitive兜底打码"的三级输出模型:连接场景对 URI 凭据做结构保留式掩码,变量场景因无法自动分类而采用全量打码;config list也遵循同一套参数约定。配合源码中的映射器设计(ids_only/masked_sensitive/full_details)与单元测试锁定,这套机制既保证了日常排障的可用性,也把敏感信息泄露的风险降到了最低。
【免费下载链接】airflowApache Airflow - A platform to programmatically author, schedule, and monitor workflows项目地址: https://gitcode.com/GitHub_Trending/ai/airflow
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考