CI 里用服务账号的 token 调 /v1/organization/api_keys 建临时 key,返回 403 原文 must be owner to perform this action,同一个服务账号调模型接口却完全正常。
原因不止一个,得分开看。创建组织级 key 需要 owner 角色;服务账号只被授予了项目范围,没有组织范围;组织开启 SSO 后 API 建 key 默认被禁用;请求带的是项目 key 而不是 admin key。
第一件事是确认当前 token 的身份和角色
curl -s https://api.openai.com/v1/organization/me -H "Authorization: Bearer $TOKEN" | jq '{type:.object, roles:.roles}'
随后用 owner 的 admin key 建一次验证权限模型
curl -X POST https://api.openai.com/v1/organization/api_keys -H "Authorization: Bearer $OWNER_KEY" -d '{"name":"ci-temp","project_id":"proj_a"}'
接着改成由 owner 预建长期 key 再下发到 CI
curl -s https://api.openai.com/v1/organization/api_keys -H "Authorization: Bearer $OWNER_KEY" | jq -r '.data[] | [.name,.redacted_value] | @tsv'
等前面都确认过,再在 CI 里只读这个 key 验证可用
curl -s -o /dev/null -w '%{http_code}\n' https://api.openai.com/v1/models -H "Authorization: Bearer $CI_KEY"
那几天正好在做别的变更,先把时间线对齐才敢动手。
排查用的命令都是临时敲的,没有写成脚本,事后才整理。
别只看报错消失,验证要多做一步。CI 用预建 key 调模型返回 200,建 key 的动作只保留在 owner 的人工流程里,403 不再出现在流水线。
这里还有个坑。服务账号不等于管理员,很多组织级写操作只认 owner,把建 key 收敛成一次人工动作比在流水线里提权安全得多。拖得越久,排查成本越高,因为现场状态会被后续操作覆盖。
