Шифрование приватных данных в HashiCorp Terraform является важным аспектом работы в этом инструменте. И существует методы, которые позволяют безопасно хранить конфиденциальные данные в зашифрованном виде и передавать их в безопасном режиме при необходимости.
В этой статье рассмотрим, как безопасно хранить секреты в Terraform, и методы их шифрования, которые помогут улучшить безопасность при работе с инфраструктурой в облаке.
Пользователи Terraform иногда сталкиваются с необходимостью обработать чувствительную информацию. Например, API-ключи или логин и пароль пользователя для БД.
Вот пример кода для создания базы данных в Timeweb Cloud (более подробный гайд о работе с Terraform и Timeweb Cloud размещён на GitHub):
resource "twc_db_mysql_8" 'my_db' {
name = "mysql_8_database'
# Имя пользователя и пароль
login = <To be decided>
password = <To be decided>
preset_id = data.twc_db_preset.example-db-preset.id
}
Чтобы код работал, в переменных «username» и «password» необходимо указать имя пользователя и пароль. Также наша задача — надежно обработать эти учетные данные и предотвратить их случайное раскрытие.
Самый простой вариант — сразу присвоить переменным текстовые значения:
resource "twc_db_mysql_8" 'my_db' {
name = "mysql_8_database'
# Имя пользователя и пароль
login = "root"
password = "admin"
preset_id = data.twc_db_preset.example-db-preset.id
}
Но это плохой путь, который вредит информационной безопасности всей системы, даже если вы используете частный репозиторий Git для хранения проекта. Дело в том, что любой пользователь с доступом к системе контроля версий также будет иметь доступ к секретам.
Кроме этого, многие инструменты, имеющие доступ к репозиторию, такие как Jenkins, CircleCI или GitLab, сохраняют копию репозитория перед сборкой кода. Также, храня чувствительную информацию в виде обычного текста, вы рискуете, что одна из программ на вашем компьютере получит доступ к этим файлам, а значит и к секретам .
В общем, хранение конфиденциальной информации в виде обычного текста сильно упрощает доступ к ним, чем, например, могут воспользоваться злоумышленники. Что, в целом, актуально для любых инструментов, а не только Terraform. Поэтому главное правило шифрования секретов в инфраструктуре как код — это хранение конфиденциальной информации не в виде открытого текста.
К чему могут привести незащищенная информация:
У Terraform есть один изъян, который снижает безопасность системы. Каждый раз, когда пользователь использует Terraform для развёртки своей инфраструктуры, он сохраняет большое количество информации о ней, в том числе и предоставленные параметры для подключения к базе данных, в файле состояния terraform.tfstate
в виде обычного текста. Этот файл сохраняется в той директории, в которой пользователь выполнял команду apply
.
Это приводит к тому, что даже если использовать любой из методов, приведённых в данной статье, чувствительная информация все равно будет доступна в виде обычного текста в файле состояния.
Об этой проблеме известно уже больше девяти лет, но до сих пор нет какого-то общего решения. Существуют временные фиксы, которые удаляют секреты из файлов состояния, но они недостаточно надежно работают и могут перестать быть совместимы после очередного обновления.
Поэтому на текущий момент, вне зависимости от того, какой метод используется для шифрования, самым важным аспектом безопасности данных является безопасность файла состояния. Не рекомендуется хранить его в локальной части системы и репозитории, а использовать для этого хранилища, которые поддерживают шифрование. Например, в объектном хранилище S3 с контролем доступа к нему.
Terraform поддерживает чтение переменных окружения, и это можно использовать для шифрования ключей. Для начала создадим несколько переменных в каталоге проекта variables.tf
:
variable "username" {
description = "Имя пользователя"
type = string
sensitive = true
}
variable "password" {
description = "Пароль пользователя"
type = string
sensitive = true
}
Параметр type
определяет тип переменных, а параметр sensitive
помечает, что в переменной хранится конфиденциальная информация. Когда его значение равно true
, данные переменной не отразятся в логах, в том числе при выполнении команд plan
и apply
.
Теперь заменим в коде resources
переменные:
resource "twc_db_mysql_8" 'my_db' {
name = "mysql_8_database'
# Имя пользователя и пароль
login = var.username
password = var.password
preset_id = data.twc_db_preset.example-db-preset.id
}
И установим значение переменных через терминал. В терминале к их имени необходимо добавлять TF_VAR_
: username
→ TF_VAR_username
.
export TF_VAR_username="root"
export TF_VAR_password="admin"
Когда пользователь выполнит команду terraform apply
, Terraform будет использовать значение переменных среды.
Важно: после выполнения Bash-команды сохраняются в истории. Чтобы в ней не сохранились конфиденциальные данные, такие как пароль и логин, воспользуйтесь переменной окружения HISTCONTROL
. Задайте ей значение ignorespace
— в таком случае команды, которые начинаются с пробела, сохранятся в истории не будут:
export HISTCONTROL=ignorespace
Благодаря переменным окружения в коде Terraform не будет конфиденциальных данных, указанных в виде текста. Однако они не полностью решают проблему шифрования секретов — в некотором смысле переменные окружения «переносят» проблему из области инструмента в область операционной системы, в которой эти конфиденциальные данные также необходимо защитить.
Популярный подход к решению этой проблемы — это хранение конфиденциальных данных в виде зашифрованных файлов с помощью GPG. Для этого можно использовать для этого можно использовать «чистый» GPG, а можно воспользоваться менеджером паролей, который реализует такую же логику. Например, Pass.
В нём данные хранятся в виде GPG-зашифрованных файлов. Они организованы в виде иерархии каталогов, которые могут быть скопированы с одного устройства на другое, а управлять Pass можно с помощью стандартных команд терминала.
Руководство по установке инструмента на разные операционные системы размещено на его официальном сайте. Вот код для инсталляции на Ubuntu:
sudo apt update
sudo apt install pass
Для работы с Pass понадобится GPG-ключ. Для его генерации в первую очередь необходимо установить GPG:
sudo apt install gpg
После — сгенерировать ключ:
gpg --full-generate-key
Выберите размер ключа и его тип (например «RSA and RSA»), а также укажите данные для генерации. После генерации в выводе терминала будет GPG-ключ:
Скопируйте GPG-ключ и используйте его для инициализации Pass:
pass init <GPG-ключ>
Зашифруем с помощью Pass уже упомянутые переменные — username
и password
:
pass insert username
После выполнения дважды введите значение переменной. Аналогичным образом поступаем с «password»:
pass insert password
Чтобы получить доступ к секретам из командной строки, используйте команду pass
и имя секрета:
pass username
Введите кодовую фразу, которую указали при генерации GPG-ключа, и секрет будет отображен в консоли в виде обычного текста.
Теперь, чтобы использовать зашифрованные данные, установите их в качестве переменных окружения:
export TF_VAR_username=$(pass username)
export TF_VAR_password=$(pass password)
В чём плюсы и минусы использовать для шифрования секретов переменные окружения:
Плюсы |
Минусы |
Секреты остаются вне кода, а значит, не отражаются в репозитории. |
Инфраструктура не полностью описывается в коде Terraform — это усложняет его поддержку и снижает читаемость. |
Легко использовать: для начала работы не требуется высокой квалификации. |
Требуются дополнительные шаги для работы с таким решением. |
Переменные окружения можно интегрировать со многими менеджерами паролей, как в примере с Pass. |
Поскольку вся работа с секретами происходит за пределами Terraform, на неё не распространяется средства безопасности, заложенные в коде. |
Подходит для тестовых запусков: в качестве переменных окружения легко установить фиктивные значения. |
Vault — это внешнее хранилище с открытым исходным кодом для хранения чувствительной информации. Его также, как и Terraform, разработала компания HashiCorp.
Vault позволяет реализовать централизованное зашифрованное хранилище для секретов. Вот его ключевые функции:
Vault — это внешнее хранилище, и взаимодействие с ним осуществляется через сеть. Поэтому его можно установить как на локальное устройство и обращаться к нему через localhost, либо на удалённый сервер.
В этом материале расскажем, как установить его на Ubuntu:
sudo apt update && sudo apt install gpg
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install vault
vault version
Если установка завершилась успешно, в терминале будет отображена последняя версия хранилища ключей.
После установки инструмент необходимо настроить. Мы будем использовать его в режиме сервера. Запустим его:
vault server -dev
Важно: здесь мы запускаем сервер в режиме разработки. Это означает, что в таком режиме работы, он будет хранить все данные, в том числе ключи, в оперативной памяти. При перезапуске сервера все данные будут потеряны, поэтому для в продакшене желательно использовать обычный режим работы сервера. Режим разработки подходит для учебных целей, как этот материал.
Во время выполнения команды в терминале будет отображаться детали её прогресса, а после — URL-адрес, на котором работает сервер, и токен для авторизации. К нему можно подключиться даже через браузер, введя в поисковой строке URL-адрес сервера.
Для дальнейшей работы с сервером в режиме разработки необходимо создать переменную окружения с его URL-адресом. Если вы устанавливали Vault на локальном компьютере, то команда будет выглядеть так:
export VAULT_ADDR='http://127.0.0.1:8200'
Проверить состояние хранилища можно следующим образом:
vault status
Команда вернёт информацию о нём: дата создания, версию программного обеспечения и другое.
Для хранения конфиденциальной информации воспользуемся хранилищем «ключ-значение». Для начала создадим его:
vault secrets enable -path=db_data kv
После добавим в хранилище секреты:
vault kv put db_data/secret_tf username=root password=admin
Проверить результат можно прямо в браузере.
В Main-файле Terraform укажем Vault как провайдера:
terraform {
required_providers {
vault = {
source = "hashicorp/vault"
version = "3.23.0"
}
twc = {
source = "tf.timeweb.cloud/timeweb-cloud/timeweb-cloud"
}
}
required_version = ">= 0.13"
}
В конфигурационном файле Terraform также необходимо прописать инструмент как провайдера:
provider "vault" {
address = "http://127.0.0.1:8200"
token = "Токен Vault"
}
А также реализовать метод для чтения данных из хранилища:
data "vault_generic_secret" "secret_credentials" {
path = "db_data/secret_tf"
}
resource "twc_db_mysql_8" 'my_db' {
name = "mysql_8_database'
# Имя пользователя и пароль
login = data.vault_generic_secret.secret_credentials.data["username"]
password = data.vault_generic_secret.secret_credentials.data["password"]
preset_id = data.twc_db_preset.example-db-preset.id
}
Шифрование секретов в Terraform можно автоматизировать, чтобы обеспечить масштабируемый и безопасный процесс управления конфиденциальными данными. Ниже — некоторые способы автоматизации:
В этой статье мы рассмотрели вопрос безопасности чувствительной информации, к чему может привести её хранение в незащищенном виде, обсудили, почему нужно бережно хранить файл состояния, а также привели примеры шифрования секретов в Terraform с помощью Hashicorp Vault, переменных сред и GPG.