Шифрование состояния и плана
OpenTofu поддерживает шифрование файлов состояния и плана при хранении — как локально, так и при использовании бэкенда. Кроме того, шифрование можно использовать с источником данных terraform_remote_state. На этой странице объясняется, как настроить шифрование и какой метод подходит для каждого сценария использования.
Общие рекомендации и возможные проблемы (обязательно прочитайте)
При включении шифрования файлы состояния и плана невозможно будет восстановить без соответствующего ключа шифрования. Внимательно прочитайте этот раздел, прежде чем включать шифрование.
От чего защищает шифрование?
При включении шифрования OpenTofu будет шифровать данные состояния при хранении. Если злоумышленник получит доступ к файлу состояния, он не должен суметь прочитать его и использовать содержащиеся в нём конфиденциальные значения (например, ключи доступа).
Однако шифрование не защищает от потери данных (повреждения файла состояния) и не защищает от атаки повторного воспроизведения (когда злоумышленник использует более старый файл состояния или плана и обманом заставляет вас запустить его). Кроме того, OpenTofu не защищает и не может защитить конфиденциальные значения в файле состояния от человека, запускающего команду tofu.
Какие меры предосторожности необходимо принять?
При включении шифрования подумайте, кому нужен непосредственный доступ к файлу состояния. Если доступ нужен более чем небольшому числу людей, рассмотрите возможность запуска рабочих процессов plan и apply для производственной среды из системы непрерывной интеграции, чтобы защитить и ключ шифрования, и конфиденциальные значения в состоянии.
Также необходимо выбрать тип ключа с учётом требований безопасности. Можно использовать статическую парольную фразу или систему управления ключами. Если вы выбрали систему управления ключами, для некоторых методов шифрования крайне важно настроить автоматическую ротацию ключей. Это особенно важно, если выбранный алгоритм шифрования может достичь состояния «насыщения ключа», когда приближается максимальный безопасный предел использования ключа, например AES-GCM. Дополнительную информацию см. в разделе методы шифрования ниже.
Наконец, прежде чем включать шифрование, проверьте план аварийного восстановления и создайте временную резервную копию незашифрованного файла состояния. Также убедитесь, что у вас есть резервные копии ключей. После включения шифрования OpenTofu не сможет прочитать файл состояния без правильного ключа.
Переход с незашифрованного состояния/плана
Если у вас уже есть файл состояния и вы хотите включить шифрование, одного включения шифрования недостаточно: OpenTofu откажется читать обычный текст. Это защитный механизм, который не позволяет OpenTofu читать изменённые незашифрованные данные. Подробные инструкции по миграции см. в разделе первоначальная настройка ниже.
Гарантия совместимости
Исследования в области криптографии могут быстро менять современный уровень развития технологий. Мы будем поддерживать все документированные поставщики ключей и методы в течение +1 минорной версии, но в любой минорной версии можем добавлять новые версии тех же поставщиков ключей и методов (например, aes_gcm_v2) или новые поставщики ключей и методы. Если мы объявим поставщика ключей или метод устаревшим, при запуске tofu plan или tofu apply в консоли появится предупреждение. Если вы получили такое предупреждение, выполните переход до обновления до следующей версии.
Конфигурация
Настроить шифрование в OpenTofu можно, указав конфигурацию в коде OpenTofu или используя переменную окружения TF_ENCRYPTION. Оба варианта равнозначны. Если использовать оба, OpenTofu объединит конфигурации, переопределив все параметры, заданные в коде, значениями из окружения.
Базовая структура конфигурации выглядит следующим образом:
- Код
- Окружение (оболочка Linux/UNIX)
- Окружение (PowerShell)
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
}
}
}TF_ENCRYPTION=$(cat <<EOF
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
}
EOF)$Env:TF_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
}
"@После шифрования данных не переименовывайте поставщиков ключей и методы в конфигурации! Зашифрованные данные, хранящиеся в бэкенде, содержат метаданные с их именами. Вместо этого используйте блок резервного варианта для обработки изменений поставщиков ключей. Можно также указать уникальный ключ хранения метаданных в поле encrypted_metadata_alias поставщика ключей — это позволит без проблем изменить имя поставщика ключей.
Вместо HCL для конфигурации шифрования можно использовать синтаксис конфигурации JSON.
Если вы используете конфигурацию окружения, добавьте следующую конфигурацию кода, чтобы не допустить записи незашифрованных данных при отсутствии переменной окружения:
terraform {
encryption {
state {
enforced = true
}
plan {
enforced = true
}
}
}Смена ключа и метода
В некоторых случаях может потребоваться изменить конфигурацию шифрования. Это может включать переименование поставщика ключей или метода, изменение парольной фразы поставщика ключей либо переход на другую систему управления ключами. OpenTofu поддерживает автоматическую смену конфигурации шифрования, если указать старую конфигурацию в блоке fallback:
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.
}
}
В конфигурации можно использовать переменные и локальные значения, но они не должны ссылаться на данные состояния или функции, определённые поставщиком. Все значения должны разрешаться во время tofu init, до того как состояние станет доступно.
Отмена шифрования
Как и при первоначальной настройке выше, перейти к незашифрованным файлам состояния и плана также можно с помощью метода 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]для выбора первого источника данных в указанном модуле с указанным именем.
В некоторых случаях имена ключей в разных проектах могут совпадать, и тогда для поставщика ключей в одном проекте потребуется использовать имя, отличное от имени в другом проекте. В этом случае следует использовать параметр encrypted_metadata_alias, чтобы задать фиксированный ключ метаданных и обеспечить работу шифрования.
Например, вы можете создавать сертификаты в проекте «A» и ссылаться на них в проекте «B». В проекте «A» можно настроить следующее:
terraform {
encryption {
key_provider "pbkdf2" "mykey" {
passphrase = "OpenTofu has encryption"
# Note the fixed encrypted_metadata_alias here:
encrypted_metadata_alias = "certificates"
}
method "aes_gcm" "mymethod" {
keys = key_provider.pbkdf2.mykey
}
state {
method = method.aes_gcm.mymethod
}
}
}
resource "tls_private_key" "webserver" {
algorithm = "ED25519"
}
resource "tls_self_signed_cert" "webserver" {
private_key_pem = tls_private_key.webserver.private_key_pem
subject {
common_name = "someserver.opentofu.org"
organization = "OpenTofu"
}
validity_period_hours = 24*365*10
allowed_uses = [
"key_encipherment",
"digital_signature",
"server_auth",
]
}
output "cert_pem" {
value = tls_self_signed_cert.webserver.cert_pem
}Затем на него можно сослаться в проекте «B» следующим образом:
terraform {
encryption {
# Note that the name of the key here is different:
key_provider "pbkdf2" "mykeyrenamed" {
passphrase = "OpenTofu has encryption"
# Note the fixed encrypted_metadata_alias here:
encrypted_metadata_alias = "certificates"
}
method "aes_gcm" "mymethod" {
keys = key_provider.pbkdf2.mykeyrenamed
}
remote_state_data_sources {
default {
method = method.aes_gcm.mymethod
}
}
}
}
data "terraform_remote_state" "cert" {
backend = "local"
config = {
# Refer to the other project here:
path = "../a/terraform.tfstate"
}
}
output "cert" {
# Use data from the other project by referencing it as follows:
value = data.terraform_remote_state.cert.outputs.cert_pem
}Поставщики ключей
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 | Количество байтов для создания ключа. | 1 | 32 |
| iterations | Количество итераций. Рекомендации см. в этом документе. | 200 000 | 600 000 |
| salt_length | Длина соли для получения ключа. | 1 | 32 |
| hash_function | Укажите sha256 или sha512 в качестве хеш-функции. sha1 не поддерживается. |
Н/Д | sha512 |
| encrypted_metadata_alias | Необязательный идентификатор, под которым хранятся метаданные в зашифрованных файлах состояния/плана. Укажите его, чтобы разрешить изменение имени поставщика ключей. | - | производное от имени поставщика ключей |
AWS KMS
Этот поставщик ключей использует службу управления ключами Amazon Web Services для создания ключей. Параметры аутентификации совпадают с параметрами бэкенда S3, за исключением устаревших параметров. Кроме того, укажите следующие параметры:
| Параметр | Описание | Мин. | По умолчанию |
|---|---|---|---|
| kms_key_id | Идентификатор ключа для AWS KMS. | 1 | - |
| key_spec |
Спецификация ключа для AWS KMS. Выберите её в соответствии с методом шифрования (например, AES_256). |
1 | - |
| encrypted_metadata_alias | Необязательный идентификатор, под которым хранятся метаданные в зашифрованных файлах состояния/плана. Укажите его, чтобы разрешить изменение имени поставщика ключей. | - | производное от имени поставщика ключей |
В следующем примере показана минимальная конфигурация:
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. | Н/Д | - |
| key_length (обязательный) | Количество байтов для создания ключа. Должно быть в диапазоне от 1 до 1024 байт. |
1 | - |
| encrypted_metadata_alias | Необязательный идентификатор, под которым хранятся метаданные в зашифрованных файлах состояния/плана. Укажите его, чтобы разрешить изменение имени поставщика ключей. | - | производное от имени поставщика ключей |
В следующем примере показана минимальная конфигурация:
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 для создания ключей данных. Его можно настроить следующим образом:
| Параметр | Описание | Мин. | По умолчанию |
|---|---|---|---|
| key_name (обязательный) | Имя ключа шифрования Transit, используемого для шифрования/расшифровки ключа данных. Предварительно настройте его на сервере OpenBao. | Н/Д | - |
| token |
Токен авторизации, используемый при обращении к API OpenBao. OpenTofu также может считывать его из переменной окружения BAO_TOKEN. |
Н/Д | - |
| address | Адрес сервера OpenBao для доступа к API. OpenTofu также может считывать его из переменной окружения BAO_ADDR. Ваша система должна доверять TLS-сертификату сервера. |
Н/Д | https://127.0.0.1:8200 |
| transit_engine_path | Путь, по которому в OpenBao включён модуль секретов Transit. Измените этот параметр, если вы изменили путь к модулю Transit. | Н/Д | /transit |
| key_length | Количество байтов для создания ключа. Допустимые значения: 16, 32 или 64 байт. |
16 | 32 |
| encrypted_metadata_alias | Необязательный идентификатор, под которым хранятся метаданные в зашифрованных файлах состояния/плана. Укажите его, чтобы разрешить изменение имени поставщика ключей. | - | производное от имени поставщика ключей |
В следующем примере показан возможный вариант конфигурации:
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
}
}
}Поставщик ключей OpenBao пока является экспериментальным, поскольку на момент выпуска OpenTofu стабильная версия OpenBao отсутствовала.
Поставщик ключей OpenBao совместим с последней версией HashiCorp Vault с лицензией MPL (1.14), но не поддерживает последующие версии с лицензией BUSL.
Методы
AES-GCM
В настоящее время поддерживается только метод шифрования AES-GCM. Его можно настроить следующим образом:
terraform {
encryption {
# Key provider configuration here
method "aes_gcm" "yourname" {
keys = key_provider.yourkeyprovider.yourname
}
}
}Для метода AES-GCM нужны ключи длиной 16, 24 или 32 байта. Настройте поставщик ключей так, чтобы он предоставлял ключи именно такой длины.
AES-GCM — это безопасный отраслевой стандарт шифрования, однако он подвержен «насыщению ключа». Для настройки безопасной системы следует использовать либо поставщик ключей, выполняющий выработку ключа (например, PBKDF2) с длинной и сложной парольной фразой, либо систему управления ключами, которая регулярно выполняет автоматическую ротацию ключей. Использование коротких статических ключей снижает надёжность шифрования.
Без шифрования
Метод unencrypted используется для явного перехода к шифрованию и обратно. Он не требует настройки; пример его использования приведён выше в блоке «Первоначальная настройка».
Copyright (c) The OpenTofu Authors
Copyright (c) 2014 HashiCorp, Inc.
Mozilla Public License, version 2.0
https://opentofu.org/docs/v1.9/language/state/encryption/