ZenML Service Account API Keys 完全指南:REST API 端点、轮换机制与 CLI 实战
【免费下载链接】zenmlZenML 🙏: One AI Platform from Pipelines to Agents. https://zenml.io.项目地址: https://gitcode.com/GitHub_Trending/ze/zenml
ZenML 通过「服务账户(Service Account)+ API Key」机制为自动化工作流(CI/CD 流水线、定时任务、远程 Agent)提供机器身份认证。本文基于开源仓库中 API Keys 参考文档 与其配套的 Rotate 参考文档,结合服务端端点实现、数据模型与 CLI 源码,完整讲解 API Key 的创建、查询、更新、删除与轮换(Rotation)五个核心操作,以及底层模型字段、权限校验与密钥存储细节。读完本文,你将能够通过curl或zenmlCLI 独立完成服务账户 API Key 的全生命周期管理。
一、背景:为什么需要服务账户与 API Key
ZenML 是一个面向 MLOps 的端到端平台,其 ZenML Server 通过 REST API 暴露所有功能。当 CI/CD 流水线、定时任务或远程 Agent 需要以机器身份(而非交互式用户身份)访问 ZenML Server 时,服务账户(Service Account)与 API Key 就是标准认证方案:
- 服务账户:一种机器身份实体,隶属于服务账户体系(见 Service Accounts 参考文档);
- API Key:绑定在某个服务账户之下的密钥凭据,用于通过
Authorization头或zenml login --api-key方式完成认证。
从服务端路由定义看,API Key 的全部端点都挂在服务账户资源之下,路由前缀为/api/v1/service_accounts,且APIRouter同时使用service_accounts与api_keys两个标签(见 service_accounts_endpoints.py),路径常量定义在 constants.py 与 constants.py:
API_KEYS = "/api_keys" API_KEY_ROTATE = "/rotate" SERVICE_ACCOUNTS = "/service_accounts"二、API 端点总览
API Key 管理共包含6 个端点(5 个位于主文档,1 个轮换端点在配套文档中),全部以/api/v1/service_accounts/{service_account_id}/api_keys为基路径:
| 方法 | 路径 | 用途 |
|---|---|---|
GET | /api/v1/service_accounts/{service_account_id}/api_keys | 列出某服务账户下的所有 API Key |
POST | /api/v1/service_accounts/{service_account_id}/api_keys | 为服务账户创建新的 API Key |
GET | /api/v1/service_accounts/{service_account_id}/api_keys/{api_key_name_or_id} | 获取单个 API Key 详情 |
PUT | /api/v1/service_accounts/{service_account_id}/api_keys/{api_key_name_or_id} | 更新 API Key(改名、改描述、启停用) |
DELETE | /api/v1/service_accounts/{service_account_id}/api_keys/{api_key_name_or_id} | 删除 API Key |
PUT | /api/v1/service_accounts/{service_account_id}/api_keys/{api_key_name_or_id}/rotate | 轮换(重新生成)API Key |
其中{service_account_id}为服务账户 UUID,{api_key_name_or_id}既可以传 API Key 的名称,也可以传其 UUID——从端点签名api_key_name_or_id: Union[str, UUID]可以看出二者皆被接受(见 service_accounts_endpoints.py)。
所有端点均要求通过认证,未认证请求统一返回401;根据操作类型还可能返回404(资源不存在)、409(冲突)、422(参数校验失败)等错误码,路由层通过responses={...}声明了这些错误响应(同文件 L89-L95、L272-L275)。
三、核心数据模型:请求、响应与内部编码
在 api_key.py 中定义了整套 API Key 的 Pydantic 模型,理解这些模型是正确调用 REST API 的前提。
3.1 请求模型
创建 API Key 的请求体APIKeyRequest仅有两个可选填字段(见 api_key.py):
| 字段 | 类型 | 说明 |
|---|---|---|
name | str | API Key 名称,必填,最大长度受STR_FIELD_MAX_LENGTH限制 |
description | Optional[str] | 描述信息,可选,最大长度受TEXT_FIELD_MAX_LENGTH限制 |
更新 API Key 的请求体APIKeyUpdate支持三个可空字段(见 api_key.py):
| 字段 | 类型 | 说明 |
|---|---|---|
name | Optional[str] | 新的名称 |
description | Optional[str] | 新的描述 |
active | Optional[bool] | 是否启用;设为false可立即吊销该 Key 的登录能力,但保留记录 |
3.2 响应模型的关键字段
APIKeyResponse将字段拆分为 body、metadata、resources 三部分,其中 body 与 metadata 暴露给 API 调用方:
- body 部分(见 api_key.py):
key:明文 API Key 值,仅在创建或轮换后的响应中返回一次,其余查询返回null——这与 CLI 提示「Please store it safely as it will not be shown again」完全一致;active:是否启用,默认true;service_account:该 Key 所属的服务账户对象。
- metadata 部分(见 api_key.py):
description:描述;retain_period_minutes:轮换后旧 Key 的保留时长(分钟);last_login:最近一次使用该 Key 登录的时间戳;last_rotated:最近一次轮换的时间戳。
3.3 密钥的编码与存储(底层原理)
API Key 并非明文存储。从源码看存在两套机制:
- 对外编码:
APIKey.encode()将{id, key}JSON 做 Base64 编码并加上ZENML_API_KEY_PREFIX前缀,形成对外可见的密钥串;decode_api_key()负责反向解析(见 api_key.py)。 - 服务端存储:
APIKeyInternalResponse.verify_key()使用passlib的 bcrypt 方案(CryptContext(schemes=["bcrypt"]))校验密钥哈希,并刻意在哈希缺失时仍执行一次校验,以规避 CWE-204 响应差异攻击(见 api_key.py)。
这也解释了为什么 API 文档反复强调:明文 Key 只在创建/轮换时出现一次,服务端只保留其哈希。
四、端点逐个详解
4.1 列出 API Key:GET .../api_keys
- 参数:路径参数
service_account_id;查询参数支持分页、排序与过滤。APIKeyFilter提供name、description、active、last_login、last_rotated等过滤条件,并强制将查询范围限定在指定服务账户(见 api_key.py)。 - 响应:分页对象
Page[APIKeyResponse]。默认hydrate=false,即不包含 metadata 扩展字段。 - 服务端逻辑(见 service_accounts_endpoints.py):先校验调用方对服务账户的
READ权限,再调用zen_store().list_api_keys()返回结果。 - 示例:
curl -X GET "https://<ZENML_SERVER>/api/v1/service_accounts/<SA_ID>/api_keys" \ -H "Authorization: Bearer <ACCESS_TOKEN>"4.2 创建 API Key:POST .../api_keys
- 请求体:
APIKeyRequest,即{ "name": "...", "description": "..." }。 - 响应:
APIKeyResponse,且本次响应中的key字段包含完整的明文密钥串,必须立即保存。 - 服务端逻辑(见 service_accounts_endpoints.py):创建前会检查服务账户是否由外部认证(
external_user_id)创建,若是则抛出IllegalOperationError,禁止为其关联 API Key;同时要求调用方具备管理员权限(无 RBAC 模式)或相应 RBAC 权限。 - 示例:
curl -X POST "https://<ZENML_SERVER>/api/v1/service_accounts/<SA_ID>/api_keys" \ -H "Authorization: Bearer <ACCESS_TOKEN>" \ -H "Content-Type: application/json" \ -d '{"name": "ci-runner-key", "description": "Key used by CI pipeline"}'4.3 获取单个 API Key:GET .../api_keys/{api_key_name_or_id}
- 参数:
api_key_name_or_id支持名称或 UUID;hydrate查询参数控制是否返回 metadata 扩展字段(默认true)。 - 注意:该端点返回的
key字段为null(明文只在创建/轮换时可见)。 - 服务端逻辑(见 service_accounts_endpoints.py):先校验服务账户的
READ权限,再通过zen_store().get_api_key()获取。
4.4 更新 API Key:PUT .../api_keys/{api_key_name_or_id}
- 请求体:
APIKeyUpdate,可组合更新name、description、active。 - 典型用途:将
active设为false立即吊销某个泄露的 Key;或为 Key 重新命名以反映其用途。 - 服务端逻辑(见 service_accounts_endpoints.py):与创建一样会拦截外部认证创建的服务账户,并校验服务账户的
UPDATE权限。 - 示例(吊销 Key):
curl -X PUT "https://<ZENML_SERVER>/api/v1/service_accounts/<SA_ID>/api_keys/my-key" \ -H "Authorization: Bearer <ACCESS_TOKEN>" \ -H "Content-Type: application/json" \ -d '{"active": false}'4.5 删除 API Key:DELETE .../api_keys/{api_key_name_or_id}
- 参数:
api_key_name_or_id;无请求体,成功返回204。 - 服务端逻辑(见 service_accounts_endpoints.py):校验调用方为管理员(无 RBAC 模式)或具备服务账户
UPDATE权限,随后从 ZenStore 删除。删除操作立即生效,使用该 Key 的客户端将立刻认证失败。
4.6 轮换 API Key:PUT .../api_keys/{api_key_name_or_id}/rotate
轮换是 API Key 管理的核心安全操作,配套文档 rotate.md 单独记录了该端点。
- 请求体
APIKeyRotateRequest:仅一个字段retain_period_minutes(默认0),表示轮换后旧 Key 继续有效的分钟数(见 api_key.py)。设为0表示立即作废旧 Key。 - 响应:
APIKeyResponse,key字段为新生成的明文密钥,需立即保存。 - 服务端逻辑(见 service_accounts_endpoints.py):同样拦截外部认证服务账户,并校验
UPDATE权限后调用zen_store().rotate_api_key()。
轮换的底层语义(见 api_key.py):内部模型维护key_generation(当前代次)与previous_key(上一代密钥哈希)。轮换后:
- 新密钥成为当前代(
key_generation + 1); - 旧密钥在
retain_period_minutes窗口内仍可认证,实现零停机轮换(先轮换、再分批更新各客户端、最后等窗口过期); is_previous_key_retained()判断旧代是否仍在保留窗口内;verify_key()会同时校验当前代与保留期内的上一代。
轮换示例(保留 30 分钟旧 Key):
curl -X PUT "https://<ZENML_SERVER>/api/v1/service_accounts/<SA_ID>/api_keys/my-key/rotate" \ -H "Authorization: Bearer <ACCESS_TOKEN>" \ -H "Content-Type: application/json" \ -d '{"retain_period_minutes": 30}'五、权限与合规约束
调用上述端点时需注意两类约束(见 service_accounts_endpoints.py):
- 外部认证限制:当 ZenML Server 配置为外部认证(
AuthScheme.EXTERNAL)时,工作区级服务账户的变更操作(创建/更新/删除 API Key、轮换)会被_ensure_workspace_service_account_mutation_allowed()直接拦截,提示改用组织级服务账户;外部认证创建的服务账户(external_user_id非空)也不能挂载 API Key。 - 权限要求:无 RBAC 模式下,创建、更新、删除、轮换均要求调用方为管理员(
is_admin);RBAC 模式下则由verify_permissions_and_create_entity、verify_permission_for_model等工具按资源类型(ResourceType.SERVICE_ACCOUNT)与动作(READ/UPDATE/DELETE)细粒度校验(见 service_accounts_endpoints.py)。
六、CLI 实战:更便捷的管理方式
REST API 之外,ZenML CLI 在 service_accounts.py 中提供了等价的命令组,适合日常运维。命令统一以zenml service-account api-key为前缀,服务账户名称或 ID 作为位置参数传入。
| 操作 | 命令 | 关键选项 |
|---|---|---|
| 创建 | zenml service-account api-key create <SA> <NAME> | -d/--description;--set-key将新 Key 直接配置到本地客户端;--output-file写入文件 |
| 查看 | zenml service-account api-key describe <SA> <NAME_OR_ID> | 输出会排除key字段,避免泄露明文 |
| 列表 | zenml service-account api-key list <SA> | 支持--columns与多种输出格式(table/json/yaml/csv/tsv) |
| 更新 | zenml service-account api-key update <SA> <NAME_OR_ID> | --name、--description、--active |
| 轮换 | zenml service-account api-key rotate <SA> <NAME_OR_ID> | --retain <分钟>;--set-key;--output-file |
| 删除 | zenml service-account api-key delete <SA> <NAME_OR_ID> | -y/--yes跳过确认 |
轮换并立即配置本地客户端(见 service_accounts.py):
zenml service-account api-key rotate my-sa my-key --retain 30 --set-key执行后 CLI 会调用client.set_api_key()将新 Key 写入本地配置(仅当当前客户端连接的是 ZenML Server 的 REST Store 时可用);未使用--set-key/--output-file时,会在终端打印新 Key 值并提示使用zenml login <URL> --api-key完成登录(见 service_accounts.py)。
七、最佳实践小结
- 一次性保存明文:
key明文只在创建与轮换响应中出现,务必通过--output-file写入安全存储或立即配置到密钥管理器; - 零停机轮换:轮换时设置合理的
retain_period_minutes过渡窗口,先轮换再依次更新各消费方,最后等待旧 Key 自然过期; - 快速吊销:怀疑泄露时优先
PUT将active置为false(保留审计记录),确认后彻底DELETE; - 身份隔离:为不同 CI 任务、不同环境各建独立服务账户与 Key,配合
APIKeyFilter的last_login字段审计使用情况。
以上全部端点行为均可通过 service_accounts_endpoints.py、api_key.py 与 service_accounts.py 三个源码文件逐一印证,API 参考文档本身位于 README.md 与 rotate.md。
【免费下载链接】zenmlZenML 🙏: One AI Platform from Pipelines to Agents. https://zenml.io.项目地址: https://gitcode.com/GitHub_Trending/ze/zenml
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考