Один из самых частых вопросов, которые нам задают по поводу использования Terraform для управления инфраструктурой как кодом, касается того, как обрабатывать такие секреты, как пароли, ключи API и другие конфиденциальные данные. Например, вот фрагмент кода Terraform, который можно использовать для развертывания MySQL с помощью Amazon RDS:
Обратите внимание, что Terraform требует от вас настройки двух секретов — имени пользователя и пароля, которые являются учетными данными для основного пользователя базы данных. В этой статье я рассмотрю наиболее распространенные методы, которые можно использовать для безопасного и надежного управления такими секретами:
- Предварительное условие №1: Не храните секреты в виде простого текста
- Предварительное условие №2: Обеспечьте безопасность состояния Terraform
- Метод №1: Переменные окружения
- Метод №2: Зашифрованные файлы (например, KMS, PGP, SOPS)
- Метод №3: Хранилища секретов (например, Vault, AWS Secrets Manager)
Предварительное условие №1: Не храните секреты в виде простого текста
Первое правило управления секретами гласит:
Не храните секреты в виде простого текста.
Второе правило управления секретами гласит:
НЕ ХРАНИТЕ СЕКРЕТЫ В ВИДЕ ПРОСТОГО ТЕКСТА.
Серьезно, не делайте этого. Например, НЕ пишите учетные данные вашей базы данных прямо в коде Terraform и не добавляйте их в систему контроля версий:
Хранение секретов в виде простого текста в системе контроля версий — ПЛОХАЯ ИДЕЯ. Вот лишь несколько причин почему:
- Любой, у кого есть доступ к системе контроля версий, имеет доступ к этому секрету. В примере выше каждый разработчик в вашей компании имеет доступ к основным учетным данным для вашей базы данных.
- Каждый компьютер, имеющий доступ к системе контроля версий, хранит копию этого секрета. На каждом компьютере, с которого когда-либо клонировался этот репозиторий, все еще может оставаться копия этого секрета на локальном жестком диске. Сюда входят компьютеры каждого разработчика в вашей команде, каждый компьютер, участвующий в CI (например, Jenkins, CircleCI, GitLab и т. д.), каждый компьютер, участвующий в управлении версиями (например, GitHub, GitLab, Bitbucket), каждый компьютер, участвующий в развертывании (например, все ваши предпродакшн- и продакшн-окружения), каждый компьютер, участвующий в резервном копировании (например, CrashPlan, Time Machine и т. д.), и так далее.
- Каждая выполняемая вами программа имеет доступ к этому секрету. Поскольку секреты хранятся в виде простого текста на стольких жестких дисках, любая программа, работающая на любом из этих компьютеров, потенциально может прочитать этот секрет.
- Отсутствует возможность аудита или отзыва доступа к этому секрету. Когда секреты хранятся на сотнях жестких дисков в виде простого текста, у вас нет возможности узнать, кто к ним обращался (нет журнала аудита), и нет способа отозвать доступ.
Короче говоря, если вы храните секреты в виде простого текста, вы предоставляете злоумышленникам (например, хакерам, конкурентам, недовольным бывшим сотрудникам) бесчисленные способы получить доступ к самым конфиденциальным данным вашей компании — например, путем компрометации системы контроля версий, любого из используемых вами компьютеров или любой программы на этих компьютерах и т. д. — и вы даже не узнаете, была ли совершена атака, и у вас не будет простого способа исправить ситуацию.
Поэтому я настоятельно рекомендую всегда хранить секреты в зашифрованном виде — и это относится ко всем секретам, а не только к тем, которые используются в Terraform! Далее в этой статье я расскажу о нескольких различных методах шифрования и дешифрования таких секретов.
Предварительное условие №2: Обеспечьте безопасность состояния Terraform
Будем надеяться, что предыдущий раздел убедил вас не хранить секреты в виде простого текста, а в последующих разделах будут показаны некоторые методы их шифрования. Однако независимо от того, какой метод вы используете для шифрования секретов на своей стороне, останется одно место, где они все равно окажутся в виде простого текста: состояние Terraform.
Каждый раз, когда вы развертываете инфраструктуру с помощью Terraform, в файл состояния записывается множество данных об этой инфраструктуре, включая все переданные вами параметры. По умолчанию это файл terraform.tfstate, который автоматически создается в папке, где вы выполнили команду terraform apply. Так что даже если вы используете один из упомянутых далее методов для безопасной передачи секретов, например учетных данных для базы данных:
Эти секреты все равно попадут в terraform.tfstate в виде простого текста! Эта проблема остается открытой уже более 6 лет, и пока нет четких планов по созданию полноценного встроенного решения. Существуют обходные пути, которые могут удалять секреты из файлов состояния, но они ненадежны и могут сломаться при любом новом релизе Terraform, поэтому я их не рекомендую.
В настоящее время, независимо от того, какой из обсуждаемых ниже методов вы в итоге выберете для управления секретами, вы также должны:
- Хранить состояние Terraform в бэкенде, поддерживающем шифрование. Вместо хранения состояния в локальном файле terraform.tfstate, Terraform нативно поддерживает различные backends, такие как S3, GCS и Azure Blob Storage. Многие из этих бэкендов поддерживают шифрование, поэтому файлы вашего состояния будут всегда зашифрованы — как при передаче (например, по протоколу TLS), так и на диске (например, с помощью AES-256). Большинство бэкендов также поддерживают функции совместной работы (например, автоматическую отправку и получение состояния; блокировку), поэтому использование бэкенда является обязательным как с точки зрения безопасности, так и с точки зрения командной работы. Дополнительную информацию см. в разделе «Как управлять состоянием Terraform».
- Строго контролировать доступ к вашему бэкенду Terraform. Поскольку файлы состояния Terraform могут содержать секреты, вам нужно тщательно контролировать, кто имеет доступ к бэкенду, используемому для хранения файлов состояния. Например, если вы используете S3 в качестве бэкенда, вам следует настроить политику IAM, которая предоставляет доступ к бакету S3 для продакшн-окружения только небольшой группе доверенных разработчиков (или, возможно, исключительно серверу CI, который вы используете для развертывания в продакшн).
А теперь, без лишних слов, давайте обсудим различные доступные вам методы управления секретами с помощью Terraform.
Метод №1: Переменные окружения
Этот первый метод защищает ваш код от секретов в виде простого текста благодаря встроенной в Terraform поддержке чтения переменных окружения.
Обзор
Чтобы использовать этот метод, объявите переменные для секретов, которые вы хотите передать:
Обновление от 3 декабря 2020 г.: в Terraform 0.14 появилась возможность помечать переменные как конфиденциальные (sensitive), что помогает не допускать их попадания в логи, поэтому вам следует добавить sensitive = true для обеих переменных выше!
Затем передайте переменные ресурсам Terraform, которым эти секреты необходимы:
Теперь вы можете передать значение для каждой переменной foo, установив переменную окружения с именем TF_VAR_foo. Например, вот как вы можете установить имя пользователя и пароль через окружение в Linux, Unix или Mac и запустить terraform apply для развертывания базы данных:
(Полезный совет: если у вас правильно установлена переменная окружения в терминале Bash, любая команда, начинающаяся с пробела, не будет сохраняться в истории Bash. Используйте это при установке переменных окружения с секретами, чтобы такие секреты не сохранялись на диске.)
Эта методика помогает избежать хранения секретов в виде простого текста в вашем коде, но оставляет открытым вопрос о том, как на самом деле безопасно хранить секреты и управлять ими. Так что в каком-то смысле эта методика лишь откладывает проблему на потом, в то время как другие подходы, описанные далее в этой статье, более регламентированы.
Тем не менее, чтобы не оставлять вас в полной неопределенности, если вы все же выбираете переменные окружения, самым распространенным решением для хранения секретов и управления ими является использование менеджера паролей, такого как:
- 1Password: SaaS-инструмент, который предлагает (а) настольные и мобильные приложения для всех платформ и (б) облачную синхронизацию, позволяющую получать доступ к секретам на всех устройствах и работать в команде.
- LastPass: SaaS-инструмент, который предлагает (а) настольные и мобильные приложения для всех платформ и (б) облачную синхронизацию, позволяющую получать доступ к секретам на всех устройствах и работать в команде.
- pass: Инструмент с открытым исходным кодом, который следует философии Unix: это инструмент командной строки, он осуществляет ввод и вывод через текстовые потоки (т. е. stdin, stdout), а «под капотом» все хранит в виде файлов, причем каждый секрет находится в своем собственный файле, зашифрованном с помощью PGP (вы можете фиксировать эти зашифрованные файлы в системе контроля версий для командной работы).
Эти инструменты решают проблему «откладывания на потом», полагаясь на человеческую память: то есть на вашу способность запомнить пароль, который дает вам доступ к менеджеру паролей.
Пример использования pass
Давайте рассмотрим быстрый пример использования pass. Сначала вам нужно сохранить свои секреты с помощью команды pass insert:
Вы можете вывести секрет в stdout, выполнив команду pass <secret>:
Вы можете использовать эту функциональность в подпанели (subshell) для установки секретов в качестве переменных окружения, а затем вызвать terraform apply:
Преимущества этого метода
- Секреты в виде простого текста не попадают в ваш код и систему контроля версий.
- Простое решение для начала работы.
- Интегрируется с большинством других решений для управления секретами: например, если в вашей компании уже есть способ управления секретами, вы, как правило, сможете заставить его работать с переменными окружения.
- Удобство тестирования: при написании тестов для вашего кода Terraform (например, с помощью Terratest) не нужно настраивать зависимости (например, хранилища секретов), так как вы можете легко установить переменные окружения в качестве фиктивных значений.
Недостатки этого метода
- Не все определено в самом коде Terraform. Это усложняет понимание и поддержку кода.
- Каждый, кто использует ваш код, должен знать о необходимости выполнения дополнительных шагов: либо вручную установить эти переменные окружения, либо запустить вспомогательный скрипт.
- Никаких гарантий или мнений относительно безопасности. Поскольку все управление секретами происходит вне Terraform, код не накладывает никаких требований безопасности, и вполне возможно, что кто-то все еще управляет секретами небезопасным способом (например, хранит их в виде простого текста).
Метод №2: Зашифрованные файлы (например, KMS, PGP, SOPS)
Второй метод заключается в шифровании секретов, сохранении шифротекста в файл и добавлении этого файла в систему контроля версий.
Обзор
Чтобы зашифровать какие-то данные, например секреты в файле, вам нужен ключ шифрования. Этот ключ сам по себе является секретом! Это создает некоторую загадку: как безопасно хранить этот ключ? Вы не можете добавить ключ в систему контроля версий в виде простого текста, так как тогда нет смысла что-либо им шифровать. Вы можете зашифровать ключ другим ключом, но тогда вам придется выяснить, где хранить этот второй ключ. Так что вы возвращаетесь к проблеме «откладывания на потом», поскольку вам все равно придется найти безопасный способ хранения ключа шифрования.
Наиболее распространенным решением этой загадки является хранение ключа в службе ключей, предоставляемой вашим облачным провайдером, такой как:
Эти службы ключей решают проблему «откладывания на потом», полагаясь на человеческую память: в данном случае на вашу способность запомнить пароль, который дает вам доступ к вашему облачному провайдеру (или, возможно, вы сохраняете этот пароль в менеджере паролей и вместо этого запоминаете пароль от него).
Пример использования AWS KMS
Вот пример того, как вы можете использовать ключ, управляемый AWS KMS, для шифрования секретов. Сначала создайте файл с именем db-creds.yml, содержащий ваши секреты:
Примечание: НЕ добавляйте этот файл в систему контроля версий!
Затем зашифруйте этот файл с помощью команды aws kms encrypt и запишите полученный шифротекст в файл db-creds.yml.encrypted:
Теперь вы можете безопасно добавлять db-creds.yml.encrypted в систему контроля версий.
Чтобы расшифровать секреты из этого файла в коде Terraform, вы можете использовать источник данных aws_kms_secrets (для GCP KMS или Azure Key Vault вы вместо этого будете использовать источники данных google_kms_secret или azurerm_key_vault_secret соответственно):
Приведенный выше код прочитает db-creds.yml.encrypted с диска и, при условии наличия прав на доступ к соответствующему ключу в KMS, расшифрует содержимое для получения исходного YAML. Вы можете разобрать YAML следующим образом:
И теперь вы можете прочитать имя пользователя и пароль из этого YAML и передать их ресурсу aws_db_instance:
Одна из трудностей такого подхода заключается в том, что работать с зашифрованными файлами неудобно. Чтобы внести изменения, вам нужно локально расшифровать файл с помощью длинной команды aws kms decrypt, внести некоторые изменения, снова зашифровать файл с помощью другой длинной команды aws kms encrypt, и все это время быть чрезвычайно осторожным, чтобы случайно не зафиксировать открытый текст данных в системе контроля версий и не оставить его навсегда на своем компьютере. Это утомительный и подверженный ошибкам процесс, если вы не используете такой инструмент, как sops.
Пример использования AWS KMS с sops и Terragrunt
sops — это инструмент с открытым исходным кодом, созданный для того, чтобы упростить редактирование и работу с файлами, зашифрованными с помощью AWS KMS, GCP KMS, Azure Key Vault или PGP. sops может автоматически расшифровывать файл при открытии его в текстовом редакторе, поэтому вы можете редактировать файл в виде простого текста, а при сохранении этих файлов он автоматически шифрует содержимое обратно. Это избавляет от необходимости запускать длинные команды aws kms для шифрования или дешифрования данных или беспокоиться о том, что вы случайно зафиксируете секреты в виде простого текста в системе контроля версий. Вот .gif-анимация, показывающая sops в действии:
Terraform пока не имеет встроенной поддержки расшифрования файлов в формате, используемом sops. Одним из решений является установка и использование пользовательского провайдера для sops, terraform-provider-sops. Другой вариант, который я продемонстрирую здесь, — это использование Terragrunt, в который встроена нативная поддержка sops. Terragrunt — это тонкая обертка над Terraform, которая помогает удерживать ваш код Terraform DRY и поддерживаемым в актуальном состоянии (ознакомьтесь с руководством по быстрому старту для получения общего представления).
Допустим, вы использовали sops для создания зашифрованного файла YAML с именем db-creds.yml, как показано на .gif-анимации выше. Теперь в вашей конфигурации terragrunt.hcl вы можете использовать встроенную в Terragrunt функцию sops_decrypt_file для расшифрования этого файла и yamldecode для его синтаксического анализа как YAML:
Затем вы можете передать имя пользователя и пароль в качестве входных данных в ваш код Terraform:
Ваш код Terraform, в свою очередь, может читать эти входные данные через переменные:
Обновление от 3 декабря 2020 г.: в Terraform 0.14 появилась возможность помечать переменные как конфиденциальные (sensitive), что помогает не допускать их попадания в логи, поэтому вам следует добавить sensitive = true для обеих переменных выше!
И передайте эти переменные в aws_db_instance:
Преимущества этого метода
- Храните секреты в виде обычного текста отдельно от вашего кода и системы контроля версий.
- Ваши секреты хранятся в зашифрованном формате в системе контроля версий, поэтому они версионируются, упаковываются и тестируются вместе с остальным вашим кодом. Это помогает уменьшить количество ошибок конфигурации, таких как добавление нового секрета в одной среде (например, staging), но забывание добавить его в другой среде (например, production).
- Работает с различными вариантами шифрования: AWS KMS, GCP KMS, PGP и т. д.
- Все определено в коде. Не требуется никаких дополнительных ручных шагов или скриптов-оберток (хотя интеграция sops требует либо кастомного провайдера, либо инструмента-обертки, такого как Terragrunt).
Недостатки этого метода
- Шифрование данных требует дополнительной работы. Вам приходится либо выполнять множество команд (например, aws kms encrypt), либо использовать внешний инструмент, такой как sops. Для правильного и безопасного использования этих инструментов требуется время на освоение.
- Секреты теперь зашифрованы, но поскольку они по-прежнему хранятся в системе контроля версий, ротация и отзыв секретов затруднены. Если кто-то когда-либо скомпрометирует ключ шифрования, он сможет вернуться назад и расшифровать все секреты, которые когда-либо были им зашифрованы.
- Возможность аудита того, кто получал доступ к секретам, минимальна. Если вы используете облачную систему управления ключами (например, AWS KMS), она, скорее всего, будет вести журнал аудита того, кто использовал ключ для расшифровки чего-либо, но вы не сможете узнать, что именно было расшифровано.
- Менее удобно для тестирования: при написании тестов для вашего кода Terraform (например, с помощью Terratest) вам потребуется выполнить дополнительную работу по шифрованию данных для ваших тестовых сред.
- Большинство управляемых сервисов ключей стоят небольших денег. Например, каждый ключ, хранящийся в AWS KMS, стоит 1 доллар в месяц.
Техника № 3: Хранилища секретов (например, Vault, AWS Secrets Manager)
Третий метод основан на хранении ваших секретов в специализированном хранилище секретов: то есть в базе данных, которая специально разработана для безопасного хранения конфиденциальных данных и жесткого контроля доступа к ним.
Обзор
Вот несколько более популярных хранилищ секретов, которые вы можете рассмотреть:
- HashiCorp Vault: кроссплатформенное хранилище секретов с открытым исходным кодом.
- AWS Secrets Manager: управляемое AWS хранилище секретов.
- AWS Param Store: управляемое AWS хранилище данных с поддержкой шифрования.
- GCP Secret Manager: управляемое GCP хранилище «ключ-значение».
Эти хранилища секретов решают проблему «перекладывания проблемы на потом», полагаясь на человеческую память: в данном случае на вашу способность запомнить пароль, который дает вам доступ к вашему облачному провайдеру (или несколько паролей в случае Vault, так как он использует Shamir’s Secret Sharing).
Пример использования AWS Secrets Manager
Сначала войдите в интерфейс AWS Secrets Manager, нажмите «store a new secret» и введите секреты, которые хотите сохранить:
По умолчанию используется формат JSON, как вы можете видеть на скриншоте выше. Затем присвойте секрету уникальное имя:
Нажмите «next» и «store», чтобы сохранить секрет.
Теперь в своем коде Terraform вы можете использовать источник данных aws_secretsmanager_secret_version для чтения этого секрета (для HashiCorp Vault, AWS SSM Param Store или GCP Secret Store вместо этого вы будете использовать источник данных vault_generic_secret, aws_ssm_parameter или google_secret_manager_secret_version):
Если вы сохранили данные секрета в формате JSON, вы можете использовать jsondecode для их синтаксического анализа:
И теперь вы можете использовать эти секреты в остальной части вашего кода Terraform:
Преимущества этого метода
- Храните секреты в виде обычного текста отдельно от вашего кода и системы контроля версий.
- Ваши секреты хранятся в специализированном хранилище секретов, которое обеспечивает шифрование и строгий контроль доступа.
- Все определено в самом коде. Никаких дополнительных ручных шагов или скриптов-оберток не требуется.
- Использование веб-интерфейса для хранения секретов обеспечивает удобство работы и минимальное время на освоение.
- Хранилища секретов обычно поддерживают ротацию секретов, что полезно в случае компрометации секрета. Вы даже можете включить ротацию по расписанию (например, каждые 30 дней) в качестве превентивной меры.
- Хранилища секретов обычно поддерживают подробные журналы аудита, которые показывают вам, кто именно и к каким данным получил доступ.
- Хранилища секретов обычно предоставляют API, который можно легко использовать из всех ваших приложений, а не только из кода Terraform. AWS Secrets Manager даже генерирует фрагменты кода, которые показывают, как именно читать ваши секреты из приложений, написанных на Java, Python, JavaScript, Ruby, Go и т. д.:
Недостатки этого метода
- Поскольку секреты не версионируются, не упаковываются и не тестируются вместе с вашим кодом, ошибки конфигурации более вероятны, такие как добавление нового секрета в одной среде (например, staging), но забывание добавить его в другой среде (например, production).
- Большинство управляемых хранилищ секретов стоят денег. Например, AWS Secrets Manager взимает 0,40 доллара в месяц за каждый сохраненный секрет плюс 0,05 доллара за каждые 10 000 вызовов API для сохранения или извлечения данных.
- Если вы используете хранилище секретов, управляемое самостоятельно, например HashiCorp Vault, вы одновременно тратите деньги на запуск хранилища (например, платите AWS за 3–5 инстансов EC2 для запуска Vault в режиме высокой доступности) и тратите время и деньги на то, чтобы ваша команда развертывала, настраивала, управляла, обновляла и мониторила хранилище.
- Менее удобно для тестирования: при написании тестов для вашего кода Terraform (например, с помощью Terratest) вам потребуется выполнить дополнительную работу по записи данных в хранилища секретов.
Заключение
Вот основные выводы из этого поста в блоге:
- Не храните секреты в виде открытого текста.
- Используйте бэкенд Terraform, поддерживающий шифрование.
- Используйте переменные среды, зашифрованные файлы или хранилище секретов для безопасной передачи секретов в ваш код Terraform. См. таблицу ниже для оценки компромиссов между этими вариантами.
Вся ваша инфраструктура. Определена как код. Примерно за день. Gruntwork.io.











