Совет: HashiCorp Learn также имеет постоянно обновляемое руководство по инъекции секретов в Kubernetes Pods через Vault Helm Sidecar. Посетите эту страницу для ознакомления с самыми последними шагами и примерами кода.
Мы рады объявить о новой интеграции Kubernetes, которая позволяет приложениям без встроенной в HashiCorp Vault нативной логики использовать статические и динамические секреты, получаемые из Vault. Она основана на новом инструменте под названием vault-k8s, который использует Kubernetes Mutating Admission Webhook для перехвата и дополнения специально аннотированной конфигурации подов для инъекции секретов с помощью Init и Sidecar контейнеров.
Приложениям нужно заботиться только о получении секрета по определённому пути в файловой системе, а не об управлении токенами, подключении к внешнему API или другим механизмам прямого взаимодействия с Vault.
Вот несколько поддерживаемых вариантов использования:
Только Init контейнер для предварительного заполнения секретов до начала выполнения приложения. Например, job для резервного копирования, которое выполняется по регулярному расписанию и нуждается во входных секретах только в момент старта.
Init и Sidecar. Init контейнер для получения секретов перед запуском приложения, и контейнер Sidecar, который запускается вместе с вашим приложением для поддержания секретов в актуальном состоянии (sidecar периодически проверяет, чтобы убедиться, что секреты актуальны). Например, веб приложение, использующее динамические секреты для подключения к базе данных с истекающим сроком аренды.
Аутентификация пода через сервисную учетную запись Kubernetes для соблюдения Vault Policy. Например, вы, вероятно, захотите ограничить Pod только доступом к секретам, которые им нужны для корректной работы. Это также должно помочь в аудите использования секретов каждым приложением.
Гибкие возможности форматирования вывода с использованием шаблона Vault Agent, встроенного в consul-template. Например, получение секретных данных из Vault для создания строки соединения с базой данных, или адаптация вывода под уже существующие форматы конфигурационных файлов и т.д.
Нашей неизменной целью является расширение поддержки Kubernetes и предоставление вам различных вариантов использования Vault для безопасного внедрения секретов в ваш рабочий процесс. Вы можете узнать больше о нашем мышлении здесь, прочитав статью в блоге What's Next for Vault and Kubernetes.
Видео гайд
Чтобы посмотреть видео демонстрацию того, как секреты Vault внедряются в поды Kubernetes с помощью init и sidecar контейнеров, пожалуйста, посмотрите видео ниже. Мы рассмотрим начальную установку Vault-k8s с помощью Vault Helm Chart и рассмотрим три примера использования (добавление аннотаций, форматирование вывода и фоновые задачи). Видео должно помочь расширить ваше понимание того, как это работает на практике.
Как это работает
Рекомендуемый метод установки - через последний Vault Helm Chart, который теперь поддерживает функционал инъекции vault-k8s (см. документацию). Также доступен Docker образ, который можно использовать для ручного запуска vault-k8s в предполагаемой среде, если вы решили не использовать Helm. В этом блоге основное внимание уделяется использованию Vault Helm Chart, так как это, как правило, хорошая отправная точка для изучения описываемого в статье функционала.
Helm Chart, с включенной функцией инъекции, запускает Vault, вместе со службой vault-k8s, и регистрирует себя в Kubernetes как Mutating Admission Webhook (привязанный к определенному namespace). На схеме ниже показано, как webhook vault-k8s используются для перехвата и изменения конфигурации пода при выполнении запроса Kubernetes API.
Вы можете выбрать каждое приложение для инъекции секретов Vault, используя специально заданные аннотации в конфигурации пода. Затем, когда webhook vault-k8s обнаруживает эти специфические аннотации, он переписывает описание пода на основе того, что было запрошено (с помощью ваших заданных аннотаций). Думайте об этих аннотациях как о параметрах настройки того, как вы хотите, чтобы секреты были представлены внутри контейнеров вашего приложения. Вы можете использовать выбранный namespace, определённым образом задавать аннотации, и учетные записи служб Kubernetes Service Accounts, привязанные к Vautl Policy, это дает вам тонкий контроль над тем, куда и какие секреты внедряются без ущерба для безопасности.
Итак, как выглядят эти специфические Vault аннотации к подам? Вы можете посмотреть документацию с детальной информацией, но мы рассмотрим несколько примеров ниже, которые должны дать вам хорошее представление о том, как выглядят аннотации в Vault. Вы можете использовать команду kubectl patch для применения аннотаций к существующему объекту Pod, они будут перехвачены службой webhook vault-k8s, которая затем добавит правильные контейнеры init и sidecar вместе с запрошенными секретами (если у вас есть доступ, основанный на учетной записи Service Account и связанной с ней Vault Policy).
# patch-basic-annotations.yaml
spec:
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-inject-secret-helloworld: "secrets/helloworld"
vault.hashicorp.com/role: "myapp"
Например, вышеприведенные аннотации при применении указывают vault-k8s внедрить init и sidecar контейнеры в указанный Pod, получить secret/helloworld секрет из Vault, и записать эти данные в /vault/secrets/helloworld, если у роли "myapp" есть доступ к этим данным.
Инъекция секретов в действии
В этом разделе мы пройдём вместе через процесс внедрения секретов чтобы понять, как работать с vault-k8s на двух примерах. Как упоминалось выше, рекомендуемым методом установки является официальный Vault Helm Chart. Чарт, с включенной функцией инжекции агента Sidecar, запускает Vault, веб-сервис инжектора webhook vault-k8s и настраивает Kubernetes Mutating Admission Webhook.
Перед установкой Vault, убедитесь, что поддержка инжекторов включена в файле Vault Helm Chart values.yaml.
injector:
enabled: true
Далее настроим тестовый namespace, зададим ему текущий контекст и установим Vault с помощью Helm Chart.
kubectl create namespace demo
kubectl config set-context --current --namespace=demo
helm install --name=vault --set='server.dev.enabled=true' ./vault-helm
Далее, подключаемся к Vault и настраиваем политику под названием "app" для тестирования. Это очень нестрогая политика, и в производственном окружении вы разумеется захотите больше ограничивать ее, но она служит в качестве примера, пока мы играем с этим функционалом.
kubectl exec -ti vault-0 /bin/sh
cat < /home/vault/app-policy.hcl
path "secret*" {
capabilities = ["read"]
}
EOF
vault policy write app /home/vault/app-policy.hcl
Далее нам нужно настроить метод Vault Kubernetes Auth и прикрепить только что созданную политику к учетной записи service account нашего приложеня (мы создадим приложение через минуту).
vault auth enable kubernetes
vault write auth/kubernetes/config token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" kubernetes_host=https://${KUBERNETES_PORT_443_TCP_ADDR}:443 kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
vault write auth/kubernetes/role/myapp bound_service_account_names=app bound_service_account_namespaces=demo policies=app ttl=1h
Наконец, давайте создадим пример имени пользователя и пароля в Vault с помощью движка KV Secrets Engine. Конечная цель здесь - чтобы это имя пользователя и пароль были введены в нашу целевую файловую систему pod, которая ничего не знает о Vault.
vault kv put secret/helloworld username=foobaruser password=foobarbazpass
Ниже пример конфигурационного файла app.yaml для запуска демонстрационного приложения. Тут создаётя простой контейнер с веб-сервисом, используемый для наших тестов. Мы также определяем учетную запись Service Account, которую мы можем затем привязать к Vault Policy, созданную нами ранее. Это позволяет вам указать каждый секрет, к которому приложению разрешен доступ.
# app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
labels:
app: vault-agent-demo
spec:
selector:
matchLabels:
app: vault-agent-demo
replicas: 1
template:
metadata:
annotations:
labels:
app: vault-agent-demo
spec:
serviceAccountName: app
containers:
- name: app
image: jweissig/app:0.0.1
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: app
labels:
app: vault-agent-demo
Теперь давайте запустим наше тестовое приложение и создадим учетную запись service account. Мы также можем проверить, что в /vault/secrets нет секретов.
kubectl create -f app.yaml
kubectl exec -ti app-XXXXXXXXX -c app -- ls -l /vault/secrets
Теперь патч для аннотаций, который мы можем применить к конфигурации pod нашего приложения, работающего на примере, который устанавливает определенные аннотации для введения нашего секрета/секретного хранилища.
# patch-basic-annotations.yaml
spec:
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-inject-secret-helloworld: "secret/helloworld"
vault.hashicorp.com/role: "myapp"
И давайте применим наш патч с аннотациями.
kubectl patch deployment app --patch "$(cat patch-basic-annotations.yaml)"
kubectl exec -ti app-XXXXXXXXX -c app -- cat /vault/secrets/helloworld
Если все пойдет правильно, то теперь вы должны увидеть tmpfs mount в /vault/secrets и helloworld секрет содержащийся там. Здесь произошло следующее: когда мы применили патч, наш webhook vault-k8s перехватил и изменил описание Pod, включив в него Init-контейнер для предварительного размещения нашего секрета, а также Vault Agent Sidecar для синхронизации этих секретных данных на протяжении всего жизненного цикла нашего приложения.
Однако, если бы вы на самом деле запустили это, вы бы заметили, что данные в /vault/secrets/helloworld не отформатированы, а являются просто Go структурированным экспортом нашего секрета. Это может быть проблематично, так как вы почти наверняка хотите, чтобы выходные данные были отформатированы определенным образом, для использования вашим приложением. Каким будет решение этой проблемы?
data: map[password:foobarbazpass username:foobaruser]
metadata: map[created_time:2019-12-16T01:01:58.869828167Z deletion_time: destroyed:false version:1]
Вы можете отформатировать ваши секретные данные, используя шаблоны Vault Agent Templates, что очень полезно для решения различных задач по форматированию вывода. В следующем примере мы разберем наши секретные данные в строку соединения postgresql. Шаблоны могут быть очень удобны и должны подходить для различных случаев использования, когда вам нужно соответствовать используемым форматам конфигурации, поднимать соединения к БД (как показано ниже) и т.д.
# patch-template-annotations.yaml
spec:
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-inject-status: "update"
vault.hashicorp.com/agent-inject-secret-helloworld: "secret/helloworld"
vault.hashicorp.com/agent-inject-template-helloworld: |
{{- with secret "secret/helloworld" -}}
postgresql://{{ .Data.data.username }}:{{ .Data.data.password }}@postgres:5432/wizard
{{- end }}
vault.hashicorp.com/role: "myapp"
Далее, как и раньше, мы применяем патч аннотаций.
kubectl patch deployment app --patch "$(cat patch-template-annotations.yaml)"
kubectl exec -ti app-XXXXXXXXX -c app -- cat /vault/secrets/helloworld
Теперь выход выглядит вот так:
postgresql://foobaruser:foobarbazpass@postgres:5432/wizard
В приведенных выше примерах был рассмотрен довольно схема работы инъекции секрета в запущенное приложение, не имеющее встроенной нативной логики работы с Vault. Приложениям нужно только заниматься поиском файла с секретом по определённому пути, вот и все.
Следующие шаги
Репозиторий vault-k8s теперь доступен на GitHub. Чтобы узнать больше, пожалуйста, читайте документацию Agent Sidecar Injector и посмотрите наш вебинар Injecting Vault Secrets Into Kubernetes Pods via a Sidecar. Вам также доступен новый гайд опубликованный на страницах HashiCorp Learn и новый блог нашей команды Инженерных Решений, которые еще глубже погружаются в использование инъекций.
Кроме того, если вам нравится заниматься подобными вещами, возможно, вам тоже будет интересно поработать в HashiCorp, мы принимаем на работу!
От переводчика:
В дальнейшем планирую перевести следующие статьи, надеюсь они так-же будут полезными:
symydo
К сожалению, vault-k8s сохраняет секреты в файлы, а не в переменные окружение, как это необходимо в 90% случаев. Приходится использовать bank-vaults (https://github.com/banzaicloud/bank-vaults)
r_andreev Автор
По поводу необходимости читать секреты именно из переменных окружения готов поспорить.
Для большинства приложений и фреймворков без разницы откуда получать данные, более того, большинство «из коробки» именно из файла получает конфиг, а уже потом разработчики прикручивают переменные окружения, с разными параметрами и секретами, просто потому что им так кажется удобней/привычней.
Поэтому, тут скорее вопрос вкусов, кому как больше нравится работать, тот так и делает.
zolti
12factor.net/ru/config