Secrets Management
Relevant source files
The following files were used as context for generating this wiki page:
Pulumi's secrets management system provides a mechanism for encrypting sensitive values within a stack's configuration and state. This ensures that secrets like passwords, tokens, and keys are not stored in plaintext. The system is designed around a pluggable architecture, allowing for different encryption backends, known as secrets providers.
The core of this system is the Manager interface, which abstracts the details of encryption and decryption. Pulumi supports several secrets providers, including a default passphrase-based provider and various cloud-based Key Management Services (KMS). When a new stack is created, a secrets provider can be specified, and this choice is persisted in the stack's state.
Core Abstractions
The secrets management feature is built upon a few key interfaces and data structures that abstract the encryption and decryption logic.
secrets.Manager Interface
The Manager is the central interface for secrets management. It defines the contract for any secrets provider.
Source: pkg/secrets/manager.go:19-32
// pkg/secrets/manager.go:19-32
type Manager interface {
// Type returns the type of the secrets manager.
Type() string
// State returns any state that must be persisted for this secrets manager.
State() interface{}
// Encrypter returns a new Encrypter that can be used to encrypt values.
Encrypter() (Encrypter, error)
// Decrypter returns a new Decrypter that can be used to decrypt values.
Decrypter() (Decrypter, error)
}
type Encrypter interface {
Encrypt(plaintext []byte) ([]byte, error)
}
type Decrypter interface {
Decrypt(ciphertext []byte) ([]byte, error)
}
The diagram below shows the relationship between the Manager interface and its concrete implementations for different provider types.
Secret Value Representation
Within the Pulumi configuration system, a secret is a configuration value that is marked for encryption. The config.Secret struct is used to represent such a value.
Source: sdk/go/common/resource/config/secret.go:11-13
// sdk/go/common/resource/config/secret.go:11-13
type Secret struct {
Value string `json:"value"`
}
In a resource schema, a property can be marked as a secret by setting Secret: true in its schema.PropertySpec. This signals to the engine that the property's value should be encrypted.
Source: cmd/pulumi-test-language/providers/secret_provider.go:61-66
// cmd/pulumi-test-language/providers/secret_provider.go:61-66
"private": {
Secret: true,
TypeSpec: schema.TypeSpec{
Type: "string",
},
},
Secrets Providers
Pulumi offers different backends for storing and managing the encryption keys used for secrets. These are selected when a stack is initialized.
Selecting a Provider
When creating a new stack, the --secrets-provider flag can be used to specify which backend to use. The default is default, which corresponds to the passphrase provider.
Source: pkg/cmd/pulumi/operations/up.go:117-119
// pkg/cmd/pulumi/operations/up.go:117-119
cmd.PersistentFlags().StringVar(
&secretsProvider, "secrets-provider", "default", "The type of the provider that should be used to encrypt and "+
"decrypt secrets (possible choices: default, passphrase, awskms, azurekeyvault, gcpkms, hashivault). Only "+
"used when creating a new stack from an existing template")
The available choices include:
default/passphraseawskmsazurekeyvaultgcpkmshashivault
Passphrase Provider
This is the default provider. It uses a user-supplied passphrase to generate an encryption key.
The passphrase.Manager is the implementation of the secrets.Manager interface for this provider. It is created using NewPassphraseSecretsManager.
Source: pkg/secrets/passphrase/manager.go:34-37
// pkg/secrets/passphrase/manager.go:34-37
func NewPassphraseSecretsManager(passphrase, salt string, rounds int) (secrets.Manager, error) {
enc, err := newEncrypter(passphrase, salt, rounds)
// ...
}
Cloud Providers
Pulumi can integrate with major cloud providers' Key Management Services (KMS) to manage secrets. This approach offloads key management to the cloud provider, which can be more secure and manageable than using passphrases.
The cloud.NewCloudSecretsManager function acts as a factory for creating a secrets manager based on a KMS key URL.
The following diagram illustrates the logic for selecting a cloud provider based on the URL scheme.
This diagram shows how NewCloudSecretsManager dispatches to a specific cloud provider's implementation based on the scheme in the provided key URL.
Source: pkg/secrets/cloud/manager.go:42-60
The supported URL schemes are:
awskmsazurekeyvaultgcpkmshashivault
Each of these corresponds to a specific implementation that handles the communication with the respective cloud KMS API for encryption and decryption operations.
Configuration
Secrets can be marked in the Pulumi.yaml project file. This tells Pulumi to encrypt the value when it's set for a stack.
The config property in the project schema allows for a secret boolean field.
Source: sdk/go/common/workspace/project.json:71-75
"secret":{
"description":"If true this configuration value should be encrypted.",
"type":"boolean",
"default":false
}
An example of a ConfigTypeDeclaration within the project schema shows how a configuration key can be defined as a secret.
Source: sdk/go/common/workspace/project.json:281-297
"configTypeDeclaration":{
"title":"ConfigTypeDeclaration",
"type":"object",
"additionalProperties":false,
"properties":{
"type":{
"$ref":"#/$defs/simpleConfigType"
},
"items":{
"$ref":"#/$defs/configItemsType"
},
"description":{
"type":"string"
},
"secret":{
"type":"boolean"
},
"default":{ },
"value": { }
}
}
Summary
Pulumi's secrets management system is a flexible and essential feature for handling sensitive data. It relies on the secrets.Manager interface to support multiple encryption backends, from a simple passphrase-based system to robust cloud-based KMS solutions. By integrating secrets directly into the configuration and resource model, Pulumi ensures that sensitive values are encrypted throughout their lifecycle, from configuration to state storage, providing a secure foundation for infrastructure as code.