В этом уроке мы не будем настраивать конкретный менеджер секретов - это будет дальше. Сейчас задача шире: понять, какие секреты вообще живут в инфра-коде, по каким правилам с ними обращаться и почему даже идеально написанный конфиг не спасает от утечки через state. Это фундамент, без которого любой инструмент - просто кнопка, на которую ты жмёшь, не понимая зачем.
Какие секреты живут в инфраструктуре
Когда говорят "секрет", новичок обычно думает про пароль от базы. На деле в реальном проекте секретов целый зоопарк, и каждый тип утекает по-своему:
- Пароли БД - классика. PostgreSQL, MySQL, Redis с паролем - всё это надо где-то хранить и как-то передавать в ресурсы.
- API-ключи - доступ к внешним сервисам: платёжки, почтовые рассыльщики, S3-совместимые хранилища, мониторинг. Утёкший ключ к платёжке - это уже не "ой", это деньги.
- Токены провайдера - тот самый OAuth-токен или сервисный ключ, которым terraform yandex cloud аутентифицируется в облаке. Самый жирный секрет: с ним можно поднять и снести вообще всю твою инфраструктуру.
- TLS-сертификаты и приватные ключи - публичный сертификат не секрет, а вот приватный ключ к нему - очень даже. Перепутать эти две сущности - популярная ошибка.
- SSH-ключи - приватные ключи для доступа на виртуалки. Публичную часть спокойно кладёшь в metadata инстанса, приватную - бережёшь как зеницу ока.

Три вопроса, на которые надо ответить про каждый секрет
Любую схему работы с секретами в iac удобно раскладывать на три вопроса. Если ты честно ответил на все три - у тебя есть стратегия, а не "как-нибудь спрячем".
1. Как хранить? Варианты по нарастанию зрелости: переменные окружения, отдельный незакоммиченный файл, и наконец менеджер секретов (HashiCorp Vault, Yandex Lockbox, облачные KMS). Файл проще, менеджер - правильнее, потому что он даёт версионирование, аудит и централизованный отзыв.
2. Как считывать? Секрет должен попадать в конфигурацию в момент исполнения, а не лежать в исходниках. Это либо передача через переменные (var с пометкой sensitive), либо чтение из менеджера через data-источник. Идея одна: код описывает, ОТКУДА взять секрет, но не сам секрет.
3. Кому доступ? Самый недооценённый вопрос. Мало спрятать секрет - надо ответить, кто и что может с ним сделать. Доступ к хранилищу секретов и доступ к state-файлу - это два отдельных контура, и оба надо ограничивать по принципу минимальных привилегий.
Антипаттерн номер один: секрет в коде и в git
Повторим жирным шрифтом, потому что это самая частая и самая дорогая ошибка: не клади секреты в исходный код и тем более не коммить их в git. Вот как НЕ надо:
Код: Выделить всё
# ПЛОХО. Так делать нельзя.
resource "yandex_mdb_postgresql_user" "app" {
cluster_id = yandex_mdb_postgresql_cluster.main.id
name = "app_user"
password = "Qwerty12345" # секрет открытым текстом, да ещё и в git
}
Как надо хотя бы на минимальном уровне - вынести секрет во входную переменную и пометить её чувствительной:
Код: Выделить всё
variable "db_password" {
type = string
sensitive = true # terraform не покажет значение в выводе plan/apply
}
resource "yandex_mdb_postgresql_user" "app" {
cluster_id = yandex_mdb_postgresql_cluster.main.id
name = "app_user"
password = var.db_password
}
Секреты всё равно попадают в state
Вот мысль, которую новички пропускают, а потом ловят инцидент. terraform state хранит реальные значения всех ресурсов, включая секретные. Пароль, который ты аккуратно передал через sensitive-переменную, в state-файле лежит открытым текстом. Никакого волшебного шифрования "из коробки" в обычном terraform нет: state - это просто JSON, и пароль БД в нём как на ладони.
Логика проста: чтобы построить план изменений, terraform должен знать текущее состояние мира. А пароль - это часть состояния. Поэтому он там и оседает. Sensitive влияет только на ВЫВОД в консоль, файл состояния это не трогает.
Отсюда два железных следствия, которые надо запомнить навсегда:
- Шифруй state. Храни его в backend, который шифрует данные на стороне сервера. Для terraform yandex cloud это бакет Object Storage с включённым шифрованием через KMS. Отдельно отмечу: OpenTofu (открытый форк, на 2026 год актуальная ветка 1.12.x) умеет шифровать state прямо на клиенте, до отправки в backend - функция называется state encryption и в самом terraform её до сих пор нет. Это один из весомых аргументов в пользу opentofu, если безопасность state для тебя приоритет.
- Ограничивай доступ к state. Бакет с состоянием - это не место для read-доступа всей команды. Кто читает state, тот читает все пароли проекта. Доступ только сервисному аккаунту CI и паре ответственных людей.
Код: Выделить всё
terraform {
backend "s3" {
endpoints = {
s3 = "https://storage.yandexcloud.net"
}
bucket = "my-tfstate"
key = "prod/terraform.tfstate"
region = "ru-central1"
skip_region_validation = true
skip_credentials_validation = true
}
}
Обзор подходов: куда мы движемся
Если разложить все способы по лесенке зрелости, получится примерно так:
- Уровень 0. Секрет в коде. Это не уровень, это инцидент. Забудь.
- Уровень 1. Sensitive-переменные плюс передача через окружение или CI. Уже прилично для пет-проекта, но секрет всё ещё руками передаётся и оседает в state.
- Уровень 2. Менеджер секретов. Секреты лежат в специализированном хранилище, terraform читает их через data-источник в момент apply, доступ рулится IAM-политиками, есть аудит и версии.
Код: Выделить всё
data "yandex_lockbox_secret_version" "db" {
secret_id = "e6q..." # идентификатор секрета в Lockbox
}
locals {
db_password = [
for entry in data.yandex_lockbox_secret_version.db.entries :
entry.text_value if entry.key == "password"
][0]
}
Типичные грабли
- Думать, что sensitive = true шифрует секрет. Нет. Он только прячет значение в консоли. В state - открытый текст.
- Закоммитить terraform.tfstate в git. Звучит дико, но в публичных репозиториях такого добра полно. State всегда в .gitignore, всегда в удалённом backend.
- Положить terraform.tfvars с реальными секретами в репозиторий. Файл tfvars - такой же исходник. Реальные значения - только в .tfvars.local, который в .gitignore, или в окружении.
- Путать публичный и приватный ключ. Публичную часть SSH-ключа кладёшь в metadata инстанса спокойно. Приватную - никогда в код.
- Раздать read на state-бакет всей команде. Это равносильно тому, чтобы раздать все пароли проекта. Доступ к state = доступ к секретам.
Руками, без облака, минут на пятнадцать:
- Заведи переменную db_password с sensitive = true и любой ресурс, который её использует (можно фейковый local_file для теста).
- Передай значение через export TF_VAR_db_password=... и выполни terraform plan apply. Убедись, что в выводе вместо значения стоит (sensitive value).
- Теперь открой terraform.tfstate любым редактором и найди свой пароль открытым текстом. Прочувствуй этот момент - это и есть главный урок про state.
- Добавь terraform.tfstate и *.tfvars в .gitignore. Проверь через git status, что они не отслеживаются.
- Назови минимум пять типов секретов, которые встречаются в инфра-коде.
- Что на самом деле делает sensitive = true, а чего НЕ делает?
- Почему секреты оказываются в terraform state и какие два правила из этого следуют?
- Чем чтение секрета из менеджера (Lockbox) через data-источник лучше передачи через переменную окружения?
Секрет в коде и в git - это инцидент, а не стиль. Любой секрет проходит через три вопроса: как хранить, как считывать, кому доступ. Флаг sensitive прячет значение только в консоли, но не в state - а в state секреты лежат открытым текстом всегда, поэтому state обязательно шифруют и жёстко ограничивают к нему доступ. Лесенка зрелости ведёт от переменных окружения к менеджеру секретов, и для Yandex Cloud этот менеджер - Lockbox, которым мы займёмся вплотную дальше.