Skip to content

Security

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 lock generates 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 local and http support 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-unlock is 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 = true on 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 data sources 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 .tf files
  • Use IAM roles or Vault for dynamic credential generation
  • Commit .terraform.lock.hcl to 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

References