本文系统讲解了 Pulumi 中密钥的流转与安全边界:从 Provider 认证、基础设施配置到 state 文件中的加密数据,分析了 Pulumi 内置 secrets 机制、ESC 环境与 KMS/Vault 等后端的能力与局限。
Published on Wednesday, April 1, 2026

Pulumi 允许你使用 TypeScript、Python、Go、C# 和 Java 等通用编程语言来定义云基础设施。这使工程团队能够对 AWS、Azure、GCP 和 Kubernetes 上的资源进行完全的程序化控制。
但这种灵活性也带来了实际的运维挑战。你的 Pulumi stack 需要在各个层面访问敏感凭据。Provider 身份验证需要云访问密钥,基础设施配置依赖数据库密码和 API Token,而 state file 最终会存储所有这些内容的加密版本。随着团队和 stack 数量的增长,仅靠 Pulumi 本身来确保这些 secret 的安全、轮换和合理作用域,会变得越来越难以管理。
本指南将拆解 secret 如何在 Pulumi 的架构中流动、其内置加密模型在生产环境中存在哪些不足,以及如何集成外部 secrets manager,以在不干扰现有 Pulumi 工作流的情况下获得中心化访问控制、轮换和审计能力。
Pulumi 允许你使用自己选择的语言编写基础设施即代码。你的代码定义了一组资源的 有向无环图(DAG),用来描述云基础设施的组成方式。

当你运行 pulumi up 时,engine 会创建一个 stack,它是你程序的一个隔离且可配置的实例。你可以把 stack 想象成部署环境,比如 production、staging、development 等。
engine 会与 cloud provider 协调,创建、更新或删除资源,直到实际状态与期望状态一致。在这个过程中,Pulumi 会维护 state file,用于跟踪每个资源、其元数据、输出和关系。你可以将这些 state file 存储在 Pulumi Cloud service(默认)、像 S3 这样的自托管 backend,或者本地文件系统中。
你的基础设施引用的任何 secret 最终都会进入这些 state file。这使得保护它们成为硬性要求,而不是事后补救。
Pulumi 的 IaC 模型会在三个不同的上下文中接触到 secret。每一种都代表着一个需要谨慎处理敏感数据的面:
pulumi up 期间需要这些凭据来向 provider 进行身份验证并执行变更,但它们通常由 Pulumi 之外的系统管理。举个具体例子:一个典型的微服务部署可能会使用 AWS 凭据来预配 RDS 数据库和 ECS 服务,在应用配置中使用 Stripe API 密钥和 SendGrid 凭据,并将 RDS 生成的管理员密码存储在 state 中。每一类都有不同的访问模式、不同的生命周期要求以及不同的安全控制。
Pulumi 自带加密能力,可保护静态存储和传输中的 secret。加密后的 secret 会存储在 state file 和 stack 配置中,避免被随意暴露。在评估是否需要外部 secrets manager 之前,值得先了解 Pulumi 开箱即提供了什么。
当你使用 pulumi.secret() 或 --secret 标志将某个值标记为 secret 时,Pulumi 会在将其存储到 state file 之前对其进行加密。每个 stack 都有自己的加密密钥,并且会为每个值应用独立的 salt,以防止相同的明文值生成相同的密文。
默认情况下,加密由 Pulumi Cloud service 处理。但你也可以根据组织需求替换为其他 provider:
要使用自定义加密 provider 初始化 stack,可以在创建时传入它:
pulumi stack init my-stack \
--secrets-provider="awskms://alias/my-key?region=us-east-1"
Pulumi 会在程序执行过程中跟踪 secret 值。当你使用 config.requireSecret() 获取一个 secret 时,返回值会被包装成一个 secret Output。任何由该 secret 派生出来的 Output 也会自动被标记为 secret。
这意味着,如果你把数据库密码插入到连接字符串中,Pulumi 知道生成后的字符串同样是敏感的,并会相应地在 state 中对其加密。
在 CLI 侧,Pulumi 会在终端输出中屏蔽 secret 值,用 [secret] 占位符替换它们。这可以防止 secret 以明文形式出现在日志、CI/CD 输出或终端历史中。
Pulumi ESC(Environments, Secrets, and Configuration)是 Pulumi 专门用于在 stack、团队和工具之间大规模管理 secret 和配置的产品。与其在 stack 配置文件中重复复制凭据,不如使用 ESC 定义可组合的 YAML environment,将其作为单一事实来源。这些 environment 可以通过内置 provider 从 AWS Secrets Manager、Azure Key Vault 或 1Password 等外部存储中动态拉取 secret,也可以通过 OIDC 签发短生命周期的云凭据,从而完全消除对长期有效访问密钥的需求。
ESC 包括 RBAC、支持打标签发布的 environment 版本管理,以及审计日志。它通过 stack 配置文件中的 environment block 直接与 Pulumi IaC stack 集成,同时也可以通过 esc run 命令独立与任何 CLI 工具或 CI/CD pipeline 协同工作。
对于完全投入 Pulumi 生态的团队来说,ESC 解决了 Pulumi 核心 secret 模型中的多个缺口。但它确实会将你的 secrets management 绑定到 Pulumi 平台上,这对于需要一个横跨 IaC 工具、CI/CD pipeline、应用运行时和其他内部系统的 secret 层的组织来说,可能并不适用。
即使有 ESC 的加持,随着规模扩大,一些结构性限制也会变得明显:
pulumi up,并希望所有下游使用方都能获取到变更。在规模化场景下,这既是安全风险,也是可靠性风险。大多数 IaC 工具都把 secret 视为次要问题,而访问控制、轮换和审计方面的缺口往往也呈现出同样的模式。生产环境中的标准做法是将 IaC 平台与专门的 secrets manager 配合使用,由后者负责完整生命周期:存储、访问控制、轮换、审计和分发。
对于 Pulumi 团队来说,Infisical 是一个值得评估的选项。它是一个开源的 secrets management 平台,将 secret、证书和特权访问集中到单一工具中。更重要的是,在这里它通过专门的 ESC provider 原生集成 Pulumi,用于 authentication 和 secret retrieval,因此你不必为了采用它而重新设计现有工作流。
Infisical 支持在 project、environment 和单个 secret 级别进行 基于角色的访问控制。你可以授予某个 CI/CD pipeline 对生产数据库凭据的只读访问,而不会暴露同一环境中的第三方服务 API 密钥。
除了 RBAC 之外,Infisical 还支持 ABAC,其权限会根据请求访问的身份上的元数据属性动态评估。这正是 SOC 2 和 ISO 27001 等合规框架所要求的策略粒度,也是 Pulumi 的 stack 级模型无法独立提供的。
Infisical 可以针对广泛的 backend 按需生成 dynamic secrets,包括 PostgreSQL、MySQL、MongoDB、Oracle、MSSQL、Cassandra、Redis、RabbitMQ、Snowflake、AWS IAM、AWS ElastiCache、Azure Entra ID、Azure SQL、GCP IAM、LDAP、Elasticsearch、Couchbase、Mongo Atlas、SAP ASE、SAP HANA、Vertica、GitHub、TOTP 和 Kubernetes service account。这些凭据是短生命周期的,并且对每个 consumer 都是唯一的,这意味着一旦 secret 被泄露,其 blast radius 有限,并且会自动过期。
对于个人用户和运维人员,Infisical 的 temporary access provisioning 允许你授予有时间限制且会自动撤销的权限。结合 access request 工作流,这为团队提供了一种自助式 production access 模式,而无需 platform engineer 手动更新策略。
Infisical 提供中心化 audit log,可跟踪所有使用方对每一次 secret 访问、修改和删除的操作,无论它来自 Pulumi stack、Kubernetes deployment、CI/CD pipeline,还是开发者的本地环境。对于需要将这些日志流式传输到 SIEM 或外部监控工具的组织,Infisical 支持向 Datadog 和 Better Stack 等 provider 进行 audit log streaming。
Infisical 通过可在 Pulumi ESC 中使用的两个 provider 与 Pulumi 连接:
infisical-login 负责 authentication。它会为 Infisical 生成短生命周期的 OIDC token,因此你的 Pulumi stack 不需要长期有效的 API key 或 service token 来访问 secret。凭据会即时创建并自动过期。infisical-secrets 负责 secret retrieval。它会在运行时从 Infisical 动态获取 secret,并遵守你配置的访问控制和策略。secret 会在 pulumi up 期间被解析,并提供给你的程序使用,而无需硬编码在配置文件或环境变量中。关键的架构点在于:Infisical 成为 secret 的事实来源,而 Pulumi 仍然是基础设施的编排层。你的 Pulumi 代码通过名称引用 secret,而 Infisical 负责谁可以访问它们、它们何时过期、如何轮换,以及最后是谁访问了它们。
最快的上手方式是通过针对 Infisical 的 Pulumi ESC providers。该设置使用基于 OIDC 的 authentication,因此你的 Pulumi stack 永远不会存储长期有效的 Infisical 凭据。
开始之前,你需要:
esc)。https://api.pulumi.com/oidc 的 Infisical Machine Identity。重要: 将 Pulumi ESC 与 Pulumi IaC 集成时存在一个已知问题:发送给 Infisical 的默认 OIDC subject identifier 无法开箱即用。你需要为 ESC environment definition 配置自定义的 subjectAttributes,或者将字面量 subject claim pulumi:environments:org:{your-org}:env:<yaml> 添加到你的 Infisical Identity 的 allowed subjects 中。
创建一个处理与 Infisical authentication 的 Pulumi ESC environment。创建一个 environment(例如 your-org/infisical-auth/oidc-login),定义如下:
## Environment: your-org/infisical-auth/oidc-login
values:
infisical:
login:
fn::open::infisical-login:
oidc:
identityId: <your-identity-id> # 替换为你的 Infisical Identity ID
environmentVariables:
INFISICAL_TOKEN: ${infisical.login.accessToken}
通过运行以下命令验证设置:
esc open your-org/infisical-auth/oidc-login
为 secret 创建第二个 ESC environment。例如,your-org/my-app/dev:
## Environment: your-org/my-app/dev
imports:
- your-org/infisical-auth/oidc-login
values:
infisical:
secrets:
fn::open::infisical-secrets:
login: ${infisical.login}
get:
dbPassword:
projectId: <your-infisical-project-id>
environment: dev
secretKey: DATABASE_PASSWORD
stripeKey:
projectId: <your-infisical-project-id>
environment: dev
secretKey: STRIPE_API_KEY
environmentVariables:
DATABASE_PASSWORD: ${infisical.secrets.dbPassword}
STRIPE_API_KEY: ${infisical.secrets.stripeKey}
pulumiConfig:
DATABASE_PASSWORD: ${infisical.secrets.dbPassword}
STRIPE_API_KEY: ${infisical.secrets.stripeKey}
在你的 stack 配置文件中引用 ESC environment:
## Pulumi.dev.yaml
environment:
- your-org/my-app/dev
在你的 TypeScript 程序中访问这些 secret:
import * as pulumi from "@pulumi/pulumi";
const config = new pulumi.Config();
const dbPassword = config.requireSecret("DATABASE_PASSWORD");
const stripeKey = config.requireSecret("STRIPE_API_KEY");
同一个 ESC environment 也可以集成到 CI/CD 工作流中。下面是一个 GitHub Actions 示例:
## .github/workflows/deploy.yml
name: Deploy Infrastructure
on:
push:
branches:
- main
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Authenticate with Pulumi Cloud (OIDC)
uses: pulumi/auth-actions@v1
with:
organization: your-org
requested-token-type: urn:pulumi:token-type:access_token:team
team: your-team-name
- name: Deploy with Pulumi
uses: pulumi/actions@v6
with:
command: up
stack-name: your-org/your-stack-name
同一个 ESC environment 可以通过 esc run 将 secret 注入任何 CLI 工具:
## Run a database migration
esc run your-org/my-app/dev -- npx prisma migrate deploy
Infisical 不仅限于 secret,还覆盖了相邻的基础设施需求,如 Kubernetes sync、certificate management 和 privileged access。
Infisical 的 Kubernetes Operator 通过 CRD 将 secret 直接同步到原生 Kubernetes Secret 中:
InfisicalSecret:同步 secret 并重新加载依赖的工作负载。InfisicalPushSecret:从 K8s 双向同步回 Infisical。InfisicalDynamicSecret:管理集群内的动态 secret lease。Infisical 处理完整的 X.509 生命周期:
Infisical PAM 将用户身份与基础设施凭据解耦:
Pulumi 为你提供强大的基础设施编排能力,但其原生 secret 模型在细粒度访问控制和大规模自动轮换方面可能存在局限。Infisical 在不要求你更改部署代码的前提下弥补了这些缺口。借助基于 OIDC 的 authentication 和动态 secret 获取能力,你的 secret 可以在每个 stack 和 pipeline 中保持安全。
- 原文链接: infisical.com/blog/pulum...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!