跳转到主要内容
请先规划网关的网络。 登录时,如果 Claude 应用网关的主机名解析为公共 IP 地址,Claude Code 会拒绝连接,即使该地址无法从互联网访问也是如此。Claude 应用网关可以向用户的机器推送设置,包括运行 shell 命令的 hook。此检查有助于防止用户意外登录到公共互联网上的恶意网关。也请不要让您自己的网关暴露在互联网上。在选择网关的运行位置之前,请先选择网关的地址。通常这是一个私有地址,用户可以在内部网络上或通过 VPN 访问。如果您的内部网络使用公共 IPv4 地址段,您可以列出一个同时包含网关和用户机器的地址段。Claude Code 会将该匹配视为网关位于您内部网络上的标志。请参阅 为网关选择地址。如果以上两种方式都不适合您的网络,请联系您的 Anthropic 客户团队。
本页面涵盖运行 Claude 应用网关 的运维方面:在身份提供商 (IdP) 中注册 OAuth 客户端、将网关部署为容器,以及日常运行。关于网关在启动时读取的 gateway.yaml 文件中的每个选项,请参阅 配置参考。 生产部署按顺序遵循四个步骤,下面的部分与之相对应。前两个是您做出选择的地方;后两个是在运行后参考的材料。
  1. 设置身份提供商:注册 OAuth 客户端并查看 Okta、Entra 和 Google 的特定 IdP 说明
  2. 部署网关:构建固定版本的容器镜像并在 Kubernetes、Cloud Run 或您自己的平台上运行它。本部分还涵盖成本、绕过、多网关和无服务器决策
  3. 设置运维:日志、健康探针、中断行为、密钥轮换和升级。当您连接监控和运行手册时参考的材料
  4. 审查安全态势:数据流向何处、威胁模型和合规性答案。用于安全审查的参考
如果在此过程中登录或启动失败,请直接转到 故障排除,该部分按您看到的错误进行索引。

身份提供商设置

向身份提供商注册一个机密 OAuth/OpenID Connect (OIDC) Web 应用程序,使用单个重定向 URI https://<gateway>/oauth/callback,并将其分配给应该有网关访问权限的用户或组。网关使用该注册的客户端密钥向 IdP 进行身份验证;如果您的 IdP 改用证书凭据,则使用您上传到该注册的证书进行身份验证。 任何符合 OIDC 的 IdP 都可以工作:Okta、Microsoft Entra ID、Google Workspace、Keycloak、Dex、PingFederate 等。IdP 必须满足三个要求:
  • 在生产环境中通过 HTTPS 提供 /.well-known/openid-configuration;网关接受 http:// 发行者,本地环回发行者另外需要 CLAUDE_GATEWAY_ALLOW_LOOPBACK=1
  • 支持授权代码流。PKCE(代码交换证明密钥)默认启用;对于不支持它的 IdP,使用 oidc.use_pkce: false 禁用它
  • 在 id_token 中返回 email 和可选的 groups,或使用 oidc.userinfo_fallback: true 从 userinfo 端点提供它们
对于私有 PKI,设置 oidc.ca_cert_pem。 一些提供商处理电子邮件和组声明的方式不同:
  • Okta:位于 https://example.okta.com 的组织授权服务器返回一个省略 email 和 groups 的简化 id_token,因此当您将其用作 issuer 时设置 oidc.userinfo_fallback: true。包含 id_token 中 email 和可选 groups 的自定义授权服务器(如 https://example.okta.com/oauth2/default)直接发出它们,不需要回退。Okta 仅在 oidc.scopes 中请求 groups 作用域且应用的组声明过滤器允许时才发出 groups;userinfo_fallback 无法填充 IdP 未被要求的声明。
  • Microsoft Entra ID:issuer = https://login.microsoftonline.com/<tenant-id>/v2.0。Entra 发出组对象 ID 而不是名称,因此在 managed.policies.match.groups 中使用 GUID,或使用应用角色获得人类可读的名称。如果您的租户在 roles 而不是 groups 下发出角色,设置 oidc.groups_claim: roles。
  • Google Workspace:issuer = https://accounts.google.com。Google 的 id_token 不包含组。要在 Google 作为 IdP 时使用基于组的 allowed_groups 或 managed.policies,配置 oidc.google_groups,它使用具有域范围委派的服务账户通过 Admin SDK Directory API 查找每个用户的组。没有它,使用 oidc.allowed_email_domains 进行成员资格门控,使用 managed.policies.match.email_domain 进行策略分配。Google 也忽略标准 offline_access 作用域。对于刷新令牌,设置 oidc.scopes: [openid, profile, email] 和 oidc.extra_auth_params: { access_type: offline, prompt: consent }。
刷新令牌让网关可以在不将开发者发送回浏览器的情况下无声地续订开发者的会话。它们也驱动取消配置,因为当 IdP 禁用用户时,下一次刷新失败,会话在 ttl_hours 内结束。网关默认请求 offline_access 以获取刷新令牌。如果您的 IdP 需要明确同意离线访问,配置 OAuth 客户端以允许它。如果您的 IdP 根本无法发出刷新令牌,网关仍然可以工作,但没有无声续订,因此开发者在会话过期时重新运行浏览器登录。为了防止每小时都发生这种情况,将 session.ttl_hours 提高到 8 或 12。权衡是取消配置延迟,因为没有刷新令牌,禁用的用户在更长的 TTL 过期之前保持访问权限。

部署

网关是一个单一的无状态 Linux 二进制文件,通过 Postgres 进行协调,因此请按照您在环境中部署任何其他无状态服务的方式来部署它。将其保留在您的网络内部,使开发人员可以通过 HTTPS 访问它,并且它可以访问您的 IdP;并像对待任何持有生产凭据的服务一样对待它。 除了运行位置之外,还有几个决策会影响部署:
  • 成本:没有单独的许可证费用或按席位收费。网关是 claude 二进制文件的一部分,因此您通过现有的承诺用量支付推理费用,外加运行它所需的计算资源。
  • 绕过:网关并不强制要求访问模型的唯一途径必须经过它。拥有自己凭据的开发人员仍然可以直接调用提供商,因此关闭该路径属于网络策略决策,例如阻止除网关之外的出站流量访问 api.anthropic.com。阻止该出站流量也会破坏 WebFetch 域安全检查,该检查会从每位开发人员的机器调用 api.anthropic.com。在托管策略中设置 skipWebFetchPreflight: true 以禁用它。
  • 多个网关:每个网关都是一个独立的部署,拥有自己的配置,并且 CLI 按网关主机名存储信任和凭据,因此不同团队可以使用不同的网关而不会产生冲突。要服务多个 OIDC 颁发者,请运行单独的实例。
  • 无服务器:如果设置 min-instances: 1 以避免冷启动时的 OIDC 发现,Cloud Run 可以正常工作。Lambda 和 Cloud Functions 无法使用,因为网关是一个长时间运行的 HTTP 服务器。
此处的每种生产拓扑都在纯 HTTP 副本前面放置了一个 L7 代理,例如 Ingress、Cloud Run 的前端或 ALB。将 listen.trusted_proxies 设置为代理的源地址范围,以便网关从 X-Forwarded-For 读取客户端 IP。仅当 TCP 对端受信任时,网关才会采用该标头。Google Cloud 和 AWS 完整示例提供了每种拓扑的具体值。如果没有受信任的代理,每个请求看起来都来自代理的 IP,这会使按 IP 的速率限制合并为一个共享配额,并在审计事件中记录代理的 IP。 不要将请求重定向到网关的设备授权端点和令牌端点,例如在 ingress 处使用 HTTP 到 HTTPS 或主机名规范化的重写。Claude Code 不会跟随这些请求上的重定向,因此对这些请求进行重定向的 ingress 规则会导致登录和令牌刷新失败。 为代理设置任意长于网关保活间隔的空闲超时时间,该间隔取决于上游:
  • 在除 provider: anthropic 之外的所有上游上,一旦流静默约 15 秒,网关就会写入一个 SSE ping。
  • 在 provider: anthropic 上,网关会原样透传响应,包括 Anthropic API 自身的 ping。
诸如 ALB 默认的 60 秒这样的值足以保持安静的流处于打开状态。AWS 完整示例仍将其提高到一小时,其故障排除条目涵盖了早于 v2.1.229 的网关,这些网关在现在会收到 ping 的上游上,于安静期间不发送任何内容。

为网关选择地址

Claude Code 通过以下两种方式之一接受网关的地址:
  • 私有地址:将网关置于内部负载均衡器或 VPN 之后,并使用仅解析为私有地址的主机名,例如 RFC 1918 或 CGNAT 100.64.0.0/10。用户的机器可以使用任何地址。私有网络前提条件列出了接受的地址范围。
  • 声明的地址块:如果您的内部网络使用您的组织拥有的公共 IPv4 地址空间,请在 gatewayInternalNetworks 托管设置中列出该地址块。网关和用户的机器都必须位于该地址块中。请参阅允许网关使用您拥有的公共地址空间。
如果没有单个地址块能同时包含两者,请改为给网关分配私有地址。

容器镜像

围绕标准 Claude Code 发行版中的原生 claude 二进制文件构建您自己的镜像:
  1. 从固定版本中下载适用于您镜像架构的 Linux 构建版本;下载 URL 请参阅安装特定版本。
  2. 按照二进制完整性和代码签名中的说明,根据该版本经过 GPG 签名的 manifest.json 对其进行验证。
  3. 将其复制到构建上下文中。
如果您的构建无法访问发布主机,请将该版本镜像到您的内部镜像仓库,并固定您的机群所运行的版本。 除了二进制文件之外,镜像还需要:
  • 基于 glibc 的镜像:glibc 构建版本唯一的动态依赖是 glibc 库。基于 musl 的镜像需要 linux-x64-musl 或 linux-arm64-musl 构建版本,其唯一的动态依赖是 musl。
  • 可写的状态目录:网关可以以任何用户身份运行,但精简镜像没有可写的主目录。将 CLAUDE_CONFIG_DIR 设置为可写路径,例如 /tmp/.claude。
  • 容器命令:claude gateway --config /etc/claude/gateway.yaml,以只读方式挂载配置文件,并以环境变量的形式提供密钥;网关在 listen.port 上监听,默认为 8080。

Kubernetes

像任何无状态服务一样,将网关作为 Deployment 运行:
  • 从 ConfigMap 挂载配置,从 Secret 挂载密钥;在 YAML 中通过 ${file:/path/to/secret} 或以环境变量的形式引用密钥
  • 在 Ingress 处终止 TLS,并将 listen.public_url 设置为 Ingress 主机名
  • 将就绪探针指向 GET /readyz,将存活探针指向 GET /healthz
有关 AWS 上的完整示例(涵盖 ECS Fargate 或 EKS、Amazon RDS 和 AWS Secrets Manager),请参阅在 AWS 上部署。 优先使用平台的工作负载身份而不是静态密钥;upstreams 参考提供了各平台的设置详情。对于跨云组合,例如在 GKE 上使用 Amazon Bedrock 上游,请改为在上游的 auth 块中设置显式凭据。

Cloud Run

按如下方式配置服务:
  • 将 listen.port 保留为默认值 8080(与 Cloud Run 的默认 PORT 一致),或设置 port: ${PORT}
  • 将 public_url 设置为外部可访问的源。对于生产环境,这通常是内部负载均衡器的主机名,因为 /login 会拒绝公共地址,而 *.run.app URL 会解析为公共地址,因此仅使用 Cloud Run URL 只适用于 curl 或浏览器冒烟测试。例外情况是 *.run.app 通过 Private Service Connect 和 Cloud DNS 私有区域解析为私有地址的网络;在该拓扑中,Cloud Run URL 是有效的 public_url。Google Cloud 完整示例涵盖了这两种情况。
  • 将配置作为密钥卷挂载
  • 设置 min-instances: 1,以避免在首次请求时进行冷启动的 OIDC 发现
有关 Google Cloud 上的完整示例(涵盖 Cloud Run 或 GKE、Cloud SQL 和 Secret Manager),请参阅在 Google Cloud 上部署。

将网关 URL 推送到开发人员机器

网关开始提供服务后,通过托管设置将 forceLoginMethod、forceLoginGatewayUrl 和 parentSettingsBehavior: "merge" 推送到每位开发人员的机器上,可以通过 MDM 或直接写入各操作系统对应的 managed-settings.json 来完成。 部署这些设置项后,Claude Code 将停止使用机器上残留的 API 密钥或 claude.ai 登录,因此请将推送与您的登录说明一起规划。Administrator policy requires a Cloud gateway sign-in 描述了开发人员会看到的消息。 有关文件路径,请参阅每种机制存储策略的位置;有关 Claude Desktop 的等效设置 bootstrapUrl,请参阅客户端托管设置。

大规模推广

登录按客户端 IP 地址进行速率限制,默认值适合小型团队。每个地址每 10 分钟可发起 30 次登录和提交 10 次代码。面向数千名开发人员的推广可能在第一天早上就达到这些限制,原因有以下两种:
  • 网关无法看到负载均衡器之后的地址。 如果没有 listen.trusted_proxies,每位开发人员看起来都来自负载均衡器的地址,并共享一个限制。网关首次忽略 X-Forwarded-For 标头时会记录一条警告。如果负载均衡器应当列入 listen.trusted_proxies,请在更改任何其他内容之前先将其列入。如果开发人员仍然共享一个地址,请提高 rate_limits。
  • 许多开发人员共享少量 NAT 或 VPN 出口地址。 即使 trusted_proxies 设置正确,他们也会共享这些地址的限制。请相应提高 rate_limits。
要确定 max 的大小,请用开发人员数量除以他们共享的出口地址数量。估算其中有多少人会在一个 window_seconds 周期(默认为 10 分钟)内登录。然后将其翻倍,以覆盖重试以及同时登录 Claude Code 和 Claude Desktop 的开发人员。 例如,位于 4 个出口地址之后的 10,000 名开发人员在一小时内均匀登录。即每个地址 2,500 名开发人员,每 10 分钟约有 420 人登录,将其翻倍并向上取整为 1,000。下面的示例将两个限制都设置为 1,000:
device_verify 用于防止有人猜测其他开发人员的登录代码,因此只需将其提高到您的估算所需的程度。即使在这些限制下,代码也是由 20 个字符的字母表组成的 8 个字符,并在 10 分钟后过期,因此猜测仍然不切实际;请参阅用户代码防暴力破解。 当您的 IdP 颁发刷新令牌时,Claude Code 会静默续订会话,因此您可以在推广完成后恢复限制。如果没有刷新令牌,开发人员每隔 session.ttl_hours 就需要重新登录。请同样按照该稳定速率确定两个限制的大小,并保持提高后的值。 达到限制时,Claude Code v2.1.274 或更高版本会显示 The gateway is limiting sign-in attempts right now。v2.1.274 或更高版本的网关会在验证页面上显示 Too many attempts came from your network address,并列出需要检查的设置。它还会写入一条 sign-in refused 日志,指明需要更改的设置。

运维

一旦网关开始提供流量,日常运维就是读取其日志、探测其健康状况,以及按您的计划轮换其密钥。下面的小节涵盖每一个,加上 Postgres 持有的内容以及升级和回滚的行为。

日志

网关向 stderr 写入两个流,都是 JSON 友好的:
  • 审计事件:每个安全相关事件一行 JSON。将 stderr 管道传输到您的日志聚合器。 发出的事件包括 config.load、session.mint、session.refresh、device.authorize、device.verify、device.callback、auth.denied、access.denied、access.public_client、inference、managed.serve、desktop_bootstrap.serve、desktop_bootstrap.denied、spend.blocked、admin.denied、admin.limit.upsert 和 admin.limit.delete。字段因事件而异:
    • 成功的 mint 和 refresh 事件携带 sub、email、client_ip 和结果
    • auth.denied 和 access.denied 携带原因和客户端 IP,加上 auth.denied 的请求路径,因为在这些拒绝时不存在用户身份。两个 access.denied 原因改变事件携带的内容:
      • xff_unparseable:事件也携带无法读取的 X-Forwarded-For 条目
      • client_ip_unknown:事件不携带客户端 IP,因为连接没有对等地址,而设置了 access_control 列表
    • access.public_client 携带每个进程从公共地址到达的第一个请求的客户端 IP,而 access_control.allow_cidrs 为空。网关照常提供请求;该事件表示网关可能可从公共互联网访问。请参阅 access_control 参考 了解什么被视为公共以及推荐的允许列表。
    • inference 记录哪个上游提供了请求以及响应状态
    • desktop_bootstrap.denied 记录被拒绝的 Claude Desktop bootstrap 获取,包含原因(not_configured、policy_not_opted_in 或 no_policy_matched)和用户的身份
    • guardrail.trace 记录触发了哪个过滤器,不包含匹配的文本
    • admin.denied 记录被拒绝的管理员 API 身份验证尝试,包括客户端 IP、方法、路径和原因,不包括呈现的密钥材料:当呈现了 x-api-key 但与配置的密钥不匹配时为 invalid_key,当仅呈现了 Authorization 标头且其未验证为 admin.admin_groups 中的网关会话时为 bearer_rejected,或当两个标头都未呈现时为 no_credentials
  • 运维日志:人类可读的 [gateway] 前缀行,用于启动、警告和上游错误。CLAUDE_GATEWAY_LOG_LEVEL 环境变量控制详细程度,接受 debug、info、warn 或 error,默认为 info。在 debug 时,每次登录和刷新也记录 id_token 中声明的名称(不是值),加上当 userinfo_fallback 提供任何时 userinfo 声明的名称,因此您可以诊断 email_claim 和 groups_claim 设置而不记录 PII。它不影响审计事件,这些事件总是被发出。

以 JSON 格式写入运维日志

如果您的日志收集器从 JSON 字段中读取每一行的级别,请在网关的环境中设置 CLAUDE_GATEWAY_LOG_FORMAT=json。需要网关服务器上的 Claude Code v2.1.319 或更高版本。 随后每条运维日志行都是一个包含 severity、time 和 message 的对象:
severity 是大写的日志级别,其中 warn 写作 WARNING。启动失败会产生一条 ERROR 行。 审计事件同样写入 stderr,不会改变。它们携带 ts 和 evt,不带 severity。

健康

网关提供 GET /healthz 作为活跃探针和 GET /readyz 作为就绪探针。/readyz 验证存储是否可达。如果您设置了 store.readiness_grace_seconds,/readyz 在存储停止应答后最多继续报告就绪达到那么多秒。 两个端点都免除 access_control.allow_cidrs,因此探针在锁定的监听器上继续工作。 /.well-known/oauth-authorization-server 处的 OAuth 发现文档也仅在配置加载、OIDC 发现、上游客户端构造和 Postgres 迁移全部成功后才返回 200,因此它也充当端到端启动检查。

并发上游请求

默认情况下,每个网关副本最多同时向上游发送 256 个请求。流式响应在流结束前计入限制。 当副本达到限制时到达的请求在网关内等待一个空闲槽位。开发者看到一个响应缓慢启动或似乎挂起。在 provider: anthropic 上游上,等待时间超过 timeouts.upstream_ttfb_ms 的请求放弃该上游,当没有后续上游提供它时以 502 失败。 包含 upstream requests: 的启动日志行显示生效的限制。当副本的打开请求数超过限制时,它也最多每分钟记录一次包含 client requests are open 的警告。 要同时提供更多请求,您有两个选项:
  • 添加副本。
  • 提高每个副本上的限制。在网关容器上设置 BUN_CONFIG_MAX_HTTP_REQUESTS 环境变量为 1 到 65535 之间的整数,然后重启容器。
副本以约限制除以请求保持打开的平均秒数的请求速率填充其限制。例如,如果请求平均保持打开 10 秒,默认限制为 256 的副本以约每秒 26 个请求的速率填充它。 如果您在 CPU 上自动扩展,限制处的副本排队请求而不触发扩展,因此将目标设置在副本在记录 client requests are open 警告时显示的 CPU 级别以下。
每个打开的请求在网关进程中持有内存,同时它流式传输,同时它等待一个槽位。如果您将限制保持在 256,过载副本上的内存仍然增长,因为等待请求保持其请求体。根据峰值时打开的请求数调整容器的内存,当您更改限制时观察内存。耗尽内存的副本被杀死并丢弃它持有的每个流。
在使用 load_test_mode 的负载测试中,请求不会在此限制处等待,但副本仍会记录 client requests are open 警告。

使 API_TIMEOUT_MS 高于网关的首字节时间

如果您降低 API_TIMEOUT_MS 以便更快地重试未得到应答的请求,请使其高于网关发送流式响应首批字节所需的时间。等待时间更长的请求会超时,同样等待那么久的每次重试也会超时,直到该轮次以 Request timed out 结束,除非首字节截止时间先将其结束。
  • provider: anthropic:将 API_TIMEOUT_MS 设置为高于您的上游发送其第一个事件所需的最长时间。
  • 其他所有上游:将其设置为比您的上游返回响应标头所需的最长时间多出 20 秒以上。网关在这些标头之后约 15 秒发送其第一个 keepalive ping,除非上游的第一个事件更早到达。早于 v2.1.229 的网关不发送 ping,因此在那里请使用 provider: anthropic 规则。
在此时间之上,还要考虑任何等待空闲槽位的时间,以及在故障转移之前等待已宕机上游的时间。同时也要使该值高于最长的非流式请求。

使用首字节截止时间更快地重试未得到应答的请求

要让 Claude Code 更快地重试未得到应答的流式请求,请在开发者的机器上设置 CLAUDE_STREAM_FIRST_BYTE_TIMEOUT_MS,为其设定首字节截止时间。该截止时间限制首次尝试等待响应标头的时长。没有它,该尝试可能等待 5 到 6 分钟。需要开发者机器上的 Claude Code v2.1.307 或更高版本。 将该变量设置为以毫秒为单位的正整数,可以在网关策略的 env 块中或开发者的 shell 中设置。请使该值高于网关最长的首字节时间,否则 Claude Code 可能会中止并重新发送标头返回较慢的健康请求。此示例在开发者的 shell 中设置 60 秒:
未得到应答的重新发送可能等待 5 到 6 分钟,直到响应体空闲超时将其结束,然后 Claude Code 在重试预算内重试它。如果您降低 API_TIMEOUT_MS 只是为了更快地重试,请移除该值,否则一次未得到应答的重新发送就可能以 No response from API 结束该轮次。 要确认截止时间已触发,请让开发者在启用调试日志的情况下启动 Claude Code,然后在日志中搜索 [first-byte] no response headers。

中断行为

如果 Postgres 宕机,网关本身继续为已登录的开发者提供服务,新登录失败。开发者是否真的继续工作取决于您的编排器如何处理就绪:
  • 现有会话:持有者令牌使用 JWT 密钥在本地验证,会话刷新不接触存储,网关进程仍然可以提供推理
  • 新登录:失败直到 Postgres 恢复,因为设备流及其速率限制计数器存在于 Postgres 中
  • 支出限制执行:在中断期间默认失败开放,因此推理仍然流动;如果您宁愿阻止而不是无计量运行,将其翻转为失败关闭
  • 就绪:默认情况下,/readyz 在 Postgres 无法访问时立即报告未就绪,因此每个副本一次失败其就绪检查。在流量仅到达通过检查的副本的地方,所有流量,包括网关仍然可以提供的推理,在 Postgres 恢复前失败。/healthz 上的活跃探针在整个过程中继续通过。
如果您的 IdP 宕机,现有会话工作直到 ttl_hours,新登录失败。会话刷新获得重试答案并在 IdP 恢复后成功。如果您的 IdP 有频繁的维护窗口,设置更长的 ttl_hours。

就绪宽限期

要让已登录的开发者在短 Postgres 中断(如数据库故障转移)期间继续工作,将 store.readiness_grace_seconds 设置为比故障转移花费的时间更长,例如 300。启用支出限制并使用默认失败开放行为,通过保持就绪的副本的请求在 Postgres 恢复前无计量,因此将值保持为低至覆盖您的故障转移。如果您设置了 enforcement.fail_closed_on_error: true,网关拒绝已登录开发者的推理,带有 429 spend limit unavailable 消息,直到 Postgres 恢复,即使副本仍然通过其就绪检查。 该设置需要网关服务器上的 Claude Code v2.1.282 或更高版本。较早的网关在找到密钥时拒绝启动,因此在添加它之前升级每个副本。升级 涵盖回滚。 如果您将就绪探针指向 /healthz 而不是,副本也在中断期间继续通过它,但 /healthz 永远不报告未就绪,因此 Postgres 连接未恢复的副本继续通过。

JWT 密钥轮换

通过三个步骤轮换签名密钥,以便现有会话保持有效:
  1. 生成一个新密钥。将其前置到 session.jwt_secret 数组。
  2. 滚动部署。新令牌使用新密钥签名;旧令牌仍然验证。
  3. 在 ttl_hours 加上一个边距之后,移除旧密钥并再次滚动。
轮换也是在过期前强制会话退出的唯一方式:持有者令牌针对 JWT 密钥在本地验证,因此没有按会话撤销。直接替换密钥,不在数组中保留旧密钥,一次使每个未完成的会话失效。对于个人离职,在您的 IdP 中取消配置用户;他们的会话在 ttl_hours 内结束。

Postgres

网关将其状态存储在 PostgreSQL 数据库中:
  • 数据库:PostgreSQL 本身,自托管或托管均可,版本为最低版本或更高。仅实现 Postgres 协议的数据库(例如分布式 SQL 数据库)不受支持。
  • 地址:store.postgres_url 接受一个主机。如果数据库有多个节点,请使用位于它们前面的地址,例如您的托管服务的端点、负载均衡器或虚拟 IP。设置一个比故障转移所需时间更长的就绪宽限期。
网关持有五个数据表加上一个 _migrations 表,全部由其启动时迁移创建: 一个 30 秒的循环过期 kv 行超过其 TTL,一个每小时的扫描在支出表上强制保留窗口,因此没有什么无限增长。没有 支出限制 配置,只有 kv 被写入。网关在启动时应用其自己的架构迁移,在每次升级时也是如此,因此其数据库角色需要创建和修改表的权限。将其指向专用于网关的数据库或架构,以保持该授权范围狭窄。 使用支出限制,丢失的数据库意味着丢失支出跟踪和上限,不仅仅是开发者重新登录,因此运行定期备份。要立即删除一个离职的开发者而不是等待保留,直接运行 DELETE FROM principal_emails WHERE principal = '<sub>';这移除了唯一持有其电子邮件、名称和组的表。spend 和 admin_audit 行仅引用伪匿名 OIDC sub。

确定连接池大小

每个副本最多打开 store.max_connections 个 Postgres 连接,默认为五个。请使副本数 × store.max_connections 不超过数据库为网关剩余的连接数:
  • 副本:统计同时运行的最大数量。从您的自动扩展最大值开始,如果您不使用自动扩展,则从副本数开始,再加上滚动更新在停止旧副本之前启动的额外副本。在使用默认设置的 Amazon ECS 上以及在 Cloud Run 上,额外副本可能与原数量一样多。
  • 剩余连接:取数据库的 max_connections,然后减去数据库保留的连接以及其他客户端持有的连接。PostgreSQL 保留 superuser_reserved_connections,默认为三个。托管服务可能保留更多,因此请查看其文档。
例如,假设自动扩展最大值为六,滚动更新最多启动两个额外副本,并使用默认的五个连接池。这需要 (6 + 2) × 5 = 40 个连接。一个 max_connections 为 50 且其他客户端持有两个连接的数据库剩余 50 − 3 − 2 = 45 个连接,因此总数可以容纳。 如果您的总数无法容纳,请更改以下其中一项:
  • 数据库:提高其 max_connections
  • 副本:运行更少的副本,或让您的编排器在滚动更新中启动更少的额外副本
  • 连接池:降低 store.max_connections,但在启用支出限制时,请使其足够大以满足每个副本的负载。否则支出检查可能在等待连接时超时,网关会记录 spend check failed: spend precheck timed out。

升级

副本是无状态的,因此滚动重启不会丢失任何网关状态。网关在启动时运行架构迁移,这意味着部署新二进制文件会自动迁移数据库。并发副本在 Postgres 咨询锁上序列化,因此只有一个应用每个迁移。 当您的编排器使用 SIGTERM 停止副本时,如在滚动重启或缩减中,网关停止接受新连接,让已在途中的请求和流完成后再退出。它最多等待 25 秒,称为排空窗口,然后关闭仍然打开的任何东西。SIGINT(如终端中的 Ctrl+C)启动相同的排空,排空期间的第二个信号关闭打开的请求并立即退出。排空需要网关 v2.1.274 或更高版本。 长生成可以流式传输数分钟。在 Kubernetes 和 Amazon ECS 上,将这两个一起提高以给这些流更多时间:
  • 排空窗口:在网关容器上设置 CLAUDE_GATEWAY_DRAIN_TIMEOUT_MS 环境变量为正整数毫秒数,如 120000。网关忽略任何其他形式的值,如 120s,并保持 25 秒的默认值
  • 您的编排器的宽限期:Kubernetes 上的 terminationGracePeriodSeconds,或 Amazon ECS 上的 stopTimeout
宽限期在两个平台上默认为 30 秒。将其保持至少比排空窗口长 5 秒,否则编排器在排空完成前杀死网关。在 Kubernetes 上,也添加任何 preStop 钩子的持续时间,因为宽限期在钩子运行之前而不是网关接收 SIGTERM 时开始计数。 您的平台也可能限制排空可以运行多长时间:
  • Amazon ECS on Fargate:stopTimeout 最多允许 120 秒
  • Cloud Run:在 SIGTERM 后 10 秒停止实例,因此打开的流在那里最多获得 10 秒,无论排空窗口是什么
当排空窗口结束时仍有请求打开,网关记录一个包含 drain window over after 的警告,计数它切断的请求,并命名两个要提高的设置。 迁移是仅追加的,因此回滚到知道较少迁移的先前二进制文件是安全的;它忽略额外的行。回滚也重新验证 YAML 针对较旧二进制文件的架构,因此采用由较新版本引入的密钥的配置在较旧版本上启动失败。在回滚前移除新密钥。
仅从上游中移除 groups 键会使其对每个开发者开放。请参阅回滚前需要移除的内容。
因为您在自己的镜像中固定网关的版本,新 Claude Code 版本中的修复,包括安全修复,仅在您更新固定并重新部署时才到达您的部署。在您用于持有生产凭证的其他服务的相同修补周期中包括网关。

安全

本部分回答安全审查提出的问题:什么数据流经网关以及它去哪里,设计防御哪些攻击,以及哪些答案属于合规性问卷。

数据流

威胁模型摘要

网关位于您的网络周边内,但个别开发者笔记本电脑不被视为受信任。设计通过三种方式考虑这一点:
  • 开发者持有短期 JWT 而不是原始上游密钥。CLI 到网关的腿使用 RFC 8628 设备授权,网关与 IdP 的授权代码交换在默认配置中运行 PKCE,因此拦截的 IdP 授权代码是无用的。
  • 设备验证页面强制执行同源 POST 和每个 RFC 8628 §5.1 的每 IP 速率限制。请参阅 用户代码暴力破解抵抗。
  • 网关对您的 IdP、您的 OTLP 收集器和 provider: anthropic 上游的请求通过服务器端请求伪造 (SSRF) 防护,解析 DNS,阻止链接本地和云元数据地址加上默认的本地环回,并将连接固定到解析的 IP,因此操作员影响的 URL 无法重定向到云元数据端点。RFC 1918 私有范围被故意允许,因为 IdP 和 OTLP 收集器通常存在于私有 IP 上。对于其他提供商,网关在加载配置时拒绝命名这些地址或元数据主机名之一的 base_url,然后提供商的 SDK 连接而不进行 DNS 检查。 如果您打开 仅代理出口,该地址检查会移到您的转发代理:网关交付主机名,代理的允许列表必须拒绝这些目的地。 仅当网关必须合法到达的某些内容存在于本地环回时(例如本地开发 IdP 或 localhost 上的 sidecar OTLP 收集器),在网关的环境中设置 CLAUDE_GATEWAY_ALLOW_LOOPBACK=1。该变量为每个操作员配置的 URL 放宽本地环回块,也跳过启动时警告,该警告检查 pod 是否可以到达云元数据端点,因此更倾向于为收集器提供其自己的内部地址。
如果您添加自己的出口控制,网关必须在使用实例元数据凭证(如工作负载身份)时到达元数据服务器。 两个威胁超出范围,因为它们是您的基础设施来保护:
  • 受损的网关主机:主机既持有上游凭证,又向每个连接的开发者分发 托管设置,因此对网关配置的控制与对您的 MDM 的控制相当。CLI 的 批准对话框 用于 shell 能力设置限制无声更改,但不替代主机安全。
  • 恶意 OIDC 提供商:提供商签署网关信任的 id_tokens,因此它可以声称任何身份。审查和保护您的 IdP 是您的责任。

用户代码暴力破解抵抗

开发者在 /device 验证页面中输入的 user_code 是从 20 字符字母表中抽取的 8 个字符,产生 20⁸ 或约 2.56×10¹⁰ 个组合,在 10 分钟后过期。 网关在设备授权端点上应用按 IP 速率限制,可通过 rate_limits 配置。如果许多开发者从单个共享公司 NAT 地址登录,提高限制。大规模推出 显示如何调整它们的大小。限制仅适用于登录流,不适用于推理。

合规性态势

  • 数据驻留:网关自己的数据平面除非 Anthropic API 是配置的上游,否则不向 Anthropic 发送任何内容;当它是时,您现有的数据处理协议适用于推理路径。遥测、审计、身份和设置仅去往您配置的目的地。
  • 主机进程流量:主机进程是 Claude Code CLI。claude gateway 在与 Amazon Bedrock 和 Google Cloud 的 Agent Platform 部署相同的第三方规则下运行,不向 Anthropic 发送任何内容。在 v2.1.227 之前,主机进程发送启动遥测,例如产品版本和平台,在容器环境中设置 CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 关闭。这些版本也在启动时发送一个 HEAD 请求,没有正文或凭证,到 https://api.anthropic.com 上的 /api/hello,或在环境设置时的 ANTHROPIC_BASE_URL 上,除非环境也设置了代理变量(如 HTTPS_PROXY)或 mTLS 客户端证书。它们忽略了响应,因此在出口防火墙处阻止该请求不影响网关。
  • 客户端分析:CLI 在登录到网关时禁用自己的使用分析和错误报告。在第一次登录之前,CLI 仍然向 Anthropic 发送启动事件,包括在托管设置强制网关登录的机器上。要保持这些关闭,在强制网关登录的相同 客户端托管设置 中传递 DISABLE_TELEMETRY。
  • 错误报告:每当 CLI 的模型请求去往 Anthropic 的第一方 API 以外的任何端点(如 Amazon Bedrock 或自定义 ANTHROPIC_BASE_URL)时,CLI 关闭错误报告。
  • 客户端机器:开发者的 CLI 仍然向 Anthropic 发送 WebFetch 主机名检查和版本检查,除非设置了 CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 和 skipWebFetchPreflight: true。插件市场请求 有各自的关闭开关。请参阅 数据使用。
  • 调查评分:在登录到网关时,CLI 禁用 Anthropic 绑定的评分上传以及分析流,因此它不向 Anthropic 发送评分。
  • 成绩单共享:在调查的成绩单共享提示上选择”是”会在 ~/.claude/feedback-bundles/ 下写入本地文件,而不是上传到 Anthropic。
  • 客户端更新:更新检查与网关流量分开。通过您自己的分发固定版本,如果笔记本电脑不得获取版本,设置 DISABLE_UPDATES。DISABLE_AUTOUPDATER 仅停止后台更新,而 claude update 仍然有效。
  • TLS:在生产中通过 HTTPS 提供 public_url,要么从网关自己的监听器通过 listen.tls,要么从 TLS 终止入口在普通 HTTP 副本前面,在两种情况下都设置 listen.public_url。网关不拒绝普通 HTTP。IdP 必须在生产中提供 HTTPS,Postgres 支持 ?sslmode=require。在您的入口处设置 Strict-Transport-Security。
  • 漏洞披露:遵循 报告安全问题

插件市场请求

Claude Code 直接从每位开发者的机器获取插件市场,而不是通过网关。网络访问要求 列出了相关主机。 开发者首次启动交互式终端会话时,Claude Code 会注册官方市场 claude-plugins-official。它从 downloads.claude.ai 下载目录,如果失败,则从 github.com 克隆。哪些市场和插件会自动更新 介绍了后续刷新。 CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 不会阻止首次注册。以下任一托管设置可以阻止: 首次注册可能在开发者登录网关之前运行,此时尚未收到任何网关策略。为覆盖首次启动,请在 客户端托管设置 以及网关策略的 cli 或 code 块 中同时传递您的选择。

故障排除

如有问题和反馈,请使用 Claude Code 支持,或在 Claude Code GitHub 仓库上提交问题。您也可以联系您的 Anthropic 客户团队。报告问题时,请包括:
  • 网关问题:相关时间窗口内网关的 stderr、您的 gateway.yaml(已隐藏密钥)、网关版本(显示在 / 的登陆页面和 /managed/settings 的 x-cc-gateway-version 响应头中),以及最近的更改
  • 登录问题:开发者运行 claude --debug-file ./claude-debug.txt、重现问题,然后发送该文件以及同一时间窗口内网关的审计日志
  • 推理问题:请求的模型、配置的上游服务,以及该请求的网关审计日志,其中记录了由哪个上游提供服务以及响应状态
网关的 stderr 包含审计事件流,审计日志记录开发者身份,调试文件记录来自开发者机器的 hook 和 MCP 服务器输出。在发布到公开问题前,请审查并隐藏这些内容。 Cloud gateway sign-in was not completed 消息会命名网关主机名。当 Claude Code 同时拥有固定指纹和呈现的指纹时,消息还显示每个的前 16 个字符。 如果 Claude Code 在网关登录后报告 couldn't load your organization's managed settings,Claude Code 会命名原因、就地重启并恢复对话。如果 Claude Code 无法重启,例如在后台会话中,Claude Code 会结束会话并保留登录。

登录后请求头过大

当开发者属于许多 IdP 组时,其请求可能在登录后失败并返回 431 错误。 当请求头总大小超过 256 KiB,或者在您设置了 limits.max_request_header_bytes 时超过该值,网关会返回 431。对于这些请求,网关不会写入任何日志行或审计事件。v2.1.284 之前的网关版本在超过 16 KiB 时返回 431。 从以下各项中第一个适用于您网关的情况开始处理:
  • 网关版本早于 v2.1.284:升级网关
  • 设置了 limits.max_request_header_bytes:提高该值或删除该键
  • 以上均不适用,或之后仍出现 431:让您的 IdP 发出更少的组。身份提供者设置介绍了 Okta、Microsoft Entra ID 和 Google Workspace 如何提供组
精简组声明时,请保留您在以下设置中指定的组,这些设置决定开发者的访问权限、策略、上游和支出上限:

长时间思考导致代理超时

如果您的上游是 provider: anthropic,请改用 Anthropic API 上游上的长时间思考:在那里您同样需要提高 CLAUDE_STREAM_IDLE_TIMEOUT_MS,但时间和错误有所不同。 如果轮次在长时间思考后失败,且您的代理记录了一个保持打开约 12.5 分钟的请求,随后是一连串 504 错误,请在网关策略中提高 CLAUDE_STREAM_IDLE_TIMEOUT_MS。例如,如果代理的空闲超时时间为 240 秒,这一连串错误就是 10 个请求,每个都在 240 秒时被切断。
  • 第一个请求:一个流,它传送首个响应事件 message_start,然后在 Claude 思考期间仅发送 ping。在默认设置下,Claude Code 会在约 12.5 分钟后结束该流。
  • 其余 10 个:同一个请求,以非流式方式发送并随后重试,默认总共发送 10 次。它们不会收到 ping,因此较慢的回复会触发代理的超时。
此修复不需要更长的代理超时时间。如果您只提高代理的超时时间,一旦回复所需时间超过 Claude Code 自身的等待时间(默认 300 秒),非流式请求仍会失败。

确认原因

让开发者使用 claude --debug-file ./claude-debug.txt 重现该失败,然后在该文件中搜索以下行:
如果您的开发者已将 CLAUDE_CODE_DISABLE_NONSTREAMING_FALLBACK 设置为 1,您不会看到任何非流式请求,该轮次会以 API Error: Stream idle timeout - no chunks received 结束。同样的修复方法适用。

选择值

选择一个 CLAUDE_STREAM_IDLE_TIMEOUT_MS 值,使其第二列的时间能覆盖您最长的思考停顿;如果您不清楚,可以从 1800000 开始。流仅靠 ping 维持的时间可比该值长约 7.5 分钟: 如果上游挂起而网关持续发送 ping,您的开发者也需要等待同样长的时间,Claude Code 才会放弃该流。完全静默的连接仍会在 5 到 6 分钟后结束,因为响应体空闲超时不受此变量影响。

通过策略下发该值

在 gateway.yaml 中,将 env 条目添加到已向您要覆盖的开发者下发 Claude Code 设置的每个策略中。将其放在该策略已有的 code、cli 或 settings 键下。如需先试用该值,可以从匹配单个组的策略开始。有关策略如何匹配和继承,请参阅 managed。以下示例将该条目添加到键为 cli 的 match: {} 策略中:
使用 cli 键时,您可以覆盖终端、IDE 扩展和 Agent SDK 会话。对于 Claude Desktop 的 Code 标签页,请参阅选择 cli 或 code。 重启网关,使其读取编辑后的文件。然后让开发者重启 Claude Code 以获取 CLAUDE_STREAM_IDLE_TIMEOUT_MS。

Anthropic API 上游上的长时间思考

如果 provider: anthropic 是该模型唯一的上游,且您的代理记录了一个保持打开约 20 分钟的请求,随后是 502 或 504 错误,请在网关策略中提高 CLAUDE_STREAM_IDLE_TIMEOUT_MS。
  • 长时间请求:一个流,它传送 message_start,然后在 Claude 思考期间仅发送 ping。在默认设置下,Claude Code 会在约 20 分钟后结束它;提高该变量后,结束时间会相应推迟。
  • 错误:Claude Code 随后以非流式方式发送的请求。每个请求都会因网关返回的 502 而失败;如果代理的超时时间短于网关的等待时间,则会因代理的 504 而失败。该等待时间即 timeouts.upstream_ttfb_ms,此修复无需提高它。
要确认原因,请参阅确认原因。 选择时间能覆盖您最长思考停顿的值:默认值约为 20 分钟,900000 约为 30 分钟,1800000 约为 45 分钟。然后按照通过策略下发该值操作。