Зарегистрируйтесь, чтобы продолжить обучение

Секреты Terraform: Основы

Когда мы работаем с проектом через Terraform, важно сохранять в безопасности чувствительные данные. Для этого в Terraform есть секреты.

В этом уроке мы научимся использовать и хранить секреты, чтобы получить контроль над доступом к чувствительным данным и снизить риск их утечки.

Что такое секреты в Terraform

В каждом проекте есть данные, которыми не стоит делиться с посторонними, например: токены, пароли, API-ключи для интеграций с другими сервисами. Их не следует хранить в коде в открытом виде.

Такие чувствительные данные в Terraform называются секретами.

Terraform позволяет использовать для хранения секретов:

  • Переменные окружения
  • Локальные файлы
  • Удаленные key-value хранилища: Hashicorp Vault или Yandex Lockbox

Рассмотрим пример файла с переменными Terraform, в котором мы описываем инфрастуктуру:

yc_zone = "ru-central1-a"
yc_postgresql_version = 15
yc_postgresql_disksize = 15
yc_postgresql_connections = 100
db_name = "hexlet"

Обычно такую информацию посторонний не сможет использовать нам во вред, даже если мы будем хранить ее в открытом виде.

Теперь добавим в файл значения с чувствительными данными:

yc_zone = "ru-central1-a"
yc_postgresql_version = 15
yc_postgresql_disksize = 15
yc_postgresql_connections = 100
db_name = "hexlet"

db_user = "me"
db_password = "bvcduXBS7AXZs"
yc_iam_token = "t1.bssvNeXFU6nxhm830mcwrwyw022208d2FWamZ2di4-...-neSNu4bhlva41shwyfb0nvcmwXumXb1RvR05DgNkKw_ZNriSAQ"

Если запушить такой файл в удаленный репозиторий, это сделает инфрастуктуру уязвимой.

Любой человек, у которого есть доступ к этому репозиторию, сможет подключиться к нашему облаку и выполнить с ресурсами любые действия. Например, можно подложить свои ключи для авторизации на наши машины или извлечь ценные данные из базы.

Чтобы избежать таких проблем, нужно научиться хранить и управлять секретами. Начнем с объявления секретных переменных.

Как объявить секретные переменные

Когда мы объявляем секретные переменные, нужно минимизировать их появление в явном виде при выполнении команд. Для этого в Terraform используют параметр sensitive = true:

variable "db_password" {
  type        = string
  sensitive   = true
}

Это параметр задается для секретной переменной и скрывает значение переменной при выполнении terraform apply / terraform plan.

Когда выполняется terraform apply, передаваемая ресурсу переменная с паролем скрывается:

# yandex_mdb_postgresql_user.dbuser will be created
+ resource "yandex_mdb_postgresql_user" "dbuser" {
    + login      = true
    + name       = "me"
    + password   = (sensitive value)
    + settings   = (known after apply)
    ...
  }

Поля, которые определяются с применением секретных переменных, будут показаны в выводе terraform как (sensitive value).

Мы корректно объявили все секретные переменные. Теперь нужно выбрать подходящее решение, чтобы передать эти переменные в инфраструктуру.

Как передавать секреты в инфраструктуру

По умолчанию Terraform позволяет передавать секреты интерактивно. Если Terraform видит объявленную переменную, но не знает ее значения, при выполнении terraform apply он попробует узнать значение у нас:

Интерактивный режим terraform

Например, мы можем ввести значение yc_iam_token в момент развертывания инфраструктуры.

Так можно не фиксировать секретные значения в проекте, а только объявлять переменные и вводить их вручную по факту выполнения terraform apply:

variable "yc_iam_token" {
  description = "Yandex Cloud authorization token. Use 'yc iam create-token' to receive"
  type        = string
  sensitive   = true
}

Это безопасный способ, но затратный по времени и силам, так как все секреты нужно вводить поочередно при выполнении любой операции Terraform. Также такой способ не подходит для автоматизации работы с инфраструктурой.

Рассмотрим, какие еще механики предлагает Terraform для передачи секретов.

Как передать секреты через командную строку

Чтобы не вводить секреты вручную поочередно, их можно передавать как дополнительные параметры -var при выполнении команд Terraform. В этом случае в параметре указываются имя и значение переменной, объявленной в Terraform:

terraform apply -var "yc_iam_token=t1.bssvNeXFU6nxhm830mcwrwyw022208d2FWamZ2di4-...-neSNu4bhlva41shwyfb0nvcmwXumXb1RvR05DgNkKw_ZNriSAQ"

Чтобы не передавать секреты в открытом виде, мы можем использовать переменные окружения:

terraform apply -var "yc_iam_token=${SECRET_TOKEN}"

Если нужно передать Terraform несколько секретов, последовательно добавляем параметры -var для каждого отдельного секрета:

terraform apply -var "yc_iam_token=${SECRET_TOKEN}" -var "db_user=${SECRET_DB_USER}" -var "db_password=${SECRET_DB_PASSWORD}"

Такое решение уже можно автоматизировать. Но при передаче секретов по одному скрипты Terraform придется изменять при появлении новых -var.

Также можно хранить переменные вида TF_VAR_* в environment сервера, с которого мы управляем инфраструктурой. Terraform распознает такие переменные, как адресованные ему. Это будет выглядеть так:

export TF_VAR_yc_iam_token=t1.bssvNeXFU6nxhm830mcwrwyw022208d2FWamZ2di4-...-neSNu4bhlva41shwyfb0nvcmwXumXb1RvR05DgNkKw_ZNriSAQ
export TF_VAR_db_user=me
export TF_VAR_db_password=bvcduXBS7AXZs

terraform apply

Такой способ удобен, так как экспортировать переменную нужно один раз. В этом случае в пределах текущей сессии терминала переменная будет передаваться всем командам Terraform.

В некоторых случаях переменных окружения будет достаточно для работы с инфраструктурой Terraform. Но этот подход управления секретами может запутывать. В моменте мы не сможем сказать однозначно, с какими секретами работаем, пока не просмотрим их значения в окружении.

Чтобы получить больший контроль над секретами и их применением, посмотрим в сторону файлов.

Как хранить секреты в файле

Чтобы хранить секреты, часто используется файл переменных окружения .env в корне проекта. В нем описываются необходимые приложению параметры, секреты, пароли и ключи.

Terraform пока не поддерживает работу с файлами переменных окружения. Например, мы можем положить в проект с Terraform файл .env с таким содержимым:

TF_VAR_yc_iam_token=t1.bssvNeXFU6nxhm830mcwrwyw022208d2FWamZ2di4-...-neSNu4bhlva41shwyfb0nvcmwXumXb1RvR05DgNkKw_ZNriSAQ
TF_VAR_db_user=me
TF_VAR_db_password=bvcduXBS7AXZs

Terraform не увидит этот файл. Но он умеет похожим образом подключать файлы с переменными в форматах .tfvars или .tfvars.json.

Сохраним наши чувствительные данные в отдельный файл с расширением .tfvars:

yc_iam_token = "t1.bssvNeXFU6nxhm830mcwrwyw022208d2FWamZ2di4-...-neSNu4bhlva41shwyfb0nvcmwXumXb1RvR05DgNkKw_ZNriSAQ"
db_user = "me"
db_password = "bvcduXBS7AXZs"

Назовем этот файл secret.tfvars и добавим в директорию с проектом Terraform. Далее его можно будет подключить при выполнении команд Terraform через параметр -var-file:

terraform apply -var-file=secret.tfvars

Все переменные, описанные в файле, будут переданы инфраструктуре Terraform.

Terraform позволяет подключить несколько файлов одновременно. Мы можем разнести секреты по разным файлам. Это будет зависеть от их целевого назначения или от того, на какое окружение разворачиваем инфраструктуру.

Например, вынесем из созданного ранее secret.tfvars секреты базы данных в отдельный файл и назовем его secret.db.tfvars:

secret.tfvars

yc_iam_token = "t1.bssvNeXFU6nxhm830mcwrwyw022208d2FWamZ2di4-...-neSNu4bhlva41shwyfb0nvcmwXumXb1RvR05DgNkKw_ZNriSAQ"

secret.db.tfvars

db_user = "me"
db_password = "bvcduXBS7AXZs"

Попробуем подключить два файла сразу:

terraform apply -var-file=secret.tfvars -var-file=secret.db.tfvars

Результат будет таким же, как при использовании единого файла. Terraform получит значения из всех подключенных файлов.

Это можно использовать для уменьшения дублирования кода. К примеру, вы пользуетесь одним облаком и используете один токен для всех своих проектов Terraform. В таком случае секреты облака можно хранить в отдельном файле и подключать этот файл ко всем своим проектам.

Что еще дает хранение секретов в .tfvars:

  • В .tfvars можно описывать переменные спискового и объектного типа
  • Они подчиняются приоритизации переменных Terraform
  • Есть вспомогательные инструменты, которые позволяют хранить такие файлы с секретами в зашифрованном виде

Файлы с секретами и обычными переменными можно сохранять с именем вида *.auto.tfvars. Такие файлы не нужно явно подключать через -var-file — Terraform при выполнении команд будет подгружать их автоматически, если они находятся в директории Terraform.

Хранение секретов в файлах может поставить под вопрос их безопасность. Мы должны хранить файлы с секретами так, чтобы они не попали к постороннему.

Как защитить секреты от утечки

Любые файлы, в которых мы можем хранить незашифрованные секреты, нужно надежно защитить от утечки в сеть. Если мы храним проект с инфраструктурой в Git, важно случайно не добавить файл с секретами в коммит и отправить его в удаленный репозиторий.

Стоит сразу определить, как именовать секретные файлы Terraform, и добавить соответствующую маску в файл .gitignore проекта. Для примеров выше подойдет маска secret.*. Любые файлы, которые начинаются с secret., будут игнорироваться Git, в том числе наши файлы secret.tfvars и secret.db.tfvars. Они будут храниться только у нас на машине либо в удаленном защищенном менеджере ключей.

Еще нужно уделить внимание файлу состояния инфраструктуры — tfstate. По умолчанию tfstate сохраняется локально и содержит поля всех созданных в инфраструктуре ресурсов.

Создадим инфраструктуру с секретными полями:

terraform apply -var-file=secret.tfvars -var-file=secret.db.tfvars

Теперь посмотрим, что сохранится в tfstate:

{
  "version": 4,
  "terraform_version": "1.3.7",
  "serial": 6,
  "lineage": "ffa90edc-0c69-4c60-b292-14668e47272d",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "yandex_mdb_postgresql_cluster",
      "name": "pgcluster",
      "provider": "provider[\"registry.terraform.io/yandex-cloud/yandex\"]",
      "instances": [
        {
          "attributes": {
            ...
            "host": [
              {
                "fqdn": "rc1a-jgobopvvzoj7cwdi.mdb.yandexcloud.net",
                ...
              }
            ],
          },
          ...
        }
      ]
    },
    {
      "mode": "managed",
      "type": "yandex_mdb_postgresql_user",
      "name": "dbuser",
      "provider": "provider[\"registry.terraform.io/yandex-cloud/yandex\"]",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "cluster_id": "c9quisq456vccu4p42vm",
            "id": "c9quisq456vccu4p42vm:me",
            "login": true,
            "name": "me",
            "password": "bvcduXBS7AXZs",
            ...
          },
          ...
          "dependencies": [
            "yandex_mdb_postgresql_cluster.pgcluster",
            "yandex_vpc_network.network",
            "yandex_vpc_subnet.subnet"
          ]
        }
      ]
    },
    ...
  ],
  "check_results": null
}

В tfstate все секретные параметры ресурсов хранятся в открытом виде независимо от их свойства sensitive. Такой tfstate позволяет получить адрес базы данных, а также логин и пароль ее владельца.

Получается, что любые файлы состояния тоже нужно игнорировать в Git, если мы хотим защитить доступ к нашим ресурсам.

Чтобы в удаленный репозиторий не попало ничего секретного, .gitignore в проекте с Terraform должен выглядеть примерно так:

secret.*
*.tfstate
*.tfstate.*

Игнорирование .tfstate ограничит возможность управлять инфраструктурой с разных машин, так как состояние инфраструктуры будет храниться только там, где мы ее создали. Позже мы найдем решение этой проблемы, когда познакомимся с удаленными бэкендами Terraform.

Выводы

Любые переменные, значения которых потенциально могут давать доступ к нашей инфраструктуре или данным, следует интерпретировать как секреты. Такие переменные нужно объявлять с параметром sensitive = true, чтобы ограничить их отображение в консоли Terraform.

Секреты можно передавать через окружение с помощью экспорта в него переменных, которые начинаются на TF_VAR_*. Также секреты можно хранить в проекте в отдельных файлах формата .tfvars.

Нужно позаботиться, чтобы файлы с секретами не попали в публичные репозитории. Для этого любые секретные файлы с переменными, а также файлы *.tfstate, которые содержат параметры ресурсов Terraform в явном виде, нужно добавить в .gitignore проекта.


Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»
Изображение Тото

Задавайте вопросы, если хотите обсудить теорию или упражнения. Команда поддержки Хекслета и опытные участники сообщества помогут найти ответы и решить задачу