Security¶
See also
This note covers the security model of OpenTofu, including state encryption, provider credential handling, module verification, and backend security.
Threat Model Overview¶
| Threat Surface | Mitigation |
|---|---|
| State file exposure (at rest) | Built-in encryption (AES-GCM with KMS/PBKDF2) |
| State file tampering | Lineage + serial integrity checks |
| Provider credential leakage | Environment variables, vault integration, IAM roles |
| Supply chain (malicious modules/providers) | GPG verification, lock file checksums |
| Network interception | TLS for all remote backend and registry connections |
| Concurrent state corruption | State locking on all supported backends |
State Encryption¶
State encryption is OpenTofu's flagship security feature and the primary differentiator from Terraform OSS. It encrypts state and plan files at rest using a configurable key provider and encryption method.
Configuration¶
terraform {
encryption {
# Key provider: where the encryption key comes from
key_provider "aws_kms" "prod_key" {
kms_key_id = "alias/tofu-state-key"
key_spec = "AES_256"
region = "us-east-1"
}
# Encryption method: how data is encrypted
method "aes_gcm" "default" {
keys = key_provider.aws_kms.prod_key
}
# Apply encryption to state and plan files
state { method = method.aes_gcm.default }
plan { method = method.aes_gcm.default }
}
}
Supported Key Providers¶
| Key Provider | Use Case | Key Generation |
|---|---|---|
pbkdf2 |
Local/dev environments | Passphrase-derived (configurable iterations, SHA-256/512) |
aws_kms |
AWS deployments | AWS KMS generates data keys |
gcp_kms |
GCP deployments | Google Cloud KMS generates data keys |
azure_keyvault |
Azure deployments | Azure Key Vault key wrapping |
openbao |
Self-hosted vault | OpenBao (Vault fork) transit secrets engine |
Encryption Method: AES-GCM¶
- Algorithm: AES-256-GCM (Galois/Counter Mode).
- Provides both confidentiality and authenticity (AEAD).
- Each encryption operation uses a unique nonce.
- Encrypted data is stored as a base64-encoded envelope containing the ciphertext, nonce, and key provider metadata.
Migration from Unencrypted State¶
OpenTofu supports a fallback mechanism for migrating existing unencrypted states:
terraform {
encryption {
key_provider "aws_kms" "my_key" {
kms_key_id = "alias/my-key"
key_spec = "AES_256"
}
method "aes_gcm" "new_method" {
keys = key_provider.aws_kms.my_key
}
method "unencrypted" "legacy" {}
state {
method = method.aes_gcm.new_method
fallback = method.unencrypted.legacy
}
}
}
Key rotation
To rotate encryption keys, add a new key provider and use the fallback field to reference the old key during the transition period. OpenTofu will decrypt with the fallback key and re-encrypt with the new key on the next write.
Provider Credential Management¶
Providers (AWS, GCP, Azure, etc.) require credentials to interact with cloud APIs. OpenTofu does not store provider credentials in state or configuration files.
Credential Sources (AWS Example)¶
| Priority | Source | Recommended For |
|---|---|---|
| 1 | Static credentials in provider block | Not recommended (avoid in production) |
| 2 | Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) |
CI/CD pipelines |
| 3 | Shared credentials file (~/.aws/credentials) |
Local development |
| 4 | IAM role for EC2/ECS | Compute-hosted OpenTofu |
| 5 | Assume role (STS) | Cross-account access |
Never hardcode credentials
Do not embed credentials in .tf files. Use environment variables, IAM roles, or a secrets manager. The sensitive attribute can mark variables as sensitive to prevent CLI output from displaying values.
Vault Integration¶
OpenTofu can retrieve dynamic credentials from HashiCorp Vault (or OpenBao) using the Vault provider:
provider "vault" {
address = "https://vault.YOUR_DOMAIN:8200"
}
data "vault_generic_secret" "aws_creds" {
path = "aws/creds/my-role"
}
provider "aws" {
access_key = data.vault_generic_secret.aws_creds.data["access_key"]
secret_key = data.vault_generic_secret.aws_creds.data["secret_key"]
}
Module Verification¶
Lock File Integrity¶
The .terraform.lock.hcl file pins provider versions and records SHA256 hashes of provider packages:
provider "registry.opentofu.org/hashicorp/aws" {
version = "5.45.0"
constraints = "~> 5.0"
hashes = [
"h1:abc123...",
"zh:def456...",
]
}
h1:hashes are computed from the provider zip content.zh:hashes are computed from the provider zip content using a platform-specific scheme.- Running
tofu providers lockgenerates this file, which should be committed to version control.
GPG Verification¶
The OpenTofu Registry supports GPG signature verification for modules. When enabled, OpenTofu verifies that module packages are signed by the expected author before downloading and extracting them.
Backend Security¶
Remote State Backends¶
| Backend | Encryption at Rest | Encryption in Transit | State Locking |
|---|---|---|---|
| S3 | SSE-S3, SSE-KMS, SSE-C | TLS (HTTPS) | DynamoDB or S3 object lock |
| GCS | Google-managed or CMEK | TLS | Native object versioning + generation |
| Azure Blob | SSE (Microsoft-managed or CMK) | TLS | Blob lease |
| PostgreSQL | Disk encryption (pg configuration) | TLS (required) | Advisory locks |
| Consul | Optional (base64) | TLS (recommended) | Session-based locks |
State Locking¶
State locking prevents concurrent writes that could corrupt state:
- All remote backends except
localandhttpsupport state locking. - Locks are acquired before any write operation (plan, apply, refresh).
- If a lock cannot be acquired, the operation fails with a clear error message.
tofu force-unlockis available as a last resort when locks are stuck (use with caution).
Backend Authentication¶
Each backend type has its own authentication mechanism:
# S3 backend with IAM role assumption
terraform {
backend "s3" {
bucket = "my-tofu-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "tofu-locks"
}
}
The encrypt = true option enables S3 server-side encryption (SSE-S3 by default, SSE-KMS if kms_key_id is specified). This is separate from OpenTofu's built-in state encryption and provides a defense-in-depth layer.
Sensitive Data in State¶
Even with encryption, certain best practices apply:
- Use
sensitive = trueon variables and outputs to prevent values from appearing in CLI output. - Review state files for inadvertently stored sensitive values (some providers may store passwords in plaintext attributes).
- Restrict file system permissions on local state files (
chmod 600 terraform.tfstate). - Use remote backends with encryption for all production workloads.
- Consider using
datasources to fetch secrets at plan/apply time rather than storing them in configuration.
Security Checklist¶
- Enable state encryption with a KMS-backed key provider
- Use remote backend with state locking for all team environments
- Enable backend encryption at rest (S3 SSE-KMS, GCS CMEK, etc.)
- Never hardcode provider credentials in
.tffiles - Use IAM roles or Vault for dynamic credential generation
- Commit
.terraform.lock.hclto version control - Mark all sensitive variables and outputs with
sensitive = true - Restrict local state file permissions to owner-only
- Use GPG verification for modules from external sources
- Rotate encryption keys periodically using the fallback mechanism