Open-Source Wikis

/

Pulumi

/

Security

pulumi/pulumi

Security

How pulumi/pulumi handles trust, authentication, and secrets.

Active contributors

Active contributors: Justin Van Patten, Thomas Gummerer, Ian Wahbe, Fraser Waters

Trust boundaries

graph TD
    User[User shell] -->|local| CLI[pulumi CLI]
    CLI -->|spawns| LangHost[Language host]
    CLI -->|spawns| Provider[Provider plugin]
    LangHost -->|spawns| UserProg[User program]
    CLI -->|HTTPS| Cloud[Pulumi Cloud / DIY backend]
    Provider -->|HTTPS| CloudAPI[AWS/Azure/GCP/...]
    UserProg -->|gRPC: localhost only| CLI

    classDef trusted fill:#dfd
    classDef external fill:#fdd
    class User,CLI,LangHost,UserProg,Provider trusted
    class Cloud,CloudAPI external

Boundaries:

  1. User shell ↔ CLI — the CLI runs with the user's privileges.
  2. CLI ↔ plugins — language hosts and providers are trusted — they're spawned by the CLI from the user's own plugin cache. Plugin tampering = full compromise.
  3. CLI ↔ Pulumi Cloud (httpstate) — HTTPS, OAuth bearer tokens.
  4. Provider ↔ cloud APIs — the provider authenticates as the user (their AWS creds, etc.).
  5. gRPC between processes — bound to localhost (127.0.0.1:<random>); not authenticated. Anyone on the same machine could connect, which is not a defended-against threat model.

Authentication

Pulumi Cloud (httpstate)

pkg/cmd/pulumi/auth/ and pkg/backend/httpstate/token_source.go handle authentication:

  • pulumi login initiates OAuth (browser flow) or accepts a PULUMI_ACCESS_TOKEN.
  • Tokens are stored in ~/.pulumi/credentials.json (mode 0600).
  • The token source supports refresh.
  • Per-org tokens are scoped to that org.

DIY backend

No authentication at the Pulumi layer. The blob bucket itself enforces access (S3/GCS/Azure Blob IAM); Pulumi just reads/writes via gocloud.dev/blob.

Secrets at rest

Already detailed under systems/secrets. Brief recap:

  • Every secret in the snapshot is encrypted with the configured secrets.Manager.
  • Default: passphrase-derived AES for DIY, service-managed for Cloud.
  • KMS providers (AWS, Azure, GCP, Vault) are first-class.
  • The "secret" bit is sticky through Output transformations.

Secrets in transit

  • Engine ↔ Pulumi Cloud: HTTPS only, hard-coded.
  • Engine ↔ provider: gRPC over localhost loopback (no TLS).
  • Engine ↔ language host: gRPC over localhost loopback (no TLS).

Pluging integrity

Plugins are downloaded via HTTPS from GitHub Releases (or the Pulumi Cloud registry). Integrity:

  • HTTPS prevents in-transit tampering.
  • pulumi plugin install validates the SHA256 against the published manifest where available.
  • Once installed, plugins live in ~/.pulumi/plugins/ and are not re-verified at run time.

There is no signature verification chain from the plugin author to the user's machine — installing a malicious plugin would be a full compromise.

Provider credentials

Pulumi providers authenticate with the cloud they manage. They typically read environment variables (AWS_ACCESS_KEY_ID, AZURE_CLIENT_ID, etc.) or files (~/.aws/credentials). The engine does not inspect these.

Because providers run as separate processes spawned by the engine, they inherit the engine's environment. Don't put secrets in Pulumi.<stack>.yaml config except as encrypted secrets.

Encrypted log streams

Diagnostic events that the engine streams back to Pulumi Cloud are encrypted in transit (HTTPS). Log payloads sanitize secret values before serialization (see sdk/go/common/diag/).

pkg/engine/encryptedlog/ handles end-to-end encrypted log streaming for sensitive scenarios where TLS alone isn't sufficient.

Policy enforcement

Policy packs (proto/pulumi/analyzer.proto) run as plugins. They can:

  • Mandatory policies — block an op (the engine refuses to proceed).
  • Advisory policies — print a warning.
  • Remediation policies — propose a transformation that the engine can apply.

The CLI pulumi policy (pkg/cmd/pulumi/policy/) is the management surface.

What about the user program?

User programs run in the language host's sandbox, which is not a security sandbox in any meaningful sense — the program has access to the network, the filesystem, the env, etc. Pulumi treats user code as trusted: a malicious user program could read secrets, exfiltrate, etc.

The design philosophy is "Pulumi is your code". If you don't trust the program, don't run it.

Recent security work

Notable security-relevant areas in recent history:

  • Snapshot integrity (validating_persister.go, analyze_snapshot.go) — every snapshot save is round-tripped through structural validation.
  • Token rotation (pkg/backend/httpstate/token_source.go) — automatic refresh.
  • Cloud registry (pkg/backend/httpstate/cloud_registry.go) — alternate plugin source allowing org-scoped trust.
  • ESC integration (pkg/cmd/pulumi/env/) — short-lived credentials per stack via Pulumi Environments.

Known limitations

  • Plugin caches are not re-verified post-install. ~/.pulumi/plugins/ should be treated as part of the trusted base.
  • No package signing chain.
  • gRPC sockets between engine and plugins are unauthenticated localhost — multi-tenant servers should not run multiple users' Pulumi at once on the same host.

See also

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Security – Pulumi wiki | Factory