ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Cloudflare Secrets Store 实战指南:为 Workers 与 AI Gateway 构建账户级密钥管理

Cloudflare Secrets Store 实战指南:为 Workers 与 AI Gateway 构建账户级密钥管理 Cloudflare Secrets Store 实战指南为 Workers 与 AI Gateway 构建账户级密钥管理【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills本文是 skills 仓库Skills Catalog for Codex中 cloudflare-deploy 技能集内 Secrets Store 模块的完整技术指南。Cloudflare Secrets Store 提供账户级加密密钥管理可跨多个 Worker 复用凭据并与 AI Gateway 集成。读完本文你将掌握其架构与权限模型、wrangler 配置与命令、Worker 绑定 API 与 REST API 的完整用法以及密钥轮换、加密、审计等生产级实战模式并能系统排查常见错误。核心概念与架构Secrets Store是 Cloudflare 提供的账户级加密密钥管理服务与传统的Worker Secrets每 Worker 独立通过wrangler secret put管理形成互补。两者核心区别在于作用域Secrets Store 的密钥归属于账户可在多个 Worker 间复用而 Worker Secrets 归属于单个 Worker。架构组成Store存储密钥的容器。Beta 阶段每个账户仅允许 1 个 store。Secret密钥字符串类型单个密钥最大1024 字节。Scopes作用域权限边界控制密钥可被谁访问workers供 Workers 运行时访问ai-gateway供 AI Gateway 访问密钥必须拥有正确的 scope绑定才能生效。Bindings绑定通过 Worker 的env对象将密钥连接到运行时绑定后才可在代码中访问。区域可用性Secrets Store 全球可用但中国网络区域China Network不可用部署到该区域的 Worker 无法使用本服务。访问控制与权限模型Secrets Store 提供四级账户角色权限按职责边界划分角色权限Super Admin超级管理员完全访问Admin管理员创建/编辑/删除密钥查看元数据Deployer部署者查看元数据与绑定Reporter只读报告者仅查看元数据此外使用 API Token 进行自动化操作时需要授予Account Secrets Store Edit/Read权限。这意味着在 CI/CD 中使用的CLOUDFLARE_API_TOKEN必须包含该权限范围否则 wrangler 的 secrets-store 命令会因权限不足而失败。Beta 阶段限制限制项数值说明每账户密钥数上限100Beta 限制每账户 store 数上限1Beta 限制单个密钥大小上限1024 字节按密钥计配额统计仅统计生产密钥本地开发密钥不计入配额可用作用域workers、ai-gateway必须有正确 scope 才能访问作用域粒度账户级可跨多个 Worker 复用访问方式await env.BINDING.get()仅异步出错时抛异常管理方式集中式通过 secrets-store 命令管理本地开发独立的本地密钥不带--remote创建区域可用性全球除中国网络中国网络区域不可用选型Secrets Store 还是 Worker Secrets应使用 Secrets Store 的场景多个 Worker 共享同一凭据如 Stripe API Key、数据库连接串需要集中化管理密钥合规要求需要审计轨迹团队协作共同管理密钥。应使用 Worker Secrets 的场景密钥仅属于某一个 Worker简单的单 Worker 项目无需跨 Worker 共享。判断要点如果同一个凭据出现在多个 Worker 的配置里就应该立即迁移到 Secrets Store。参考文档地图Secrets Store 模块共包含 5 份文档按任务给出了明确的阅读顺序任务起点接着读快速概览README-首次配置configurationapi给 Worker 添加密钥configurationapi实现访问模式apipatterns排查错误gotchasapi密钥轮换patternsconfiguration最佳实践gotchaspatterns相关集成文档workers 模块 —— Worker 绑定集成wrangler 模块 —— CLI 密钥管理命令bindings 配置模块 —— 各类绑定的通用配置约定。Wrangler 配置为 Worker 绑定 Secrets Store基础绑定wrangler.jsonc{ secrets_store_secrets: [ { binding: API_KEY, store_id: abc123, secret_name: stripe_api_key } ] }wrangler.toml等价写法[[secrets_store_secrets]] binding API_KEY store_id abc123 secret_name stripe_api_key字段说明binding代码中通过env访问的变量名例如env.API_KEYstore_id来自wrangler secrets-store store list的输出是账户下唯一 store 的 IDsecret_namestore 内密钥的标识符不能包含空格且大小写敏感。环境特定配置production / stagingwrangler.jsonc{ env: { production: { secrets_store_secrets: [ { binding: API_KEY, store_id: prod-store, secret_name: prod_api_key } ] }, staging: { secrets_store_secrets: [ { binding: API_KEY, store_id: staging-store, secret_name: staging_api_key } ] } } }wrangler.toml等价写法[env.production] [[env.production.secrets_store_secrets]] binding API_KEY store_id prod-store secret_name prod_api_key [env.staging] [[env.staging.secrets_store_secrets]] binding API_KEY store_id staging-store secret_name staging_api_key生产与预发环境各自绑定独立的 store 与密钥名可避免测试数据污染生产凭据。部署时使用wrangler deploy --env staging/wrangler deploy分别发布到对应环境。Wrangler 命令全览Store 管理# 列出 store获取 store_id wrangler secrets-store store list # 创建 store--remote 表示作用于云端生产环境 wrangler secrets-store store create my-store --remote # 删除 store wrangler secrets-store store delete store-id --remote密钥管理生产环境# 交互式创建 wrangler secrets-store secret create store-id \ --name MY_SECRET --scopes workers --remote # 管道式创建推荐用于 CI/CD避免交互 cat secret.txt | wrangler secrets-store secret create store-id \ --name MY_SECRET --scopes workers --remote # 列表 / 查看 / 更新 / 删除 wrangler secrets-store secret list store-id --remote wrangler secrets-store secret get store-id --name MY_SECRET --remote wrangler secrets-store secret update store-id --name MY_SECRET --new-value val --remote wrangler secrets-store secret delete store-id --name MY_SECRET --remote # 复制用于轮换或环境间复制 wrangler secrets-store secret duplicate store-id \ --name ORIG --new-name COPY --remote本地开发关键点生产密钥--remote在本地开发环境中不可访问。本地开发需要单独创建本地密钥不带--remote# 创建仅本地可用的密钥不带 --remote wrangler secrets-store secret create store-id --name DEV_KEY --scopes workers wrangler dev # 使用本地密钥 wrangler deploy # 使用生产密钥最佳实践是为本地与生产使用不同的密钥名通过环境配置区分{ env: { development: { secrets_store_secrets: [ { binding: API_KEY, store_id: store, secret_name: dev_api_key } ] }, production: { secrets_store_secrets: [ { binding: API_KEY, store_id: store, secret_name: prod_api_key } ] } } }Dashboard 操作创建密钥进入Secrets Store→Create secret填写Name不含空格、Value值、Scope选择Workers、Comment备注点击Save保存保存后值将被隐藏无法再次查看明文。添加绑定方法一Worker → Settings → Bindings → Add → Secrets Store方法二直接从 Worker 设置页的下拉菜单创建新密钥。部署选项Deploy立即 100% 生效Save version保存为版本支持逐步灰度发布。CI/CD 集成GitHub Actions- name: Create secret env: CLOUDFLARE_API_TOKEN: ${{ secrets.CF_TOKEN }} run: | echo ${{ secrets.API_KEY }} | \ npx wrangler secrets-store secret create $STORE_ID \ --name API_KEY --scopes workers --remote - name: Deploy run: npx wrangler deployGitLab CIscript: - echo $API_KEY_VALUE | npx wrangler secrets-store secret create $STORE_ID --name API_KEY --scopes workers --remote - npx wrangler deployCI 环境需要预先设置CLOUDFLARE_API_TOKEN带Account Secrets Store Edit/Read权限并将敏感值存于 CI 平台的 Secret 变量中。在沙箱环境执行部署时若网络调用被拦截可按 cloudflare-deploy 技能总览 SKILL.md 的说明以sandbox_permissionsrequire_escalated重新执行。Binding API在 Worker 代码中读取密钥基础访问关键约束Binding 的.get()是异步方法密钥不会直接暴露为字符串且.get()出错时会抛异常而非返回 null因此必须使用 try/catch 处理。interface Env { API_KEY: { get(): Promisestring }; } export default { async fetch(request: Request, env: Env): PromiseResponse { const apiKey await env.API_KEY.get(); return fetch(https://api.example.com, { headers: { Authorization: Bearer ${apiKey} } }); } }错误处理export default { async fetch(request: Request, env: Env): PromiseResponse { try { const apiKey await env.API_KEY.get(); return fetch(https://api.example.com, { headers: { Authorization: Bearer ${apiKey} } }); } catch (error) { console.error(Secret access failed:, error); return new Response(Configuration error, { status: 500 }); } } }多密钥与使用模式// 并行读取多个密钥 const [stripeKey, sendgridKey] await Promise.all([ env.STRIPE_KEY.get(), env.SENDGRID_KEY.get() ]); // ❌ 错误缺少 .get()拿不到实际值 const key env.API_KEY; // ❌ 错误模块级缓存。模块初始化时 env 尚不可用必然失败 const CACHED_KEY await env.API_KEY.get(); // Fails // ✅ 正确请求作用域内缓存同一次请求内可复用 const key await env.API_KEY.get(); // OK - reuse within requestTypeScript 类型官方类型通过cloudflare/workers-types提供import type { SecretsStoreSecret } from cloudflare/workers-types; interface Env { STRIPE_API_KEY: SecretsStoreSecret; DATABASE_URL: SecretsStoreSecret; WORKER_SECRET: string; // 普通 Worker secret直接访问字符串 }注意普通 Worker Secret 的绑定类型是string直接访问而 Secrets Store 绑定是SecretsStoreSecret需.get()异步读取二者在类型上严格区分。REST API 参考基础地址https://api.cloudflare.com/client/v4认证所有请求携带账户级 API Tokencurl -H Authorization: Bearer $CF_TOKEN \ https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/secrets_store/storesStore 操作# 列出 stores GET /accounts/{account_id}/secrets_store/stores # 创建 store POST /accounts/{account_id}/secrets_store/stores {name: my-store} # 删除 store DELETE /accounts/{account_id}/secrets_store/stores/{store_id}Secret 操作# 列出密钥 GET /accounts/{account_id}/secrets_store/stores/{store_id}/secrets # 创建单个 POST /accounts/{account_id}/secrets_store/stores/{store_id}/secrets { name: my_secret, value: secret_value, scopes: [workers], comment: Optional } # 创建批量 POST /accounts/{account_id}/secrets_store/stores/{store_id}/secrets [ {name: secret_one, value: val1, scopes: [workers]}, {name: secret_two, value: val2, scopes: [workers, ai-gateway]} ] # 获取元数据 GET /accounts/{account_id}/secrets_store/stores/{store_id}/secrets/{secret_id} # 更新值或备注 PATCH /accounts/{account_id}/secrets_store/stores/{store_id}/secrets/{secret_id} {value: new_value, comment: Updated} # 删除单个 DELETE /accounts/{account_id}/secrets_store/stores/{store_id}/secrets/{secret_id} # 删除批量 DELETE /accounts/{account_id}/secrets_store/stores/{store_id}/secrets {secret_ids: [id-1, id-2]} # 复制 POST /accounts/{account_id}/secrets_store/stores/{store_id}/secrets/{secret_id}/duplicate {name: new_name} # 配额查询 GET /accounts/{account_id}/secrets_store/quota响应格式成功响应{ success: true, result: { id: secret-id-123, name: my_secret, created: 2025-01-11T12:00:00Z, scopes: [workers] } }错误响应{ success: false, errors: [{code: 10000, message: Name exists}] }TypeScript 辅助函数自定义辅助类型与通用工具函数可显著简化多密钥场景interface SecretsStoreBinding { get(): Promisestring; } // 带降级的读取主密钥失败时回退 async function getSecretWithFallback( primary: SecretsStoreBinding, fallback?: SecretsStoreBinding ): Promisestring { try { return await primary.get(); } catch (error) { if (fallback) return await fallback.get(); throw error; } } // 批量读取 async function getAllSecrets( secrets: Recordstring, SecretsStoreBinding ): PromiseRecordstring, string { const entries await Promise.all( Object.entries(secrets).map(async ([k, v]) [k, await v.get()]) ); return Object.fromEntries(entries); }实战模式Patterns零停机密钥轮换利用版本化命名api_key_v1、api_key_v2实现无缝切换interface Env { PRIMARY_KEY: { get(): Promisestring }; FALLBACK_KEY?: { get(): Promisestring }; } async function fetchWithAuth(url: string, key: string) { return fetch(url, { headers: { Authorization: Bearer ${key} } }); } export default { async fetch(request: Request, env: Env): PromiseResponse { let resp await fetchWithAuth(https://api.example.com, await env.PRIMARY_KEY.get()); // 轮换期间的主密钥失败回退 if (!resp.ok env.FALLBACK_KEY) { resp await fetchWithAuth(https://api.example.com, await env.FALLBACK_KEY.get()); } return resp; } }轮换流程创建api_key_v2→ 添加 fallback 绑定 → 部署 → 将主绑定切换为 v2 → 部署 → 移除v1。整个过程无需停机且任一时刻都至少有一个可用密钥。使用 KV 做加密存储利用 Web Crypto APIAES-GCM在边缘对敏感数据加密后写入 KVinterface Env { CACHE: KVNamespace; ENCRYPTION_KEY: { get(): Promisestring }; } async function encryptValue(value: string, key: string): Promisestring { const enc new TextEncoder(); const keyMaterial await crypto.subtle.importKey( raw, enc.encode(key), { name: AES-GCM }, false, [encrypt] ); const iv crypto.getRandomValues(new Uint8Array(12)); const encrypted await crypto.subtle.encrypt( { name: AES-GCM, iv }, keyMaterial, enc.encode(value) ); const combined new Uint8Array(iv.length encrypted.byteLength); combined.set(iv); combined.set(new Uint8Array(encrypted), iv.length); return btoa(String.fromCharCode(...combined)); } export default { async fetch(request: Request, env: Env): PromiseResponse { const key await env.ENCRYPTION_KEY.get(); const encrypted await encryptValue(sensitive-data, key); await env.CACHE.put(user:123:data, encrypted); return Response.json({ ok: true }); } }加密密钥存放在 Secrets Store 中密文写入 KV实现密钥与密文分离存储的安全基线。IV 与密文拼接后 Base64 编码存储解密时需先取出前 12 字节作为 IV。HMAC 签名interface Env { HMAC_SECRET: { get(): Promisestring }; } async function signRequest(data: string, secret: string): Promisestring { const enc new TextEncoder(); const key await crypto.subtle.importKey( raw, enc.encode(secret), { name: HMAC, hash: SHA-256 }, false, [sign] ); const sig await crypto.subtle.sign(HMAC, key, enc.encode(data)); return btoa(String.fromCharCode(...new Uint8Array(sig))); } export default { async fetch(request: Request, env: Env): PromiseResponse { const secret await env.HMAC_SECRET.get(); const payload await request.text(); const signature await signRequest(payload, secret); return Response.json({ signature }); } }审计与监控在每次密钥使用前后记录审计事件失败时同样上报形成可追溯的审计轨迹export default { async fetch(request: Request, env: Env, ctx: ExecutionContext) { const startTime Date.now(); try { const apiKey await env.API_KEY.get(); const resp await fetch(https://api.example.com, { headers: { Authorization: Bearer ${apiKey} } }); ctx.waitUntil( fetch(https://log.example.com/log, { method: POST, body: JSON.stringify({ event: secret_used, secret_name: API_KEY, timestamp: new Date().toISOString(), duration_ms: Date.now() - startTime, success: resp.ok }) }) ); return resp; } catch (error) { ctx.waitUntil( fetch(https://log.example.com/log, { method: POST, body: JSON.stringify({ event: secret_access_failed, secret_name: API_KEY, error: error instanceof Error ? error.message : Unknown }) }) ); return new Response(Error, { status: 500 }); } } }借助ctx.waitUntil将审计上报放到请求生命周期之外异步执行不阻塞主响应。从 Worker Secrets 迁移核心变化env.SECRET直接字符串→await env.SECRET.get()异步读取。迁移步骤在 Secrets Store 中创建密钥wrangler secrets-store secret create store-id --name API_KEY --scopes workers --remote在wrangler.jsonc中添加绑定{binding: API_KEY, store_id: abc123, secret_name: api_key}更新代码const key await env.API_KEY.get();在 staging 环境测试后部署移除旧密钥wrangler secret delete API_KEY。跨 Worker 共享密钥同一个密钥postgres_url可在不同 Worker 中绑定为不同的变量名// worker-1: bindingSHARED_DB, secret_namepostgres_url // worker-2: bindingDB_CONN, secret_namepostgres_url这是 Secrets Store 相比 Worker Secrets 的核心优势一份凭据、多处绑定、统一管理。JSON 结构化配置将结构化配置存为 JSON 密钥运行时解析interface Env { DB_CONFIG: { get(): Promisestring }; } interface DbConfig { host: string; port: number; username: string; password: string; } export default { async fetch(request: Request, env: Env): PromiseResponse { try { const configStr await env.DB_CONFIG.get(); const config: DbConfig JSON.parse(configStr); // 使用解析后的配置 const dbUrl postgres://${config.username}:${config.password}${config.host}:${config.port}; return Response.json({ connected: true }); } catch (error) { if (error instanceof SyntaxError) { return new Response(Invalid config JSON, { status: 500 }); } throw error; } } }存入 JSON 密钥echo {host:db.example.com,port:5432,username:app,password:secret} | \ wrangler secrets-store secret create store-id \ --name DB_CONFIG --scopes workers --remote与 Service Bindings 集成典型的微服务模式Auth Worker 使用 Secrets Store 中的密钥签发 JWTAPI Worker 通过 Service Binding 调用 Auth Worker 验证签名实现密钥不落地、单向信任链。具体服务绑定模式可参考 workers 模块。常见错误与排查Gotchas.get() 抛异常而非返回 null原因误以为.get()失败时返回 null。解决始终用 try/catch 包裹.get()调用try { const key await env.API_KEY.get(); } catch (error) { return new Response(Configuration error, { status: 500 }); }日志泄漏密钥值原因在 console 或错误信息中意外打印了密钥明文。解决只记录元数据如Retrieved API_KEY绝不记录实际密钥值。模块级访问密钥原因在模块初始化阶段访问密钥此时 env 尚未注入。解决密钥只在请求作用域内缓存不在模块级缓存。Secret not found in store原因密钥名不存在、大小写不匹配、缺少 workers scope、或 store_id 错误。解决用wrangler secrets-store secret list store-id --remote确认密钥存在检查名称是否完全一致大小写敏感确认密钥具有workersscope核对 store_id。Scope Mismatch原因密钥存在但缺少workersscope只有ai-gateway。解决更新密钥作用域wrangler secrets-store secret update store-id --name SECRET --scopes workers --remote或在 Dashboard 中修改。JSON 解析失败原因存储了非法 JSON运行时解析失败。解决存储前先校验# 存储前校验 echo {key:value} | jq . \ echo {key:value} | wrangler secrets-store secret create store-id \ --name CONFIG --scopes workers --remote运行时解析同样加错误处理try { const configStr await env.CONFIG.get(); const config JSON.parse(configStr); } catch (error) { console.error(Invalid config JSON:, error); return new Response(Invalid configuration, { status: 500 }); }本地开发无法访问密钥原因本地环境访问了生产密钥。解决创建不带--remote的本地密钥wrangler secrets-store secret create store-id --name API_KEY --scopes workers本地与生产使用不同密钥名。Property get does not exist原因缺少密钥绑定的 TypeScript 类型定义。解决显式定义接口interface Env { API_KEY: { get(): Promisestring }; }。Binding already exists原因Dashboard 中存在重复绑定或 wrangler.jsonc 与 Dashboard 配置冲突。解决从 Dashboard Settings → Bindings 删除重复项检查冲突配置或删除旧 Worker 密钥wrangler secret delete API_KEY。Account secret quota exceeded原因账户达到 100 个密钥的上限Beta。解决用wrangler secrets-store quota --remote查询配额删除无用密钥、合并重复密钥或联系 Cloudflare 申请提升。总结Cloudflare Secrets Store 将密钥管理从每 Worker 手工维护提升为账户级集中管控配合四级角色权限、明确的 scope 边界、wrangler/Dashboard/REST API 三套管理入口以及await env.BINDING.get()这一统一的异步访问模型是构建多 Worker 共享凭据、满足审计合规要求的推荐方案。上线前请重点核对三点Beta 配额100 密钥 / 1 store / 1024 字节、scope 与绑定匹配、本地与生产密钥命名隔离。更完整的命令与模式说明可继续阅读本模块的 configuration、api、patterns 与 gotchas 文档。【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表