Terraform 0.15: sensitive outputs, предупреждения об устаревшем CLI и финальный шаг перед 1.0
Terraform 0.15 вводит sensitive outputs, которые закрывают утечку паролей в plain text в выводе. Показываем связку с Vault Dynamic Secrets на примере PostgreSQL.
Terraform 0.15 вводит sensitive outputs и deprecation CLI warnings - финальный релиз перед 1.0
Terraform 0.15 вышел на прошлой неделе, и у него интересный статус: это последний релиз перед 1.0, то есть HashiCorp публично говорит «мы готовы к стабильному API». Перемен много, но одна из них закрывает дыру, которая нас раздражала давно. Речь про sensitive для выходных переменных.
Что было не так с outputs
Terraform умеет выводить значения ресурсов через output. Удобно - берёшь IP, endpoint базы данных, ARN ресурса и передаёшь дальше. Проблема в том, что до 0.15 terraform output и terraform apply честно печатали в терминал всё что там есть - включая пароли, токены, connection strings с кредами.
Сценарий, который мы видели у нескольких клиентов в рамках managed-сопровождения: Terraform создаёт RDS-инстанс или Managed PostgreSQL, генерирует пароль через random_password, кладёт его в output для передачи в следующий модуль - и этот пароль печатается в stdout CI/CD пайплайна. Логи пайплайна доступны всем у кого есть доступ к GitLab/Jenkins. Кто-то скринит логи для отладки, пересылает в чат. Пароль уже не секрет.
В state-файле это вообще отдельная история: Terraform хранит значения outputs в .tfstate в plain text всегда - это архитектурная особенность. Но хотя бы не печатать их в терминал было бы хорошим шагом.
Что делает sensitive = true
В 0.15 у output появился атрибут sensitive:
output "db_password" {
value = random_password.postgres.result
sensitive = true
}
При terraform apply и terraform output такое значение маскируется:
Outputs:
db_password = <sensitive>
Если явно запросить значение - оно доступно:
terraform output -raw db_password
Но в общем выводе и в логах - маска. Небольшое улучшение, но реальное.
Плюс теперь Terraform умеет автоматически промечать output как sensitive если значение пришло из sensitive-переменной или ресурса с пометкой. Это работает транзитивно: если ресурс random_password возвращает sensitive-атрибут, то output, использующий это значение, автоматически становится sensitive даже без явного sensitive = true. В ряде кейсов это ломает существующие конфиги - об этом чуть ниже.
Связка с Vault Dynamic Secrets
Маскировка в терминале - хорошо, но настоящее решение проблемы паролей в Terraform - это не хранить пароли в state вообще. Для этого есть Vault Dynamic Secrets.
Схема на примере PostgreSQL: Vault настраивается как secrets engine для базы данных, и вместо того чтобы Terraform создавал статический пароль, он запрашивает у Vault роль - а Vault сам создаёт временного пользователя с ограниченным TTL прямо в PostgreSQL.
Terraform-конфиг для такой связки:
terraform {
required_providers {
vault = {
source = "hashicorp/vault"
version = "~> 2.19"
}
}
}
# Настраиваем database secrets engine в Vault
resource "vault_database_secret_backend_connection" "postgres" {
backend = "database"
name = "postgres-prod"
allowed_roles = ["app-role"]
postgresql {
connection_url = "postgresql://{{username}}:{{password}}@${var.db_host}:5432/appdb"
}
}
# Роль с TTL
resource "vault_database_secret_backend_role" "app" {
backend = "database"
name = "app-role"
db_name = vault_database_secret_backend_connection.postgres.name
default_ttl = "1h"
max_ttl = "24h"
creation_statements = [
"CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'",
"GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO \"{{name}}\"",
]
}
# Output - отдаём путь к секрету, а не сам секрет
output "vault_db_role_path" {
value = "database/creds/${vault_database_secret_backend_role.app.name}"
}
Принципиальное отличие: приложение при старте само идёт в Vault и получает временные кредентиалы. Terraform знает только путь к роли Vault, не сам пароль. В state-файле нет никаких паролей PostgreSQL.
Если для какого-то инструмента всё же нужно достать кредентиал через Terraform output - тут и пригождается sensitive = true как минимальная защита.
Breaking changes в 0.15
Раз уж 0.15 - финальная подготовка к 1.0, HashiCorp пошёл на несколько breaking changes, о которых предупреждали с 0.13-0.14.
Устаревшие CLI-флаги теперь дают ошибку. Флаги вроде -var-file в старом формате, некоторые комбинации в terraform state - всё что давало deprecation warning раньше, теперь валится с ошибкой. Если CI/CD скрипты не обновлялись с 0.12-0.13 - при апгрейде они могут сломаться.
Sensitive-атрибуты теперь транзитивные, и это может сломать существующие outputs. Если у вас был output, который брал значение из ресурса с sensitive-атрибутом, и вы его использовали без sensitive = true - Terraform теперь требует явно прописать sensitive = true или убрать sensitive-значение из output. Это правильно по смыслу, но ломает конфиги которые «работали» раньше.
Removed providers. Несколько провайдеров окончательно вышли из «hashicorp» namespace и должны использоваться через новые source-адреса. В 0.14 это давало warning, в 0.15 - ошибку при init.
Мы прогнали апгрейд на паре тестовых проектов. В обоих случаях понадобилось поправить два-три outputs и один CI-скрипт. Ничего катастрофического, но нулевых изменений не было.
Где сейчас
На production-проектах клиентов пока сидим на 0.14. Апгрейд до 0.15 запланирован как плановое обновление в ближайшие недели - breaking changes понятны, непредсказуемого нет.
Vault Dynamic Secrets на PostgreSQL у двух клиентов уже работает в production - не как следствие 0.15, а потому что к этому шли отдельно. Связка надёжная: временные кредентиалы с TTL, ротация прозрачная для приложения, в state-файле паролей нет. Возможность пометить outputs как sensitive - это скорее дополнительный барьер для проектов, где Dynamic Secrets ещё не добрались до внедрения.
Если вы используете Terraform для создания managed-баз данных и передаёте пароли через outputs - 0.15 хороший момент чтобы разобраться с этой схемой правильно, а не просто поставить sensitive = true и считать вопрос закрытым.
- Terraform 0.14: lock-файл для провайдеров и конец истории с расходящимися планами · 28 января 2021
- Helm 3 OCI Registry: храним чарты там же, где образы · 18 марта 2021