共享 secret store 和 egress proxy
本页涵盖共享密钥存储、默认关闭的密钥出口代理及其流量允许列表,以及基于文件的 API 密钥。
共享密钥存储¶
共享密钥存储是一个 Gateway 全局、团队范围的位置,用于存放应可供使用同一状态数据库的每个 Gateway 进程访问的密钥和环境值。可在 Control UI 的 设置 → 密钥 中管理,或本地使用 openclaw secrets store。CLI 命令操作本地状态数据库,不接受 Gateway URL 或 token 选项。
条目具有两种显式访问模式。两者都保留现有的 secret 和 env 存储类型,且任一类型都可以作为 SecretRef 的后端:
- 受保护密钥(
kind: "secret")的值在保存后为只写。Gateway 列表结果、Control UI 以及 CLI list/get 输出从不包含它们;不存在 reveal RPC。受保护值处于惰性状态,直到受支持的配置字段通过 SecretRef 引用它,或已启用且绑定目的地的 密钥出口代理 使用它。 - Agent 可读环境(
kind: "env")的值在 Control UI 中仍对管理员可见,并可通过store list和store get返回。OpenClaw 将它们以明文形式添加到通过其 exec 工具运行的 Gateway 托管命令中,位于继承的进程值之后、显式每次调用 env 之前。Agent 可以打印、传输或持久化这些值。受保护的主机密钥会被忽略,并显示可见警告。
Agent 可读环境值不会到达 Codex 原生 shell、Codex 沙箱 exec-server、诸如 Claude Code 的 ACP 子进程、OpenClaw 沙箱 exec 或远程 node exec。这些路径会组装不同的子环境。在符合条件的 Codex app-server 轮次中,使用 gateway_exec 有意重新进入 OpenClaw Gateway 执行路径;gateway_process 提供现有的每会话后台后续操作。普通本地工作仍优先使用原生 Codex shell。Gateway 托管 exec 会在一次运行中的首次执行时捕获存储快照。后续添加、替换、删除和主机编辑需要新的运行;存储凭据不会刷新已捕获的 exec 快照。
默认情况下,secret 条目从不注入子进程环境。当默认关闭的 密钥出口代理 启用时,Gateway 托管 exec 命令会收到进程本地哨兵,而不是明文值。
名称使用与 env SecretRefs 相同的大写语法,每个 UTF-8 值限制为 64 KiB(65,536 字节)。存储保留提交的空白和换行。secret 条目必须携带值;空密钥会被拒绝,因为它们只会表现为令人困惑的下游认证失败。env 条目可以为空。这支持 PEM 密钥和服务账户 JSON,而不会继承普通环境变量的较小限制。
从 openclaw.json 使用 store 源引用条目:
{
models: {
providers: {
openai: {
apiKey: { source: "store", provider: "default", id: "OPENAI_API_KEY" },
},
},
},
}
Control UI 的 set/delete 操作会在更改的名称被活动源配置或 auth-profile 快照中的 store SecretRef 引用时,自动刷新活动的 secrets 运行时。未被引用的名称会跳过该工作。直接 CLI 写入仍然是离线/本地路径;使用 CLI 更改被引用的值后,运行 openclaw secrets reload,使活动内存快照加载该值。
Agent 还可以使用 secrets 工具 要求你添加条目:它指定条目和原因,你将值输入到掩码提示中,Gateway 会直接将其写入存储。该值永远不会进入聊天、转录或模型上下文,并且相同的自动运行时刷新适用。
凭据提示绑定到确切的请求方权限,并在其关闭时取消。已提交的回答是终态的,即使随后的运行时刷新失败。保存的值仍然存在;解决 provider 错误并重试 openclaw secrets reload,而不是回答。使用工具返回的完整 SecretRef,包括其 provider 别名。
Warning
存储值在静态时未加密。它们未加密地存储在共享状态 SQLite 数据库(state/openclaw.sqlite)中,受与其他该数据库中的凭据相同的 0600 文件和 0700 目录权限保护。需要更强存储隔离的运维人员应使用外部 exec provider,例如 1Password 插件 或 Vault SecretRefs。
密钥出口代理¶
密钥出口代理允许 Gateway 托管的 Agent 子进程使用共享存储中的 secret 条目,而无需接收其明文。OpenClaw 将现有已认证哨兵放入子进程环境,然后 Gateway 拥有的回环代理在出口前立即在请求 URL、标头和流式主体中替换它。
监听器运行在专用的 Gateway Worker 中,该 Worker 负责 TLS、证书准备、替换和转发。请求和响应字节保持在 Gateway 主事件循环之外。Gateway 与该 Worker 交换进程授权和证书健康状态;撤销会立即在连接关闭前隔离授权。失败的 Worker 会关闭受保护出口,并要求重启 Gateway。
每个密钥还必须指定允许替换的确切 HTTPS 主机。主机名以小写 ASCII/punycode 形式存储并精确匹配;不支持通配符、后缀匹配和端口。没有允许主机的密钥永远不会被替换。绑定主机而不替换存储值:
重复 --allow-host 以用多个主机替换绑定,或使用 --clear-allowed-hosts 删除所有绑定。被拒绝的请求会指定密钥,并打印该目的地所需的精确 store set ... --allow-host ... 命令。
显式启用它,然后重启 Gateway:
例如,将 OpenAI 密钥绑定到其 API 主机并启用代理:
openclaw secrets store set OPENAI_API_KEY --allow-host api.openai.com
openclaw config set secrets.egressProxy.enabled true --strict-json
重启 Gateway 后,Gateway 托管的智能体可以运行:
在智能体环境中,$OPENAI_API_KEY 是一个 oc-sent-v2...end 哨兵。代理仅针对 api.openai.com 将其替换为存储值。对未绑定主机的请求会被拒绝,并返回 Secret "OPENAI_API_KEY" is not allowed for host "<host>". Run: openclaw secrets store set OPENAI_API_KEY --allow-host <host>。
等效配置:
{
secrets: {
egressProxy: {
enabled: true,
allowedHosts: ["api.openai.com"],
bypassHosts: ["pinned-api.example.com"],
},
},
}
启用后,OpenClaw 会向 Gateway 托管的 exec 环境添加以下值:
HTTPS_PROXY和HTTP_PROXY,其中每个进程的凭据嵌入在回环代理 URL 中NODE_USE_ENV_PROXY=1,使受支持的 Node.js 全局fetch客户端遵循HTTP_PROXY和HTTPS_PROXY,而无需使用NODE_OPTIONSNODE_EXTRA_CA_CERTS、SSL_CERT_FILE、CURL_CA_BUNDLE、REQUESTS_CA_BUNDLE和GIT_SSL_CAINFO,指向 Gateway 的可信证书包- 每个 team-store
secret条目作为oc-sent-v2...end哨兵;env条目保留其现有行为和优先级
代理身份验证使用标准 Basic 代理身份验证,用户名为 openclaw,并为每个受管理的 exec 进程使用随机密码。OpenClaw 在审批和启动检查后创建授权。当源智能体回合结束时,后台命令保留其授权;其进程监督者同时拥有执行和代理访问权限。Base64 不被视为加密:监听器仅绑定到回环地址,并且能够从智能体环境中读取代理令牌的进程已经可以读取该环境中的哨兵。缺失、错误或已吊销的凭据会收到 407 Proxy Authentication Required,并且永远不会被转发。
进程退出、启动失败、取消和超时都会吊销该进程的授权,并拆除其代理连接、上游请求和旁路隧道。取消会在原生进程终止之前吊销访问权限。停止一个命令不会吊销同级命令的授权。Gateway 关闭会吊销所有授权;重启需要启动新命令。新授权无法恢复已吊销的连接或绑定。在吊销之前已交给上游传输的字节无法撤回。
每个进程都会收到其所属运行 secret 快照的固定副本,包括每个哨兵的 secret 名称和允许的主机。后续命令无法更改现有进程的授权。代理身份验证后,代理会在该进程的注册信息中查找匹配的哨兵,并在解密哨兵之前授权规范化后的目标主机名。未注册、未解析、未绑定或绑定到其他主机的哨兵会在其明文被转发之前被拒绝。
Warning
目标绑定不会使允许的主机变得可信。一个反射请求凭据的绑定服务仍可能将明文返回给智能体。由于策略基于主机名而非 IP 固定,DNS 层面的入侵可以重定向被允许的主机名。非 HTTPS 请求会被拒绝而不是受到保护,例外是发往字面回环目标的纯 HTTP 请求,代理会按照其流量策略转发这些请求,而不替换 secret。HTTPS 拦截仍然存在以下协议限制。当这些威胁在范围内时,请使用外部网络策略或进程隔离。
CA 在每次 Gateway 启动时于状态目录下生成一次,证书有效期为十年。其密钥仍由进程拥有,而不是保留十年。一天的叶子证书会在其最后一小时内按需续期,而无需替换 CA 或中断已建立的 TLS 连接。这使得已在运行的子进程在续期后仍信任同一颁发者。其目录权限为 0700,私钥权限为 0600,它会在 Gateway 关闭时删除,并且 OpenClaw 永远不会将其安装到系统信任存储中。当哨兵无法通过身份验证或解析时,请求会失败关闭;代理永远不会转发或静默移除未解析的哨兵。请求体作为流进行扫描,并带有有界的携带窗口,因此当哨兵跨越块边界或出现在大型上传中时,替换也能正常工作。
openclaw status 和 openclaw doctor 会报告证书准备失败,并在进程 CA 距离过期不足七天时发出警告。准备失败会以可操作的错误拒绝新的 CONNECT 请求;在修正 OpenSSL、文件系统访问或时钟问题后,下一个请求可以重试。已过期的 CA 或尚未生效的 CA 需要检查系统时钟并重启 Gateway,而不是禁用 TLS 验证。在受保护的出口降级时,Gateway RPC 仍可能保持可达。要进行只读的机器可读探测,请运行 openclaw doctor --lint --only core/doctor/gateway-health --json;默认 JSON 检查不会探测正在运行的 Gateway。
对于具有有效 Content-Length 且最多为 100 MiB 的 HTTP 请求,代理会将原始字节收集到一个进程内存缓冲区中。然后它会检查当前目标和哨兵绑定,就地替换,并将测量的字节长度发送到上游。这保留了固定长度的二进制上传,而无需为每个传入块保留一个对象。哨兵替换不能扩展请求体;没有 MIME 类型可以免于扫描,也不会写入请求体临时文件。
每个代理在命令之间共享 128 MiB 的预留预算,按声明的请求体长度加上每个请求 256 KiB 的余量计费,最多有 64 个缓冲上传正在准备或发送。这限制了暂存的有效载荷和请求数量,而不是总进程 RSS。繁忙的代理会以 503 拒绝额外的缓冲上传;请在进行中的请求完成后重试。每个缓冲上传都有五分钟的准备/发送期限,包括上游连接设置。超时、取消、授权吊销和传输失败会释放其资源。在收集期间不会打开上游连接,并且转发的审计记录的是上游发送完成,而不是缓冲区准备。
100 MiB 封装是每个请求的暂存限制,而不是目标上传大小限制。更大的请求以及长度未知的请求会继续以分块帧和背压方式流式传输;要求 Content-Length 的目标仍可能拒绝这些请求。已交给上游传输的字节无法收回。
bypassHosts 包含必须保持端到端 TLS 的精确主机名,以用于证书固定客户端。这些主机使用已认证的盲 CONNECT 隧道。隧道内部无法进行替换;发送到那里的哨兵值在设计上是安全的,因为它是已认证的密文而非凭据,因此供应商会看到无效凭据并拒绝它。
流量允许列表¶
目标绑定保护的是机密,而不是流量:一旦命令持有代理凭据,不携带哨兵值的请求就可以到达任何主机。设置 secrets.egressProxy.allowedHosts 以同时限制非哨兵流量可以到达的位置:
当列表存在时,代理仅转发到列表中的主机名、绑定到为请求进程注册的机密的主机,以及 bypassHosts,因此现有的 --allow-host 绑定可以继续工作,而无需将其主机重复列出。发往任何其他主机的请求或 CONNECT 隧道都会被拒绝,并返回 Host "<host>" is not in the secret egress proxy traffic allowlist. Add it to secrets.egressProxy.allowedHosts or bind a store secret to it with: openclaw secrets store set <NAME> --allow-host <host>, then restart the Gateway.
空数组是锁定模式:只有按机密绑定的主机和 bypassHosts 仍可访问。省略 allowedHosts 会使流量不受限制。主机名遵循与机密绑定相同的规则:精确的小写 ASCII/Punycode 匹配,不支持通配符或端口。更改允许列表后,请重启 Gateway。
当前限制:
- 流量允许列表仅约束遵守代理环境(
HTTPS_PROXY和 CA 变量)的协作客户端。子进程可以忽略这些变量并打开原始套接字,因此允许列表是纵深防御;目标绑定的哨兵值仍是主要防御,因为它们能抵御代理绕过。 - 不支持 HTTP/2 上游连接;代理使用 HTTP/1.1 上游。
- WebSocket 升级支持在握手 URL 和请求头中进行机密替换。消息帧原样通过;WebSocket 消息内部的哨兵值不会被替换。
- 非 443 端口的 HTTPS 替换不是受支持的兼容性目标。
- 不支持身份范围的机密;只有团队存储参与。
- 允许主机策略仅进行精确主机名授权。它不会验证解析出的 IP,也不会阻止允许的来源反射凭据。
- 除直接代理请求到字面回环目标(
localhost、127.0.0.0/8或::1)外,明文 HTTP 会被拒绝。这些请求仍受代理认证、流量允许列表、审计和上传限制约束。DNS 应答不会使其他主机名符合此例外。携带机密哨兵值的回环 HTTP 请求仍会被拒绝;机密永远不会被替换到明文 HTTP 上。 - 自动共享存储机密出口仅适用于 Gateway 托管的 exec。沙箱和远程
nodeexec 既不会接收代理变量,也不会接收哨兵值,因此共享存储中的secret条目在那里不可用。提供商原生 harness 子进程也不使用此代理。下面的显式 Crabbox 命令会单独授予已配置的模型凭据。 - 后台子进程会保留其原始机密快照,直到退出或被停止。对存储凭据或目标绑定的更改需要新的运行和新的命令;停止现有命令可立即撤销其较早的授权。
Crabbox 命令的模型凭据¶
Crabbox 插件允许 Linux 租约中的前台应用程序使用保存在主机上的凭据调用 OpenAI 兼容 API。云代理已经在 Gateway 上保留自己的推理和提供商认证;此命令用于应用程序自身发起的 API 调用。
要求¶
在拥有已配置凭据的主机上运行该命令,并从拥有该租约的本地项目目录运行。使用一个独占拥有、由协调器支持的 Linux 租约,该租约具有已配置的 Crabbox 登录且没有活动的出口会话。Crabbox 二进制文件必须支持带 --upstream-proxy-env 的 egress run;该命令会在读取凭据之前检查支持情况。使用 --binary <path> 选择另一个二进制文件。
所选提供商必须在 models.providers.<provider>.apiKey 中使用 API 密钥 SecretRef。文件、环境变量、exec 和共享存储 SecretRef 会使用现有解析器,而不会将凭据复制到另一个存储。模型必须解析到 openai-responses 或 openai-completions 路由,并且具有 443 端口上的 HTTPS 端点。端点 URL 不能包含凭据、查询或片段。Auth-profile 和 OAuth 凭据、自定义请求头、请求代理/TLS 覆盖、禁用的认证请求头以及本地服务配置均不受支持。
准备并运行¶
首先通过正常的 Crabbox 工作流同步文件、填充工作区并安装依赖项。模型命令会传递 --no-sync --no-hydrate,因此它使用已准备好的工作区,并且无法通过其仅限模型主机的桥接获取依赖项。为准备和执行保留相同的本地项目目录:--id 选择租约,但不会覆盖 Crabbox 的仓库声明或工作区选择。
对于现有租约和已配置的 OpenAI SecretRef,此示例会检查 curl 是否可用,然后在不读取密钥的情况下发起 Responses API 请求:
cd ~/path/to/project
crabbox run --id <lease-id> -- curl --version
openclaw crabbox run --id <lease-id> --model openai/gpt-5.6-sol -- sh -c '
curl --fail-with-body --silent --show-error "${OPENAI_BASE_URL%/}/responses" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
--data "{\"model\":\"$OPENAI_MODEL\",\"input\":\"Reply with OK.\"}"
'
成功时返回提供商的响应 JSON。将 sh -c ... 替换为你的应用命令,例如 node test-app.js。使用与已配置路由匹配的端点;仅支持 Completions 的提供商需要调用其对应的 API。--provider <backend> 选择 Crabbox 后端,而 --model 选择模型提供商。--timeout <seconds> 同时限制设置和执行(1–86400 秒,默认 600)。取消和超时会立即撤销凭据访问,并给 Crabbox 75 秒进行优雅清理。
OpenClaw 会解析所选模型已配置的或提供商拥有的 API 端点,并启动一个隔离的 secret 代理。Crabbox 原生的 egress run 负责前台桥接、远程命令和会话清理。OpenClaw 提供 OPENAI_API_KEY 作为不透明哨兵值、OPENAI_BASE_URL 和 OPENAI_MODEL,以及 HTTP 代理设置和临时公共 CA 证书包。API 密钥、上游代理身份验证和 CA 私钥保留在主机上。桥接仅允许所选主机名;这不会阻止程序打开直接套接字。模型选择会设置应用的默认环境,而不是限制提供商凭据的 API 操作或模型。
应用的 HTTP 客户端必须同时遵守代理和 CA 设置。curl 和 Python 默认的 urllib.request opener 会使用注入的环境。对于 Node.js,请使用支持 NODE_USE_ENV_PROXY 的运行时(例如 Node.js 24+),并配合 NODE_EXTRA_CA_CERTS。自定义 Node dispatcher、Python opener 或 SDK 客户端可能会覆盖这些默认值;如有需要,请显式配置其代理和信任设置。禁用证书验证或忽略代理并不能建立受保护的模型访问。
生命周期与恢复¶
取消和超时会立即撤销凭据使用,先于命令清理完成。完成时会关闭授权和桥接,并停止匹配的租约侧 egress 客户端。租约和已准备好的工作区仍保持可用。在整个应用生命周期内保持前台命令运行;分离式应用在该命令退出后会失去模型访问。普通远程 exec、background 和 sandbox 命令不会获取此授权。
旧版二进制文件会被拒绝,并提示更新信息。active-egress 错误要求租约处于空闲状态,或停止你拥有的现有会话。repository-claim 错误表示你必须返回租约所属的本地项目目录。不要回收其他任务的租约来绕过任一检查。
如果清理无法确认结算完成,命令将失败。检查 crabbox egress status --id <lease-id>;如果失败的命令报告了会话 ID,请针对该会话重试 crabbox egress stop --id <lease-id> --session <egress-session-id>。在重用租约之前,也请确认远程工作负载已停止,或通过其正常所有者释放一次性租约。撤销并不能证明不可达的远程进程已经退出。
此独立命令不需要 secrets.egressProxy.enabled,也不会更改 Gateway 配置或重启 Gateway。在命令完成或取消后,启动新命令以获取新的授权;其旧的哨兵值和 CA 文件不是可重用的凭据。
基于文件的 API 密钥¶
不要在配置 env 块中放入 file:... 字符串。该块是字面量且不可覆盖,因此 file:... 在那里永远不会被解析。
改为在受支持的凭据字段上使用文件 SecretRef:
{
secrets: {
providers: {
xai_key_file: {
source: "file",
path: "~/.openclaw/secrets/xai-api-key.txt",
mode: "singleValue",
},
},
},
models: {
providers: {
xai: {
apiKey: { source: "file", provider: "xai_key_file", id: "value" },
},
},
},
}
对于 mode: "singleValue",SecretRef 的 id 为 "value"。对于 mode: "json",请使用绝对 JSON 指针,例如 "/providers/xai/apiKey"。
参见 SecretRef 凭据字段 了解接受 SecretRef 的字段。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw