# sensitive в Terraform: про логи, не про шифрование _Security · TerraformLab Knowledge Base_ **TL;DR:** `sensitive = true` у variable/output/local прячет значение от CLI-вывода и логов. В state и .tfstate.backup значение **открытое**. Это redaction, не encryption. Реально секретное, храни в Secrets Manager или Vault, читай через data-source, плана не сохраняй в артефактах. `nonsensitive()` нужен в редких случаях когда sensitive-флаг каскадируется и мешает. ## Что делает sensitive Помеченный `sensitive = true` объект: 1. **Не показывается в CLI**. `terraform plan` пишет `(sensitive value)` вместо реального значения. 2. **Не показывается в `terraform output`**. Чтобы увидеть, нужен `-raw` или `-json` явно (warning'ом). 3. **Каскадируется**. Если в local'е есть sensitive-значение, весь local становится sensitive. Это «заражение» защищает от случайных протечек через `output local.something`. Что **НЕ делает**: - **Не шифрует state.** В `terraform.tfstate` значение лежит открыто. Прочитать может любой с доступом к файлу. - **Не шифрует backup.** `*.tfstate.backup` после каждой apply, то же самое. - **Не маскирует в plan-файле.** `plan.tfplan`, бинарь, но `terraform show plan.tfplan` развернёт его и покажет всё. - **Не маскирует в `TF_LOG=DEBUG`.** Debug-лог показывает HTTP-запросы, включая payloads, там видно всё, что отправляется провайдеру. Sensitive, это **udit-defence**. Защита от случайного `terraform output | paste-in-slack`. Не защита от инсайдера, имеющего доступ к state. ## Объявление Variable: ```hcl variable "db_password" { type = string sensitive = true } ``` Output: ```hcl output "connection_string" { value = "postgres://user:${var.db_password}@${aws_db_instance.main.address}/db" sensitive = true } ``` Если `value` в output упоминает sensitive-источник, но `sensitive = true` не указан, Terraform упадёт с ошибкой «output … depends on sensitive value, set sensitive = true». Это страховка. Local: ```hcl locals { full_url = "https://api.example.com/${var.api_token}" # full_url автоматически sensitive, заразился от api_token } ``` Resource attribute (некоторые провайдеры так помечают атрибуты, это уже schema-уровень): ```hcl resource "random_password" "user" { length = 24 } # random_password.user.result, auto-sensitive (so документация и провайдер) ``` ## nonsensitive() когда стрелка перепрыгнула Иногда нужно достать публично-частную часть из sensitive-значения. Например, `aws_db_instance.main.endpoint` сам не sensitive, но из-за того что build'ится через `format(..., aws_db_instance.main.address, ...)` весь результат становится sensitive, и Terraform отказывается это отдавать в plan-выводе. ```hcl output "host" { value = nonsensitive(aws_db_instance.main.address) } ``` `nonsensitive(x)` снимает флаг. Используй **сознательно**, это explicit ack «знаю что делаю». Никогда не оборачивай в nonsensitive то, что реально секретное. ## Sensitive в plan-файле ```bash terraform plan -out=plan.tfplan terraform show plan.tfplan ``` `show plan.tfplan` показывает **всё** что в плане, включая значения sensitive-полей и переменных. Если ты экспортируешь `plan.tfplan` как CI-артефакт между jobs, он содержит секреты. Это значит: - **Не хранить plan.tfplan дольше нужного.** В GitHub Actions `actions/upload-artifact` имеет retention; ставь минимум, удаляй после apply. - **Не публиковать plan-файлы.** Даже в private-репо, кому-то может быть видно по правам. - **Шифруй артефакт при передаче.** Если pipeline сложный, sops, age, или нативные artifact-secrets провайдера. См. [tf-plan-apply-ci](/terraform/kb/tf-plan-apply-ci.md) и [tf-secrets-in-state](/terraform/kb/tf-secrets-in-state.md). ## Реально секретное, не в variable Антипаттерн: ```hcl variable "db_password" { sensitive = true default = "supersecret" # НИКОГДА } ``` Дефолт в HCL = в git = read-only-доступ к репо = доступ к паролю. Это не «secret in variable», это «secret in code». Правильный паттерн, читать секрет из менеджера на каждом apply: ```hcl data "aws_secretsmanager_secret_version" "db" { secret_id = "prod/db/master" } resource "aws_db_instance" "main" { # ... password = data.aws_secretsmanager_secret_version.db.secret_string } ``` Секрет ротируется в Secrets Manager, Terraform на следующем apply заберёт новый. См. [tf-secrets-in-state](/terraform/kb/tf-secrets-in-state.md). ## Какие атрибуты провайдеры помечают sensitive автоматически | Resource/Data | Атрибут | Помечен sensitive | |---|---|---| | `random_password` | `result` | да | | `tls_private_key` | `private_key_pem` | да | | `aws_db_instance` | `password` | да (если set) | | `aws_secretsmanager_secret_version` | `secret_string` | да | | `aws_iam_access_key` | `secret`, `ses_smtp_password_v4` | да | | `aws_kms_data_key` | `plaintext`, `ciphertext_blob` | оба | Это не полный список, открой docs провайдера, ищи иконку 🔒 или метку «(sensitive)». ## Подводные камни - **State не шифруется sensitive-флагом.** Любой с доступом к `terraform.tfstate` видит все sensitive-значения. Шифрование, задача backend'а: S3 SSE, encryption_at_rest у Terraform Cloud. - **TF_LOG=DEBUG течёт.** Логи провайдера показывают HTTP-payloads. Никогда не публикуй DEBUG-логи в issue/Slack, там сырые секреты. - **`sensitive = true` нельзя поменять без replace.** Из non-sensitive в sensitive, ОК, Terraform проглотит. В обратную сторону, ему нужно оценить, не утечёт ли значение через output, и часто он требует apply на промежуточном шаге. - **На output без sensitive ошибка cryptic.** Если output упоминает sensitive-значение, забыл `sensitive = true`, будет «output value … is sensitive but the sensitive marker is not set». Это reminder добавить флаг, не баг. - **Sensitive не работает на провайдер-конфиг.** `provider "aws" { access_key = var.x }`, Terraform НЕ умеет sensitive-маркировать сам provider конфиг. Передавай через env (`AWS_ACCESS_KEY_ID`), не через переменную. - **`nonsensitive()` отключает защиту полностью.** Не вставляй «на всякий случай чтобы plan читался», потеряешь redaction там, где он был нужен. ## Команды ```bash terraform output -json ``` Sensitive-значения видны (явный opt-in). Warning в stderr. ```bash terraform output -raw db_password ``` Print raw value. Single output только. ```bash terraform show -json terraform.tfstate | jq '.values.outputs' ``` Все outputs из state, включая sensitive-значения. ```bash terraform plan -out=plan.tfplan && terraform show plan.tfplan ``` Plan-файл показывает sensitive: обращайся с ним как с секретом. ## См. также - [Секреты и Terraform state: где хранить и как читать](/terraform/kb/tf-secrets-in-state.md) - [Блок variable: вход в конфигурацию](/terraform/kb/tf-variable.md) - [Блок output: что Terraform возвращает наружу](/terraform/kb/tf-output.md) - [locals: вычисляемые внутренние имена](/terraform/kb/tf-locals.md) - [Checkov: статический анализ HCL](/terraform/kb/tf-checkov.md)