请先规划网关的网络。 登录时,如果 Claude 应用网关的主机名解析为公共 IP 地址,Claude Code 会拒绝连接,即使该地址无法从互联网访问也是如此。Claude 应用网关可以向用户的机器推送设置,包括运行 shell 命令的 hook。此检查有助于防止用户意外登录到公共互联网上的恶意网关。也请不要让您自己的网关暴露在互联网上。在选择网关的运行位置之前,请先选择网关的地址。通常这是一个私有地址,用户可以在内部网络上或通过 VPN 访问。如果您的内部网络使用公共 IPv4 地址段,您可以列出一个同时包含网关和用户机器的地址段。Claude Code 会将该匹配视为网关位于您内部网络上的标志。请参阅 为网关选择地址。如果以上两种方式都不适合您的网络,请联系您的 Anthropic 客户团队。
gateway.yaml 文件中的每个选项,请参阅 配置参考。
生产部署按顺序遵循四个步骤,下面的部分与之相对应。前两个是您做出选择的地方;后两个是在运行后参考的材料。
- 设置身份提供商:注册 OAuth 客户端并查看 Okta、Entra 和 Google 的特定 IdP 说明
- 部署网关:构建固定版本的容器镜像并在 Kubernetes、Cloud Run 或您自己的平台上运行它。本部分还涵盖成本、绕过、多网关和无服务器决策
- 设置运维:日志、健康探针、中断行为、密钥轮换和升级。当您连接监控和运行手册时参考的材料
- 审查安全态势:数据流向何处、威胁模型和合规性答案。用于安全审查的参考
身份提供商设置
向身份提供商注册一个机密 OAuth/OpenID Connect (OIDC) Web 应用程序,使用单个重定向 URIhttps://<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 端点提供它们
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 }。
部署
网关是一个单一的无状态 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 服务器。
listen.trusted_proxies 设置为代理的源地址范围,以便网关从 X-Forwarded-For 读取客户端 IP。仅当 TCP 对端受信任时,网关才会采用该标头。Google Cloud 和 AWS 完整示例提供了每种拓扑的具体值。如果没有受信任的代理,每个请求看起来都来自代理的 IP,这会使按 IP 的速率限制合并为一个共享配额,并在审计事件中记录代理的 IP。
不要将请求重定向到网关的设备授权端点和令牌端点,例如在 ingress 处使用 HTTP 到 HTTPS 或主机名规范化的重写。Claude Code 不会跟随这些请求上的重定向,因此对这些请求进行重定向的 ingress 规则会导致登录和令牌刷新失败。
为代理设置任意长于网关保活间隔的空闲超时时间,该间隔取决于上游:
- 在除
provider: anthropic之外的所有上游上,一旦流静默约 15 秒,网关就会写入一个 SSEping。 - 在
provider: anthropic上,网关会原样透传响应,包括 Anthropic API 自身的 ping。
为网关选择地址
Claude Code 通过以下两种方式之一接受网关的地址:- 私有地址:将网关置于内部负载均衡器或 VPN 之后,并使用仅解析为私有地址的主机名,例如 RFC 1918 或 CGNAT
100.64.0.0/10。用户的机器可以使用任何地址。私有网络前提条件列出了接受的地址范围。 - 声明的地址块:如果您的内部网络使用您的组织拥有的公共 IPv4 地址空间,请在
gatewayInternalNetworks托管设置中列出该地址块。网关和用户的机器都必须位于该地址块中。请参阅允许网关使用您拥有的公共地址空间。
容器镜像
围绕标准 Claude Code 发行版中的原生claude 二进制文件构建您自己的镜像:
- 从固定版本中下载适用于您镜像架构的 Linux 构建版本;下载 URL 请参阅安装特定版本。
- 按照二进制完整性和代码签名中的说明,根据该版本经过 GPG 签名的
manifest.json对其进行验证。 - 将其复制到构建上下文中。
- 基于 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
upstreams 参考提供了各平台的设置详情。对于跨云组合,例如在 GKE 上使用 Amazon Bedrock 上游,请改为在上游的 auth 块中设置显式凭据。
Cloud Run
按如下方式配置服务:- 将
listen.port保留为默认值8080(与 Cloud Run 的默认PORT一致),或设置port: ${PORT} - 将
public_url设置为外部可访问的源。对于生产环境,这通常是内部负载均衡器的主机名,因为/login会拒绝公共地址,而*.run.appURL 会解析为公共地址,因此仅使用 Cloud Run URL 只适用于curl或浏览器冒烟测试。例外情况是*.run.app通过 Private Service Connect 和 Cloud DNS 私有区域解析为私有地址的网络;在该拓扑中,Cloud Run URL 是有效的public_url。Google Cloud 完整示例涵盖了这两种情况。 - 将配置作为密钥卷挂载
- 设置
min-instances: 1,以避免在首次请求时进行冷启动的 OIDC 发现
将网关 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
- 成功的 mint 和 refresh 事件携带
-
运维日志:人类可读的
[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 之间的整数,然后重启容器。
client requests are open 警告时显示的 CPU 级别以下。
在使用 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 秒:
API_TIMEOUT_MS 只是为了更快地重试,请移除该值,否则一次未得到应答的重新发送就可能以 No response from API 结束该轮次。
要确认截止时间已触发,请让开发者在启用调试日志的情况下启动 Claude Code,然后在日志中搜索 [first-byte] no response headers。
中断行为
如果 Postgres 宕机,网关本身继续为已登录的开发者提供服务,新登录失败。开发者是否真的继续工作取决于您的编排器如何处理就绪:- 现有会话:持有者令牌使用 JWT 密钥在本地验证,会话刷新不接触存储,网关进程仍然可以提供推理
- 新登录:失败直到 Postgres 恢复,因为设备流及其速率限制计数器存在于 Postgres 中
- 支出限制执行:在中断期间默认失败开放,因此推理仍然流动;如果您宁愿阻止而不是无计量运行,将其翻转为失败关闭
- 就绪:默认情况下,
/readyz在 Postgres 无法访问时立即报告未就绪,因此每个副本一次失败其就绪检查。在流量仅到达通过检查的副本的地方,所有流量,包括网关仍然可以提供的推理,在 Postgres 恢复前失败。/healthz上的活跃探针在整个过程中继续通过。
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 密钥轮换
通过三个步骤轮换签名密钥,以便现有会话保持有效:- 生成一个新密钥。将其前置到
session.jwt_secret数组。 - 滚动部署。新令牌使用新密钥签名;旧令牌仍然验证。
- 在
ttl_hours加上一个边距之后,移除旧密钥并再次滚动。
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,默认为三个。托管服务可能保留更多,因此请查看其文档。
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
preStop 钩子的持续时间,因为宽限期在钩子运行之前而不是网关接收 SIGTERM 时开始计数。
您的平台也可能限制排空可以运行多长时间:
- Amazon ECS on Fargate:
stopTimeout最多允许 120 秒 - Cloud Run:在
SIGTERM后 10 秒停止实例,因此打开的流在那里最多获得 10 秒,无论排空窗口是什么
drain window over after 的警告,计数它切断的请求,并命名两个要提高的设置。
迁移是仅追加的,因此回滚到知道较少迁移的先前二进制文件是安全的;它忽略额外的行。回滚也重新验证 YAML 针对较旧二进制文件的架构,因此采用由较新版本引入的密钥的配置在较旧版本上启动失败。在回滚前移除新密钥。
因为您在自己的镜像中固定网关的版本,新 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 不会阻止首次注册。以下任一托管设置可以阻止:
- 市场列表:不包含该市场的
strictKnownMarketplaces允许列表,或指明该市场的blockedMarketplaces条目 - 环境变量:在托管的
env块 中将CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL设置为"1"
cli 或 code 块 中同时传递您的选择。
故障排除
如有问题和反馈,请使用 Claude Code 支持,或在 Claude Code GitHub 仓库上提交问题。您也可以联系您的 Anthropic 客户团队。报告问题时,请包括:- 网关问题:相关时间窗口内网关的 stderr、您的
gateway.yaml(已隐藏密钥)、网关版本(显示在/的登陆页面和/managed/settings的x-cc-gateway-version响应头中),以及最近的更改 - 登录问题:开发者运行
claude --debug-file ./claude-debug.txt、重现问题,然后发送该文件以及同一时间窗口内网关的审计日志 - 推理问题:请求的模型、配置的上游服务,以及该请求的网关审计日志,其中记录了由哪个上游提供服务以及响应状态
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 如何提供组
oidc.allowed_groups:决定谁可以登录admin.admin_groups:决定谁可以使用其网关会话调用管理 APImanaged.policies中的match.groups:决定哪个策略适用于开发者- 上游上的
groups:决定哪些上游为开发者提供服务 rbac_group支出上限:决定哪些组上限适用于开发者
长时间思考导致代理超时
如果您的上游是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 --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,此修复无需提高它。
900000 约为 30 分钟,1800000 约为 45 分钟。然后按照通过策略下发该值操作。
相关
- Claude 应用网关概述:快速入门和开发者连接
- 配置参考:每个
gateway.yaml选项