- PVSM.RU - https://www.pvsm.ru -
Задайте себе вопрос — как правильно хранить пароль от базы данных, которая используется вашим сервисом? В отдельном репозитории с секретами? В репозитории приложения? В системе деплоя (Jenkins, Teamcity, etc)? В системе управления конфигурациями? Только на личном компьютере? Только на серверах, на которых работает ваш сервис? В некоем хранилище секретов?
Зачем об этом думать? Чтобы минимизировать риски безопасности вашей инфраструктуры.
Начнём исследование вопроса с определения требований к хранению секретов.
Vault [2] - хранилище секретов от признанных "решателей" проблем современной инфраструктуры — Hashicorp [3], авторов Vagrant, Consul, Terraform, Otto, etc. Секреты хранятся в key-value виде. Доступ к хранилищу осуществляется исключительно через API.
Основные фичи Vault:
Для нас Vault решает проблемы передачи секретов по незащищённым каналам, проблемы отказоустойчивого хранения секретов, а также проблемы гибкого разделения и аудита доступов. В планах использовать Vault как собственный CA.
Не буду переводить официальную документацию, поэтому вот несколько ссылок:
Итак, вы запустили Vault. Первым делом положим наш ключ в хранилище. Официальный процесс работы выглядит так:
Оказалось неудобным постоянно переключать токены, если ты отвечаешь за несколько сервисов.
Из-за особенности управления политиками доступа (подробнее ниже) выросли накладные расходы на первичное внедрение Vault в рабочий процесс сервиса.
Они описываются в формате JSON [7] или HCL [8] и записываются в Vault с помощью запроса к API или через cli. Например, так:
$ vault policy-write policy_name policy_file.hcl
Чтобы созданный вами мастер-токен мог создавать новые токены, он должен иметь политику:
path "auth/token/create" {
policy = "write"
}
Мастер-токен может создавать токены только с теми политиками, которыми он обладает.
Это означает, что вам недостаточно выдать токену политику service_name_prod_root:
path "secret/service_name/prod/*" {
policy = "write"
}
которая обладает полным доступом к secret/service_name/prod/*, но также нужно озаботиться, политикой service_name_prod_read:
path "secret/service_name/prod/*" {
policy = "read"
}
и тогда вы сможете создавать токены для read-only доступа с помощью команды:
$ vault token-create -policy=service_name_prod_read
И тут есть нюанс: политики применяются по степени детализации. Это означает, что если вы сделаете root политику вида:
path "secret/service_name/prod/*" {
policy = "write"
}
и захотите дать доступ на чтение политике service_name_prod_database_read:
path "secret/service_name/prod/database/*" {
policy = "read"
}
то вам нужно будет назначить токену, управляющему этим сервисом обе этих политики. Когда вы это сделаете, вы не сможете писать в secret/service_name/prod/database/*:
$ vault write secret/service_name/prod/database/replicas
secret_key=sha1blabla
Error writing data to secret/service_name/prod/database/replicas: Error making API request.
URL: PUT https://vault.service.consul:8200/v1/secret/service_name/prod/database/replicas
Code: 400. Errors:
* permission denied
Вам придётся уравновесить каждую детализированную политику на чтение, чтобы этого не случилось. Например, видоизменить политику service_name_prod_root:
path "secret/service_name/prod/*" {
policy = "write"
}
path "secret/service_name/prod/database/*" {
policy = "write"
}
Это не делает жизнь операторов легче, поэтому мы отказались от этого подхода и работаем с секретами только из под root-ключей.
В документации к Vault зря не сказано о важности учёта выпущенных токенов.
Нет простого способа узнать, какие ключи присутствуют в системе. Если вы создали токен и забыли о нём, не записав никакой информации, то вам придётся дожидаться, пока у него закончится срок жизни.
У root-токенов нет срока жизни, поэтому они будут оставаться в вашей системе бесконечно. Чтобы избежать этой ситуации, для создания нового токена в целях тестирования и проверки, укажите прекрасный флаг -ttl="1h", который устанавливает время жизни.
Это позволит спокойно работать и не бояться бесконтрольно увеличить количество токенов.
Также есть риск не уследить за истечением срока жизни токена и узнать об этом, например, во время деплоя.
Эту проблему можно решить, записывая токены и мониторя срок жизни. Но записывать токены куда-то в одно место — небезопасно. Поэтому с версии Vault 0.5.2 каждый токен после создания возвращает параметр accessor, зная который, можно взаимодействовать с токеном, не зная его самого (отзывать, обновлять, добавлять политики), например
$ vault token-revoke --accessor b30ee2a3-ea4b-9da0-3e5c-4189d375cad9
Подробности тут [9].
Ведение данных о выданных токенах позволяет проводить быстрый аудит активных доступов в системе и настроить мониторинг на истечение срока жизни токена.
$ vault token-create -policy=service_name_prod_root -policy=service_name_prod_read
Key Value
--- -----
token 82c5fb97-da1b-1d2c-cfd5-23fa1dca7c85
token_accessor dd256e17-b9d9-172d-981b-a70422e12cb8
token_duration 2592000
token_renewable true
token_policies [default, service_name_prod_root, service_name_prod_read ]
Пройдём по возвращённым параметрам:
token - тот самый ключ доступа к Vault.
token_accessor - ключ, по которому можно производить действия с ключом, не имея самого ключа. Разрешены следующие действия: посмотреть на метаданные токена, отозвать токен.
token_duration - время жизни токена в секундах.
token_renewable - если true, то время жизни токена может быть обновлено, но не более чем на срок от времени создания до параметра max-lease-ttl, который по умолчанию также 30d. Это означает, что если вы создали токен со сроком жизни в 30 дней и максимальный срок жизни тоже 30 дней, то обновить вы его не сможете.
token_policies - политики, которые назначены токену. Список политик изменить невозможно, возможно только отозвать токен и пересоздать заново.
Если токен был родительским для других токенов, то по умолчанию при отзыве этого токена все токены и секреты, созданные отзываемым токеном, отзываются рекурсивно. Этого можно избежать, указав флаг -mode. Более подробно
vault token-revoke -h
Если вы будете использовать consul-template [10] для автоматической перегенерации конфигов при изменении секретов, имейте в виду (и этого вы не найдёте в документации), что consul-template опрашивает изменение секрета в двух случаях:
Чтобы не вводить свой рабочий токен постоянно, его можно положить в $HOME/.vault-token или в переменную окружения VAULT_TOKEN. Тут надо оговориться, что я по умолчанию считаю рабочую станцию админа защищённой (зашифрованные диски, отсутствие гостевого входа, автоблокирование через минуту неактивности). Если это не так — то стоит отказаться от этой идеи.
Нам не подошёл официальный процесс работы с Vault по причине излишней трудоёмкости внедрения Vault в эксплуатацию сервиса. Поделюсь нашими решениями и ответами на возникающие вопросы в процессе внедрения Vault.
Именование секретов:
$ vault write secret/service_name/prod/database
base=appname
login=appname
password=difficult_password
или, например,
$ vault write secret/service_name/prod/database/base value=appname
$ vault write secret/service_name/prod/database/login value=appname
$ vault write secret/service_name/prod/database/password value=difficult_password
Решите этот вопрос до того, как ваш список секретов начнёт быстро расти и вы начнёте прикручивать различные средства автоматизации. Мы используем первую схему.
Мы установили и запустили Vault, получили root-токен. Что дальше?
По последней версии процесса, принятого в моей компании, это будет выглядеть так:
Установим vault [4].
Укажем расположение Vault:
$ export VAULT_ADDR='https://vault.service.consul:8200'
Авторизуемся под root токеном:
$ vault auth 82c5fb97-da1b-1d2c-cfd5-23fa1dca7c85
Запишем наш секрет:
$ vault write secret/service_name/prod/database base=appname login=appname password=difficult_password
Запишем политику для чтения в файл service_name_prod_read.hcl:
path "secret/service_name/prod/database*" {
policy = "read"
}
Создадим политику в Vault:
$ vault policy-write service_name_prod_read service_prod_read.hcl
Сгенерируем токен для чтения:
$ vault token-create -policy=service_name_prod_read
Key Value
--- -----
token cb347ae0-9eb4-85d1-c556-df43e82be4b0
token_accessor c8996492-17e3-16a7-2af1-d58598ae10d8
token_duration 2592000
token_renewable true
token_policies [default, service_name_prod_read ]
Запишем token_accessor для последующего аудита и мониторинга.
Проверим, что есть доступ на чтение:
$ vault auth cb347ae0-9eb4-85d1-c556-df43e82be4b0
$ vault read secret/service_name/prod/database
Key Value
lease_duration 2592000
base appname
login appname
password difficult_password
Всё работает, мы готовы использовать наш токен в системах автоматизации. Приведу примеры для популярных систем.
$ curl
-H "X-Vault-Token:cb347ae0-9eb4-85d1-c556-df43e82be4b0"
https://vault.service.consul:8200/v1/secret/service_name/prod/database
Настраиваем окружение:
$ export VAULT_ADDR=https://vault.service.consul:8200
$ export VAULT_TOKEN=cb347ae0-9eb4-85d1-c556-df43e82be4b0
Используем переменные из Vault в роли:
database_password: "{{ lookup('vault', 'secret/service_name/prod/database', 'password') }}"
Мы делаем запросы к vault только на уровне group_vars/group_name. Это удобно и позволяет не искать переменные по роли.
Hashicorp в своём блоге описали несколько путей использования секретов из Vault в своих chef-кукбуках — https://www.hashicorp.com/blog/using-hashicorp-vault-with-chef.html [13]
Для puppet существует прекрасный модуль https://github.com/jsok/hiera-vault [14], из документации к которому понятен процесс использования секретов из Vault.
Необходимо либо иметь токен и адрес Vault в переменных окружения:
$ export VAULT_ADDR=https://vault.service.consul:8200
$ export VAULT_TOKEN=cb347ae0-9eb4-85d1-c556-df43e82be4b0
или добавить в конфиг строчки:
vault {
address = "https://vault.service.consul:8200"
token = "cb347ae0-9eb4-85d1-c556-df43e82be4b0"
renew = true
}
и использовать секреты в своих шаблонах:
{{with secret "secret/service_name/prod/database"}}{{.Data.password}}{{end}}
Более подробно — в readme [10]
Спасибо за потраченное время, надеюсь, это было полезно.
Готов ответить на ваши вопросы.
Автор: bhavenger
Источник [15]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/it-infrastruktura/165778
Ссылки в тексте:
[1] можно вытащить на уровне сервера: http://thiébaud.fr/jenkins_credentials.html
[2] Vault: https://www.vaultproject.io
[3] Hashicorp: https://www.hashicorp.com
[4] тут: https://www.vaultproject.io/intro/getting-started/install.html
[5] Тут: https://www.vaultproject.io/#/demo/0
[6] Вот: https://www.amon.cx/blog/managing-all-secrets-with-vault/
[7] JSON: http://www.json.org/json-ru.html
[8] HCL: https://github.com/hashicorp/hcl
[9] Подробности тут: https://www.hashicorp.com/blog/vault-0.6.html#token-accessors
[10] consul-template: https://github.com/hashicorp/consul-template
[11] Подробности тут: https://www.vaultproject.io/docs/concepts/seal.html
[12] https://github.com/jhaals/ansible-vault: https://github.com/jhaals/ansible-vault
[13] https://www.hashicorp.com/blog/using-hashicorp-vault-with-chef.html: https://www.hashicorp.com/blog/using-hashicorp-vault-with-chef.html
[14] https://github.com/jsok/hiera-vault: https://github.com/jsok/hiera-vault
[15] Источник: https://habrahabr.ru/post/306812/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.