Секреты в kubernetes используя Hashicorp Vault + External Secrets Operator

—

от автора

Всем привет! Меня зовут Сергей Смирнов, я DevOps экосистемы «Своё» в РСХБ-Интех (технологическая дочка Россельхозбанка).
Сегодня я расскажу вам об одном из способов доставки секретов в kubernetes из Hashicorp Vault.

Hashicorp Vault уже довольное время на слуху и зарекомендовал как надежная система хранения конфиденциальной информации(секретов): пароли, ssh ключи, api токены, сертификатов и пр.

Hashicorp Vault поможет решить сразу несколько задач:

• Создать единственное хранилище секретов;
• Защитить от утечки секретов;
• Сделать работу с секретами для пользователей удобной.

На текущий момент существует довольно много интергации Hashicorp Vault с популярными системами CI/CD, облачными провайдерами, системами оркестрации/контейнеризации

Мы в работе активно используем kubernetes. Основной задачей было найти решение, чтобы получить на выходе секрет kubernetes.
Перепробовав несколько способов доставить секреты в kubernetes используя Hashicorp Vault, мы остановились на External Secrets Operator.

Пробовали:

  • Средствами Hashicorp Vault. Vault Agent монтирует директорию с секретами через init или sidecar контейнеры. Не подходит, т.к. возможна лишняя нагрузка на кластер при создании лишних подов. Плюс не умеют преобразовать секреты Vault в секреты Kubernetes, а сохраняет секреты в файлы.

  • Bank Vault Не подходит, сохраняет секреты в переменные окружения.

  • ArgoCD Vault Plugin Умеет работать с секретами kubernetes, но показался сложным в настройке.

External Secrets Operator простыми словами — это оператор для kubernetes, который автоматически синхронизирует секреты из внешних API и создает их в kubernetes. Если секрет во внешнем API изменяется, контроллер обновляет секреты.

С точки зрения безопасности kubernetes есть пара моментов:

  • Необходимо настраивать RBAC в kubernetes, чтобы исключить доступ до ресурса secrets. Для того, чтобы пользователь не смог прочитать секрет.

  • Если есть необходимость, ограничить доступ к кластеру. Если у пользователя есть доступ к поду, он сможет посмотреть переменные/значение секрета через терминал или логи пода.

Со стороны Hashicorp Vault все зашифровано. Чтобы получить доступ к api, необходимо владеть соответствующими привилегиями для авторизации.
Плюс при рестарте кластер запечатывается, если конечно не настроено автораспечатывание кластера(autounseal) транзитными ключами. Для ручного распечатывания по умолчанию требуются 3 из 5 ключей(unseal keys). В идеале ключи должны храниться у разных людей.

Реализация по этапам


Что потребуется для реализации?

  • Кластер kubernetes версии минимум 1.22+ согласно документации

  • Helm версии 3.6+ согласно документации

Этап 1

1. Установка vault

Добавляем репозиторий hashicorp и устанавливаем vault. В качестве бекенда Integrated Storage (Raft).

Integrated Storage (Raft) работает таким образом, что все узлы в кластере Vault будут иметь реплицированную копию данных Vault. Integrated Storage Raft официально поддерживается Hashicorp.

Бекап можно запускать на любом из узлов.
Команды довольно простые:

  • vault operator raft snapshot save new.snapshot

  • vault operator raft snapshot restore new.snapshot

helm repo add hashicorp https://helm.releases.hashicorp.com  helm install vault hashicorp/vault \   --set='server.ha.enabled=true' \   --set='server.ha.raft.enabled=true' \   -n vault \   --create-namespace  

Проверяем, что все поды запустились:

kubectl get po -n vault  NAME                                               READY   STATUS    RESTARTS   AGE vault-0                                             1/1     Running   0          1d vault-1                                             1/1     Running   0          1d vault-2                                             1/1     Running   0          1d 

2. Инициализация и распечатывание кластера Hashicorp Vault

Инициализируем vault

kubectl exec -ti vault-0 -- vault operator init 

После инициализации vault получим корневой ключ и 5 ключей для распечатки.

Unseal Key 1: wQRU1yjL88Plb3rPSQPUfLw1KOCsPMACpXLY0Ixbdhfg Unseal Key 2: ai3NajyVDFyqG5Lz6pDYNX118ti9Slqo2vueQhtg6Usq Unseal Key 3: x1a0VneA8cBkcXMDkxPwOOByPzlwuUw3dNaa7hfUqDAx Unseal Key 4: oztkcLGBAesVQwyO7Kc059xlqq9YSh1vkEkQFzKlnwae Unseal Key 5: 5cVmmDVZ7BIbQolCQdCoUXhdTRojPD2rgE1t83QgRKNn   Initial Root Token: s.lTEYiTAv63CLsf0FqBcS672x                      

Распечатаем кластер. По умолчанию используются 3 unseal ключа.

kubectl exec -ti vault-0 -- vault operator unseal 

После присоединим оставшиеся 2 пода к кластеру.

kubectl exec -ti vault-1 -- vault operator raft join http://vault-0.vault-internal:8200 kubectl exec -ti vault-1 -- vault operator unseal 
kubectl exec -ti vault-2 -- vault operator raft join http://vault-0.vault-internal:8200 kubectl exec -ti vault-2 -- vault operator unseal 

После распечатки кластера авторизируемся в vault используя корневой ключ(Initial Root Token).

kubectl exec -ti vault-0 -- vault login 

Проверим состояние кластера

kubectl exec -ti vault-0 -- vault operator raft list-peers  Node                                    Address                        State       Voter ----                                    -------                        -----       ----- 4rzii8af-8847-7f28-23f0-p36vwkghqxng    vault-0.vault-internal:8201    leader      true aydfyon3-6b3x-1b7x-9b34-bdzpa1el2dzf    vault-1.vault-internal:8201     follower   true 2tjh4iqi-8bcv-8b2n-1b7s-wj6wtxgOk5cs     vault-2.vault-internal:8201    follower    true 

3. Создание политики на чтение секретов из Hashicorp Vault

Мы используем вторую версию KV хранилища, поэтому путь до секрета будет /projects/data/ и /projects/metadata.

Одно из отличий между версиями, что вторая поддерживает версионирование.

Плюс выдадим права на обновление токена.

vault policy write read-secret - <<EOF path "/projects/data/dev/*" {     capabilities = ["read", "list"] }  path "/projects/metadata/dev/*" {     capabilities = ["read", "list"] }  path "auth/token/renew-self" {     capabilities = ["update"] } EOF 

4. Настройка авторизации kubernetes

vault auth enable kubernetes  vault write auth/kubernetes/config \ kubernetes_host=https://${KUBERNETES_PORT_443_TCP_ADDR}:443 \ issuer="https://kubernetes.default.svc.cluster.local" 

создадим роль

vault write auth/kubernetes/role/read-secret \ bound_service_account_names=vault-auth \ bound_service_account_namespaces=vault \ policies=read-secret \ alias_name_source=serviceaccount_name \ ttl=1h 

создадим service account

kubectl create serviceaccount vault-auth -n vault 

6. Создадим секрет

vault kv put projects/dev/app user=admin password=123456 ==== Secret Path ==== projects/data/dev-app  ======= Metadata ======= Key                Value ---                ----- created_time       2023-08-18T10:28:38.163062633Z custom_metadata    <nil> deletion_time      n/a destroyed          false version            1 

Этап 2

1. Установка External Secrets Operator

helm repo add external-secrets https://charts.external-secrets.io  helm install external-secrets \    external-secrets/external-secrets \    -n vault \    --create-namespace  

Проверяем, что все поды запустились:

kubectl get po -n vault  NAME                                                READY   STATUS    RESTARTS   AGE external-secrets-h4ls02c0wd-1cxyr                   1/1     Running   0          58d external-secrets-cert-controller-h4lfd8bfzg-km8ez   1/1     Running   0          58d external-secrets-webhook-su3x3fOjk-uxsy2            1/1     Running   0          58d 

2. Настройка External Secrets Operator

External Secrets Operator — набор custom resources таких как: ExternalSecret, SecretStore и ClusterSecretStore.

Мы рассмотрим только ExternalSecret и SecretStore, т.к. для каждого пространства имен(namespace) свои секреты. ClusterSecretStore будет доступен из любого пространства имен(namespace).

SecretStore указывает как получить доступ.

ExternalSecret указывает какие данные нужно извлечь.

Создадим манифесты:

apiVersion: external-secrets.io/v1beta1 kind: SecretStore metadata:   name: app   namespace: dev-app spec:   provider:     vault:       server: "http://vault.vault:8200" # адрес нашего vault. Складывается из имени сервиса и пространства имен.       path: "projects" # имя kv       version: "v2" # версия kv       auth:         kubernetes: # метод авторизации           mountPath: "kubernetes"           role: "read-secret" # роль для авторизации           serviceAccountRef:             name: "vault-auth" # имя нашего serviceAccount --- apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata:   name: app   namespace: dev-app spec:   refreshInterval: "15s"   secretStoreRef:     name: app # имя SecretStore     kind: SecretStore   target:     name: vault-secrets # имя будущего секрета kubernetes   data:     - secretKey: user # ключ секрета       remoteRef:         key: dev/app # путь до секрета в vault         property: user # ключ секрета в vault     - secretKey: password       remoteRef:         key: dev/app         property: password 

Проверим, что все создалось

kubectl get externalsecrets -n vault NAME                                  AGE   STATUS   CAPABILITIES   READY secretstore.external-secrets.io/app   14m   Valid    ReadWrite      True  NAME                                     STORE   REFRESH INTERVAL   STATUS         READY externalsecret.external-secrets.io/app   app     15s                SecretSynced   True 

Проверим наш созданный секрет

kubectl describe secrets -n vault vault-secrets Name:         vault-secrets Namespace:    vault Labels:       <none> Annotations:  created-by: system:serviceaccount:vault:external-secrets               reconcile.external-secrets.io/data-hash: b749f0dfb88266e3b81d63dbc2a4402b  Type:  Opaque  Data ==== password:  6 bytes user:      5 bytes 
kubectl get secrets/vault-secrets --template={{.data.user}} | base64 -D admin kubectl get secrets/vault-secrets --template={{.data.password}} | base64 -D 123456 

Подведем небольшой итог. Этот инструмент хорошо решает задачу автоматической синхронизации секретов между Vault и kubernetes. Также ESO поддерживает другие системы управления секретами, такие как AWS Secrets Manager, GCP Secrets Manager, Yandex Lockbox, что упрощает работу. Предлагаю Вам его оценить и написать комментарий под этим постом.

Спасибо за уделенное время! Надеюсь, что эта информация будет полезной для вашей работы и общего развития. Кстати, у нас есть helm чарт для создания secret-store и external-secrets. Мы используем в связке с Argo CD, который очень нам пришёлся кстати. Может и вас он заинтересует, переходите по ссылке на github.


ссылка на оригинал статьи https://habr.com/ru/articles/759816/