跳至主要内容

状态和计划加密

OpenTofu 支持对静态状态和计划文件进行加密,无论是在本地存储还是使用后端时。此外,您还可以将加密与 terraform_remote_state 数据源一起使用。此页面说明如何设置加密以及哪种加密方法适合哪种用例。

一般指南和陷阱(请阅读)

启用加密后,您的状态和计划文件在没有相应的加密密钥的情况下将无法恢复。请务必在启用加密之前仔细阅读本节。

加密可以防止什么?

启用加密后,OpenTofu 将静态加密状态数据。如果攻击者获取了您的状态文件,他们应该无法读取它并使用状态文件中包含的敏感值(例如访问密钥)。

但是,加密不能防止数据丢失(您的状态文件损坏),也不能防止重放攻击(攻击者使用旧的状态或计划文件并诱骗您运行它)。此外,OpenTofu 不会也不会阻止状态文件中敏感的值被运行 tofu 命令的人员访问。

我需要采取哪些预防措施?

启用加密后,请考虑谁需要直接访问您的状态文件。如果您有多于极少数的人员需要访问,您可能需要考虑从持续集成系统运行生产 planapply 运行,以保护加密密钥和状态中的敏感值。

您还需要根据您的安全要求决定要使用哪种密钥。您可以选择静态密码短语,也可以选择密钥管理系统。如果您选择密钥管理系统,则必须为某些加密方法配置自动密钥轮换。这对于您选择的加密算法有可能达到“密钥饱和”点(即接近密钥的最大安全使用限制)的情况尤其重要,例如 AES-GCM。您可以在下面的 加密方法 部分中找到有关此方面的更多信息。

最后,在启用加密之前,请演练您的灾难恢复计划并创建未加密状态文件的临时备份。此外,请确保您已备份密钥。启用加密后,OpenTofu 在没有正确密钥的情况下无法读取您的状态文件。

从未加密的状态/计划迁移

如果您有一个预先存在的状态文件并希望启用加密,则仅仅启用加密是不够的,因为 OpenTofu 将拒绝读取纯文本数据。这是一种保护机制,可以防止 OpenTofu 读取被操纵的、未加密的数据。有关详细的迁移说明,请参阅下面的 初始设置 部分。

兼容性保证

密码学研究可以迅速改变现有技术。我们将支持所有已记录的密钥提供程序和方法 +1 个次要版本,但可能会在任何次要版本中引入相同密钥提供程序和方法的新版本(例如 aes_gcm_v2),或新的密钥提供程序和方法。如果我们弃用密钥提供程序或方法,您将在运行 tofu plantofu apply 时在控制台上收到警告。如果您收到此类警告,请在升级到下一个版本之前切换。

配置

您可以通过在 OpenTofu 代码中指定配置或使用 TF_ENCRYPTION 环境变量来配置 OpenTofu 中的加密。这两种解决方案是等效的,如果您同时使用两者,OpenTofu 将合并这两个配置,并使用环境配置覆盖任何基于代码的设置。

基本配置结构如下所示

代码块
terraform {
encryption {
key_provider "some_key_provider" "some_name" {
# Key provider options here
}

method "some_method" "some_method_name" {
# Method options here
keys = key_provider.some_key_provider.some_name
}

state {
# Encryption/decryption for state data
method = method.some_method.some_method_name
}

plan {
# Encryption/decryption for plan data
method = method.some_method.some_method_name
}

remote_state_data_sources {
# See below
}
}
}

密钥和方法切换

在某些情况下,您可能希望更改加密配置。这可能包括重命名密钥提供程序或方法、更改密钥提供程序的密码或切换密钥管理系统。如果您在fallback块中提供旧配置,OpenTofu 支持自动轮换您的加密配置。

代码块
terraform {
encryption {
# Methods and key providers here.

state {
method = method.some_method.new_method
fallback {
method = method.some_method.old_method
}
}

plan {
method = method.some_method.new_method
fallback {
method = method.some_method.old_method
}
}
}
}

如果 OpenTofu 无法使用新方法**读取**您的状态或计划文件,它将自动尝试回退方法。当 OpenTofu **保存**您的状态或计划文件时,它将始终使用新方法,而不是回退方法。

初始设置

新项目

如果您正在设置一个新项目并且还没有状态文件,此示例配置将帮助您开始使用基于密码的加密。

代码块
variable "passphrase" {
# Change passphrase to be at least 16 characters long:
default = "changeme!"
}

terraform {
encryption {
## Step 1: Add the desired key provider:
key_provider "pbkdf2" "mykey" {
passphrase = var.passphrase
}
## Step 2: Set up your encryption method:
method "aes_gcm" "new_method" {
keys = key_provider.pbkdf2.mykey
}

state {
## Step 3: Link the desired encryption method:
method = method.aes_gcm.new_method

## Step 4: Run "tofu apply".

## Step 5: Consider adding the "enforced" option:
# enforced = true
}

## Step 6: Repeat steps 3-5 for plan{} if needed.
}
}

现有项目

当您首次在现有项目上配置加密时,您的状态和计划文件未加密。默认情况下,OpenTofu 拒绝读取它们,因为它们可能已被篡改。要启用读取未加密的数据,您必须指定unencrypted方法。

代码块
variable "passphrase" {
# Change passphrase to be at least 16 characters long:
default = "changeme!"
}

terraform {
encryption {
## Step 1: Add the unencrypted method:
method "unencrypted" "migrate" {}

## Step 2: Add the desired key provider:
key_provider "pbkdf2" "mykey" {
passphrase = var.passphrase
}

## Step 3: Add the desired encryption method:
method "aes_gcm" "new_method" {
keys = key_provider.pbkdf2.mykey
}

state {
## Step 4: Link the desired encryption method:
method = method.aes_gcm.new_method

## Step 5: Add the "fallback" block referencing the
## "unencrypted" method.
fallback {
method = method.unencrypted.migrate
}

## Step 6: Run "tofu apply".

## Step 7: Remove the "fallback" block above and
## consider adding the "enforced" option:
# enforced = true
}

## Step 8: Repeat steps 4-8 for plan{} if needed.
}
}

回滚加密

与上述初始设置类似,也可以通过使用unencrypted方法迁移到未加密的状态和计划文件,如下所示。

代码块
terraform {
encryption {
## Step 1: Leave the original encryption method unchanged:
method "some_method" "old_method" {
## Parameters for the old method here.
}

# Step 2: Add the unencrypted method here:
method "unencrypted" "migrate" {}

state {
## Step 3: Disable or remove the "enforced" option:
enforced = false

## Step 4: Move the original encryption method into the "fallback" block:
fallback {
method = method.some_method.old_method
}

## Step 5: Reference the unencrypted method as your primary "encryption" method.
method = method.unencrypted.migrate
}

## Step 6: Run "tofu apply".

## Step 7: Remove the "state" block once the migration is complete.

## Step 8: Repeat steps 3-7 for plan{} if needed.
}
}

远程状态数据源

您还可以为使用terraform_remote_state数据源的项目配置加密设置。这可以与您的主要配置相同,但您也可以定义一组单独的密钥和方法。配置语法如下所示。

代码块
terraform {
encryption {
# Key provider and method configuration here

remote_state_data_sources {
default {
method = method.my_method.my_name
}
remote_state_data_source "my_state" {
method = method.my_method.my_other_name
}
}
}
}

data "terraform_remote_state" "my_state" {
# ...
}

对于特定的远程状态,您可以使用以下语法。

  • myname 针对具有给定名称的主项目中的数据源。
  • mymodule.myname 针对指定模块中具有给定名称的数据源。
  • mymodule.myname[0] 针对指定模块中具有给定名称的第一个数据源。

密钥提供程序

PBKDF2

PBKDF2 密钥提供程序允许您使用长密码来生成用于加密方法(例如 AES-GCM)的密钥。您可以按如下方式配置它。

代码块
terraform {
encryption {
key_provider "pbkdf2" "foo" {
# Specify a long / complex passphrase (min. 16 characters)
passphrase = "correct-horse-battery-staple"

# Adjust the key length to the encryption method (default: 32)
key_length = 32

# Specify the number of iterations (min. 200.000, default: 600.000)
iterations = 600000

# Specify the salt length in bytes (default: 32)
salt_length = 32

# Specify the hash function (sha256 or sha512, default: sha512)
hash_function = "sha512"
}
}
}
选项描述最小值。默认值
passphrase (必需)输入一个长而复杂的密码。16 个字符。-
key_length要生成为密钥的字节数。132
iterations迭代次数。有关建议,请参阅此文档200.000600.000
salt_length密钥派生的盐的长度。132
hash_function指定sha256sha512作为哈希函数。不支持sha1N/Asha512

AWS KMS

此密钥提供程序使用Amazon Web Servers 密钥管理服务生成密钥。身份验证选项与S3 后端相同,不包括任何已弃用的选项。此外,请提供以下选项。

选项描述最小值。默认值
kms_key_idAWS KMS 的密钥 ID。.1-
key_specAWS KMS 的密钥规范。将其调整到您的加密方法(例如AES_256)。1-

以下示例说明了最小配置。

代码块
terraform {
encryption {
key_provider "aws_kms" "basic" {
kms_key_id = "a4f791e1-0d46-4c8e-b489-917e0bec05ef"
region = "us-east-1"
key_spec = "AES_256"
}
}
}

GCP KMS

此密钥提供程序使用Google Cloud 密钥管理服务生成密钥。身份验证选项与GCS 后端相同,不包括任何已弃用的选项。此外,请提供以下选项。

选项描述最小值。默认值
kms_encryption_key (必需)GCP KMS 的密钥 ID。.N/A-
key_length (必需)要生成为密钥的字节数。必须在11024字节的范围内。1-

以下示例说明了最小配置。

代码块
terraform {
encryption {
key_provider "gcp_kms" "basic" {
kms_encryption_key = "projects/local-vehicle-id/locations/global/keyRings/ringid/cryptoKeys/keyid"
key_length = 32
}
}
}

OpenBao(实验性)

此密钥提供程序使用OpenBao Transit Secret Engine生成数据密钥。您可以按如下方式配置它。

选项描述最小值。默认值
key_name (必需)用于加密/解密数据密钥的传输加密密钥的名称。在您的 OpenBao 服务器中预先配置它。N/A-
token访问 OpenBao API 时使用的授权令牌。OpenTofu 也可以从BAO_TOKEN环境变量中读取它。N/A-
address访问 API 的 OpenBao 服务器地址。OpenTofu 也可以从BAO_ADDR环境变量中读取它。您的系统必须信任服务器的 TLS 证书。N/Ahttps://127.0.0.1:8200
transit_engine_path在 OpenBao 中启用 Transit Secret Engine 的路径。如果您更改了传输引擎路径,请自定义此路径。N/A/transit
key_length要生成为密钥的字节数。可用选项为163264字节。1632

以下示例说明了可能的配置。

代码块
terraform {
encryption {
key_provider "openbao" "my_bao" {

# Required. Name of the transit encryption key
# to use to encrypt/decrypt the data key.
key_name = "test-key"

# Optional. Authorization Token to use when accessing OpenBao API.
# You can also set this in the BAO_TOKEN environment variable.
token = "s.Fg8wA4nDrP08TirpjEXkrTmt"

# Optional. OpenBao server address to access the API on.
# You can also set this using the BAO_ADDR environment variable.
address = "http://127.0.0.1:8200"

# Optional. You can customize this if you mounted the
# transit engine on a different path. Default: /transit
transit_engine_path = "/my-org/transit"

# Optional. Number of bytes to generate as a key. Default: 32
key_length = 16
}
}
}

方法

AES-GCM

目前唯一支持的加密方法是 AES-GCM。您可以通过以下方式配置它。

代码块
terraform {
encryption {
# Key provider configuration here

method "aes_gcm" "yourname" {
keys = key_provider.yourkeyprovider.yourname
}
}
}

未加密

unencrypted方法用于提供到加密和从加密的显式迁移路径。它不需要任何配置,并且可以在上面的初始设置块中看到其使用。