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

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Привет, Хабр!

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

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

  • Иметь единственное хранилище секретов

  • Защита от утечки секретов

  • Удобная работа с секретами для пользователей

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

Мы используем kubernetes, в этой статье расскажу об одном из способов деплоя секретов в kubernetes из Hashicorp Vault.

Основной задачей было найти решение, чтобы получить на выходе секрет 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

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

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

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/companies/rshb/articles/759816/


Интересные статьи

Интересные статьи

Салют. За время изучения римской брони, у меня осталось несколько зависших в воздухе вопросов. Поскольку ответить на них могла лишь экспериментальная археология, то я пошел по пути изготовления "анало...
Нужен ли каждому разработчику свой dev-сервер? Многие компании, опасаясь лишних расходов и проблем, даже не пробуют внедрить такой подход.CarPrice развернул свои dev-серверы еще в те далекие времена, ...
Система контейнерной оркестрации Kubernetes де факто стала золотым стандартом в DevOps, как универсальный инструмент управления конфигурацией и автоматической поддержки ожидаемого состояния системы. Н...
Развертывание Kubernetes на физических серверах  рекомендуется организациям, которые находятся в поисках новых возможностей в сфере управления инфраструктурой. Рассмотрим 6 причин попробовать K8s...
Kubevious — это революционная панель управления Kubernetes, намного функциональнее существующих аналогов. Она имеет множество интересных функций, но в этой статье мы соср...