ADG Оставить заявку
Блог DevOps 5 мин чтения

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 и считать вопрос закрытым.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.