Что делает sensitive
Помеченный sensitive = true объект:
- Не показывается в CLI.
terraform planпишет(sensitive value)вместо реального значения. - Не показывается в
terraform output. Чтобы увидеть, нужен-rawили-jsonявно (warning'ом). - Каскадируется. Если в 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:
variable "db_password" {type = string
sensitive = true
}
Output:
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:
locals { full_url = "https://api.example.com/${var.api_token}"# full_url автоматически sensitive, заразился от api_token
}
Resource attribute (некоторые провайдеры так помечают атрибуты, это уже schema-уровень):
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-выводе.
output "host" {value = nonsensitive(aws_db_instance.main.address)
}
nonsensitive(x) снимает флаг. Используй сознательно, это explicit
ack «знаю что делаю». Никогда не оборачивай в nonsensitive то, что реально
секретное.
Sensitive в plan-файле
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 и tf-secrets-in-state.
Реально секретное, не в variable
Антипаттерн:
variable "db_password" {sensitive = true
default = "supersecret" # НИКОГДА
}
Дефолт в HCL = в git = read-only-доступ к репо = доступ к паролю. Это не «secret in variable», это «secret in code».
Правильный паттерн, читать секрет из менеджера на каждом apply:
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.
Какие атрибуты провайдеры помечают 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 там, где он был нужен.