商业产业 Hacker News AI 2026-10-27T16:25 正文 ok 1774 words

我们如何让云端 Agent 查询生产环境数据(基于 OIDC,而非静态密钥)

英文标题:🔶 We let our cloud agents query prod (with OIDC, not secrets)

安全平台 Formal 分享了如何利用 OpenID Connect(OIDC)替代长期静态密钥,安全赋能 Cursor、Devin 等云端编码 Agent。通过将每个 Agent 运行实例视为独立的工作负载身份,并结合人类发起者身份做上下文断言,企业能够为 Agent 分配短期的细粒度访问权限。这一方案不仅覆盖生产数据分析与工作流排错,还能在开发环境中验证真实云资源,为 AI Agent 融入基础设施权限管理提供了标杆范式。
打开原文相关 3 · 机会 1
media4.8 KB media31.4 KB media1.2 KB media46.2 KB

中文

在 Formal,我们讨厌静态凭据(Secrets)。从第一天起,我们的使命就是将基础设施中长期凭据的数量缩减为零。

我们希望我们的产品以及客户与之交互的方式也能体现这一理念。因此,几个月前我们构建了 OIDC Integrations:允许任意机器工作负载凭借其自身的基础设施身份向 Formal 进行身份验证,并在此过程中可选地证明某个对应的人类身份。

除了产品本身与 CI/CD 工作负载之外,我们还发现支持 OIDC 在赋能基础设施中的 Agent 方面极其有用。像 Cursor 和 Devin 这样的云端编码 Agent 本身就是 OIDC 发行方(Issuers),这意味着我们现在可以通过 Formal 向它们授予经过审计、受策略控制的只读权限,以访问我们的生产数据和云基础设施。

OpenID Connect (OIDC)

简要回顾一下,针对机器工作负载的 OIDC 身份验证流程如下。其目标是从工作负载的身份提供商(IdP)获取一个短期令牌(Token),并将其作为身份证明提交给被称为依赖方(RP)的外部服务。依赖方通常会预先配置关于 IdP 的已知信息。

  1. 工作负载从其环境中获取其 OIDC JWT 令牌。有时这是作为环境变量提供的(例如 GitHub、GitLab);有时工作负载需要通过向元数据端点发起 API 调用来获取(例如 AWS、GCP、Azure)。通常,该令牌的作用域会限定在特定的 RP(例如通过设置 Audience aud 声明),以防止令牌在不同的 RP 之间被复用。
  2. 工作负载向 RP 发起 API 调用,使用获取到的 OIDC 令牌作为 Bearer 令牌(即 Authorization: Bearer eyJ...)。
  3. RP 从其配置中查找 JWKS 端点(通常类似于 https://idp.com/.well-known/openid-configuration),并获取 IdP 的公钥来验证该 JWT。
  4. RP 验证 JWT 是否由 IdP 的私钥签名。RP 还会检查嵌入在 JWT 中的部分声明(Claims)作为认证条件——这一点至关重要,因为 IdP 通常是多租户的。这些声明通常还会用于将传入的身份映射到 RP 上的机器身份。
  5. 如果所有检查均通过,则允许该 API 调用继续进行。有时,还会对通过 OIDC 认证的 API 调用应用额外的授权条件。

工作负载、其身份提供商与 Formal 之间的 OIDC 身份验证流程

Formal 中的 OIDC

我们的 OIDC 集成遵循上述流程,并包含以下几个关键补充细节:

  • 客户可以为每个集成指定一个 CEL 条件,只有当该条件评估为 true 时,给定令牌才被允许通过身份验证。此外,OIDC 声明字段还会作为输入暴露给权限(Permissions)模块,以便客户可以针对特定的控制平面 API 请求定义更细粒度的授权条件。
  • OIDC 认证调用方可以调用的一个端点是 CreateFederationToken,它会返回一个嵌入了源 OIDC 声明字段的短期 Connector 访问令牌。这意味着工作负载可以将 Connector 用作凭据注入代理,而无需自身存储长期凭据。
  • SPIFFE 工作负载也原生支持:Formal 像处理任何其他 OIDC 令牌一样验证来自 SPIRE 的 JWT-SVID,并让 SPIFFE ID 可用于策略(Policies)和权限(Permissions)。
  • OIDC 也可用于对 Formal Endpoint 和 Kubernetes Egress Agent 进行身份验证。

证明人类身份(Attesting human identity)

每个 OIDC 集成都会映射到一个单一机器用户。对于大多数传统的机器工作负载来说,这是一个很好的默认模型;然而,我们关注的许多工作负载并不是代表它们自己行动的。例如,CI 任务通常可以归因于提交了代码(Push commit)的人类,而云端 Agent 往往是因为有人从 Slack 中启动才运行的。因此,每个集成都可以选择定义一个令牌声明,Formal 会将其解析为人类身份,该身份会记录在日志中并作为策略的输入。

这里涉及的两种身份伴随着不同层级的信任。Formal 可以对机器身份进行加密验证,但人类身份仅由下游工作负载断言。我们接受该断言,是因为我们的客户信任做出断言的系统,并显式配置 Formal 去信任它。

我们从第一天起就支持这种模式,但原本是用于一个截然不同的场景:我们的许多客户使用 Formal 来控制商业智能(BI)工具与其数据源之间的数据库流量。像 Metabase 这样的工具每个数据库只能使用一组凭据,但客户通常需要根据登录 Metabase 的具体人类用户来差异化执行控制策略。我们最初构建最终用户身份传递(End-user identity propagation),就是为了允许 Metabase 在数据库查询中声明人类身份,并让 Formal 据此评估策略。

事实证明,这一模式与云端编码 Agent 几乎完美契合,尤其是那些全团队共享、任何人都可以从 Slack、Linear 或 GitHub 启动的多人协同 Agent。云端 Agent 平台无法合理地为可能启动 Agent 的每个人生成一套单独的凭据。但是,它可以颁发一个签名令牌,说明这是哪个 Agent 会话、它在哪个代码仓库中工作,以及是谁启动了它。我们的客户已经信任其 Agent 平台做出的声明,正如他们信任 Metabase 报告谁已登录一样。因此,客户可以使用相同的 Formal 原语,既在 Metabase 中对数据库列进行脱敏,又可以根据其拥有者所在的团队来控制云端 Agent 对基础设施的访问。

内部践行(Dogfooding)OIDC 集成

在 Formal,我们深度“吃自己的狗粮”——我们有一个 Connector 部署在生产数据库、S3 存储桶和日志集群前面,并使用 Formal Endpoint 来监控和控制开发者笔记本电脑上的编码 Agent 使用。我们生产租户的控制平面完全由 Terraform 管理,因此我们在内部使用 OIDC 集成的首批场景之一,就是将 Terraform Cloud 向 Formal 控制平面的身份验证方式切换为使用 OIDC,而不是长期 API 密钥。

我们是几家云端编码 Agent 平台的重度用户,并且一直在逐步将开发工作从笔记本电脑转移到云端沙盒中。此前,我们一直不愿让这些云端 Agent 访问我们的基础设施,因为这样做意味着必须分发静态凭据,而缺乏细粒度的范围控制。

然而,OIDC 带来了解决方案——我们不再需要为编码 Agent 分配静态凭据,并且我们可以基于云端 Agent 的具体细节(例如是哪位人类启动了它,或者它正在哪个 GitHub 仓库中工作)来评估策略。

以下是我们用来让云端 Agent 更高效的部分实际案例:

产品分析

通常,产品分析工作流依赖于来自生产数据库的数据(我们会将其复制到分析数据库中)。例如,衡量一个新产品功能的采用率,需要查看实际有哪些客户在数据库中存在该功能对象的记录。

在不久前,这个过程还是完全手动的——工程师需要手动编写针对这些工作流的 SQL 查询。编码 Agent 既擅长编写 SQL,又擅长理解分析结果,但我们此前没有一种安全的方式赋予它们受控的访问权限。因此,我们不得不手动运行查询、将结果导出为 CSV,然后再粘贴回 Agent 的上下文窗口中。

随着引入 OIDC,我们现在有办法将每个云端 Agent 会话视为独立的身份。受到我们帮助 Cursor 团队创建的一个流程的启发,我们建立了一套机制:云端 Agent 可以向对应的人类拥有者请求对生产数据库的短期访问审批。该流程使用了我们的 OIDC 集成,以及包括 Workflows 和 Policy Suspensions 在内的其他几个现有平台原语。

具体而言,Agent 可以通过 Slack 告知人类它究竟为什么需要访问权限,以及需要访问多久。一旦人类批准,访问权限的作用域就会限定在当前云端 Agent 的会话 ID 和所请求的具体数据库上。

调试异步工作流

我们的工作流编排提供商不提供远程 MCP 服务器,因此之前如果不使用长期凭据,就无法向云端 Agent 授予访问权限。这限制了我们使用编码 Agent 基于真实运行历史来优化工作流性能的能力。

我们现在可以将工作流编排器注册为 Formal 资源,并将只读 API 密钥存储为 Native User。借助 OIDC,云端 Agent 可以像上述数据库场景一样,申请并获得短期访问批准。所有请求都会记录在 Formal 中,并同时归因于该云端 Agent 和启动它的工程师。

测试云原生功能

Formal 平台支持 AWS、GCP 和 Azure 的多项原生功能。例如,Connector 可以使用 IAM 对数据库和 Kubernetes 集群进行身份验证。此外,控制平面可以与各大云厂商集成以自动发现资源并将日志发送到存储桶。

当我们迭代这些功能时,现在可以让云端 Agent 使用 OIDC 联合登录到我们的 Formal 开发租户以及云厂商的开发账户中。这使得 Agent 能够在一个实时、具有代表性的云环境中确保其代码更改按预期工作,帮助它形成闭环,而无需依赖脆弱的 mock 环境。

例如,我最近让一个 Cursor 云端 Agent 负责扩展我们的 Kubernetes Operator,使其能够使用 OIDC(例如通过 Azure Workload Identity)向 Formal 控制平面进行身份验证。在开发过程中,我赋予了该云端 Agent 访问 Azure 开发账户的权限,它在该账户中部署了一个新的 AKS 集群,以运行来自其 PR 分支的 Kubernetes Operator。该 Agent 通过连接到我们的 Formal 开发租户验证了:一旦它在集群中创建了一个 FormalPolicy CRD,该策略就会同步到该开发租户——从而证明了代码更改完全按预期工作。

总结

当云端 Agent 能够自主形成闭环时,它们会变得极其有用:它们应该能够收集产品使用数据、排查故障,并在真实环境中验证其工作成果。然而,这不应该以分发可复用的长期凭据为代价。

OIDC 帮助我们将单次 Agent 运行建模为一个唯一的工作负载身份。当 Agent 通过 Formal 向外部服务发出请求时,访问权限可以绑定到启动它的工程师、正在处理的仓库以及发起请求的特定 Agent 会话。生产环境的访问依然保持只读且需要审批把关,而开发环境中的权限可以更广泛,同时又不会演变为永久权限。

从我们的内部实践中得到的更广泛教训是:Agent 的访问权限应该被建模为一种临时委派(Temporary Delegation)——而不是仅仅看作另一个挂着长期凭据的机器工作负载。一旦建立起这种区分,赋予 Agent 访问真实基础设施的权限就会变得不再令人提心吊胆,并且其实用价值也将大为提升。

English / 原文

At Formal, we hate secrets. Since day one, we’ve been on a quest to drive the number of long-lived secrets in our infrastructure to zero.

We want our product and the way our customers interact with it to reflect that philosophy, too. Accordingly, we built OIDC Integrations a few months ago: a way for arbitrary machine workloads to authenticate to Formal on the strength of their own infrastructure identity, and optionally attest a human identity along the way.

Beyond product and CI/CD workloads, we’ve also found that supporting OIDC has been extremely useful for enabling agents to do more in our infrastructure. Cloud coding agents like Cursor and Devin themselves are OIDC issuers, which means that we can now give them auditable, policy-controlled, read-only access through Formal to our production data and cloud infrastructure.

OpenID Connect (OIDC)

For a brief refresher, an OIDC authentication flow works as follows for machine workloads. The goal is to obtain a short-lived token from the workload’s identity provider (IdP) and use it as proof of identity on an external service, known as the relying party (RP). The relying party is usually preconfigured with knowledge about the IdP.

  1. The workload obtains its OIDC JWT token from its environment. Sometimes this is provided as an environment variable (e.g., GitHub, GitLab); sometimes the workload needs to retrieve it by making an API call to a metadata endpoint (e.g., AWS, GCP, Azure). Typically, the token is scoped to the RP (e.g., by setting the Audience aud claim) to prevent token reuse across different RPs.
  2. The workload makes an API call to the RP, using the obtained OIDC token as a bearer token (i.e., Authorization: Bearer eyJ...).
  3. The RP looks up the JWKS endpoint (usually something like https://idp.com/.well-known/openid-configuration) from its configuration and obtains the IdP’s public key to verify the JWT with.
  4. The RP verifies the JWT is signed with the IdP’s private key. The RP also checks some of the claims embedded in the JWT as an authentication condition — which is important, because IdPs are typically multi-tenant. These claims are also typically used to map the incoming identity to a machine identity on the RP.
  5. If all checks pass, the API call is allowed to proceed. Sometimes, additional authorization conditions are applied to API calls that are authenticated via OIDC.

OIDC authentication flow between a workload, its identity provider, and Formal

OIDC at Formal

Our OIDC integrations follow the above flow, with a few key additional details:

  • Customers can specify a CEL condition for each integration, where a given token is only allowed to authenticate if that condition evaluates to true. Additionally, OIDC claim fields are also surfaced as inputs to Permissions, such that customers can define more granular authorization conditions for specific control plane API requests.
  • One endpoint that OIDC-authenticated callers can call is CreateFederationToken, which returns a short-lived Connector access token that embeds the source OIDC claim fields. This means that workloads can use the Connector as a credential-injecting proxy without needing to store long-lived credentials themselves.
  • SPIFFE workloads work natively too: Formal verifies JWT-SVIDs from SPIRE like any other OIDC token, with the SPIFFE ID available to Policies and Permissions.
  • OIDC can also be used to authenticate the Formal Endpoint and Kubernetes Egress Agent.

Attesting human identity

Every OIDC integration maps to a single machine user. This is a good default model for most traditional machine workloads; however, many workloads we care about don’t act on their own behalf. For example, CI jobs can usually be attributed to a human that pushed a commit, and cloud agents are often run because someone kicked one off from Slack. As such, each integration can optionally define a token claim that Formal resolves to a human identity which appears in logs and as input to policies.

The two identities involved here come with different kinds of trust. Formal can cryptographically authenticate the machine identity, but the human identity is only asserted by the downstream workload. We accept that claim because our customer trusts the system making it, and explicitly configures Formal to trust it too.

We’ve supported this shape since day one, but for a very different use case: many of our customers use Formal to control database traffic between business intelligence (BI) tools and their data sources. Tools like Metabase can only use one credential per database, but customers often differentially enforce controls based on the human user logged into Metabase. We originally built end-user identity propagation as a way to allow Metabase to assert the human identity to database queries and let Formal evaluate policies against it.

That pattern turns out to fit cloud coding agents almost perfectly, especially multiplayer ones that a whole team shares and anyone can launch from Slack, Linear, or GitHub. A cloud agent platform can’t reasonably produce a separate credential for every human who might launch an agent. It can, however, issue a signed token that says which agent session this is, which repository it’s working in, and who started it. Our customers already trust their agent platform to make that statement, just as they trust Metabase to report who is logged in. As a result, customers can use the same Formal primitive to both mask database columns in Metabase and to control cloud agent access to infrastructure by its owner’s team.

Dogfooding OIDC integrations

At Formal, we extensively dogfood our own product — we have a Connector that sits in front of our production databases, S3 buckets, and log clusters, and we use the Formal Endpoint to monitor and control coding agent usage on developer laptops. Our production tenant’s control plane is fully managed with Terraform, so one of our first internal use cases for the OIDC integration was to switch the way Terraform Cloud authenticates to the Formal control plane to use OIDC rather than a long-lived API key.

We’re heavy customers of a few cloud coding agent platforms, and we’ve been gradually shifting development away from our laptops and onto cloud sandboxes. We’d been reluctant to give these cloud agents access into our infrastructure, as doing so would have meant handing out static credentials without much in the way of granular scoping.

However, OIDC presented the solution — we no longer needed to assign coding agents static credentials, and we could evaluate policies based on specific details of the cloud agent (such as which human kicked it off, or which GitHub repo it’s working on).

The following is a non-exhaustive list of the use cases we’ve found to make our cloud agents more productive.

Product analytics

Often, product analytics workflows lean on data from our production databases (which we replicate to an analytics database). For example, measuring the rate of adoption of a new product object requires seeing which customers actually have database entries for that product object.

In the not-too-distant past, this process used to be manual — engineers used to need to manually compose SQL queries for these workflows. Coding agents are great both at writing SQL and digesting the results, but we previously didn’t have a safe way to give them controlled access. As a result, we would have to manually run the query, export the result as CSV, and then paste it back into the agent’s context.

With OIDC in the mix, however, we now had a way to treat each cloud agent session as its own identity. Inspired by a flow we helped our friends at Cursor create, we set up a way for cloud agents to request approval from their human owners for short-lived access to our production databases. This flow uses our OIDC integration, along with a few other existing platform primitives including Workflows and Policy Suspensions.

In particular, the agent can tell the human over Slack why exactly it needs access, and for how long. Once the human approves, access is scoped to the cloud agent session ID and the specific database that was requested.

Debugging asynchronous workflows

Our workflow orchestration provider doesn’t supply a remote MCP server, so previously there was no way to give cloud agents access without long-lived credentials. This limited our ability to use coding agents to optimize workflow performance based on real run history.

We can register the workflow orchestrator as a Formal resource and store a read-only API key as a Native User. Using OIDC, cloud agents can request and be approved for short-lived access as they would with databases above. Requests are always logged in Formal and attributed to both the cloud agent and the engineer that launched it.

Testing cloud-native features

The Formal platform supports a number of native features of AWS, GCP, and Azure. For example, the Connector can authenticate to databases and Kubernetes clusters using IAM. Additionally, the control plane can integrate with each cloud provider to autodiscover resources and to send logs to storage buckets.

When we iterate on these features, we can now have cloud agents federate using OIDC to both our Formal dev tenant and to a dev account on a cloud provider. This allows an agent to ensure its code change works as intended in a live, representative cloud environment, helping it close the loop without needing to resort to brittle mocks.

For example, I recently had a Cursor cloud agent work on extending our Kubernetes operator to authenticate to the Formal control plane using OIDC (e.g., with Azure Workload Identity). During the development process, I gave the cloud agent access to a dev Azure account, into which it deployed a new AKS cluster to run the Kubernetes operator from its PR branch. The agent was able to verify through connecting to our Formal dev tenant that once it created a FormalPolicy CRD in the cluster, the policy was synced to that dev tenant — proving the code change worked as intended.

Conclusion

Cloud agents become substantially more useful when they can close the loop themselves: they should be able to gather product usage data, investigate failures, and validate their work in realistic environments. However, that shouldn’t need to require handing out reusable, long-lived credentials.

OIDC helps us model an individual agent run as a unique workload identity. When an agent makes requests to external services via Formal, access can be tied to the engineer who launched it, the repository it is working on, and the specific agent session making the request. Production access remains read-only and approval-gated, while permissions in development environments can be broader without becoming permanent.

The broader lesson from our internal use is that agent access should be modeled as a temporary delegation — not just as another machine workload with another long-lived secret. Once that distinction exists, giving agents access to real infrastructure becomes much less frightening and considerably more useful.