Как создавать Kubernetes секреты из Vault, используя external-secrets-operator

—

от автора

Если вы пользуетесь кубом, у вас есть секреты…

Бывают случаи, когда инженеры хранят секретные данные, ключи, токены в открытом виде или в переменных Gitlab. В Kubernetes для хранения данных, которые нежелательно показывать широкому кругу лиц, предусмотрены секреты.

Если обратиться к официальной документации Kubernetes, там описаны три варианта управления секретами:

  1. Использовать kubectl

  2. Использовать файл конфигурации

  3. Использовать Kustomize

Но есть еще один способ: синхронизация секретов из HashiCorp Vault в Kubernetes. Многие компании используют HashiCorp Vault, потому что: 

  • Он безопасно хранит ключи и управляет ими. 

  • В нём много сделано для аутентификации и авторизации доступа к секретам, например, ACL и принцип минимальных привилегий. 

Для синхронизации секретов из HashiCorp Vault в Kubernetes существует несколько инструментов. Их сравнение – тема отдельной статьи. Сегодня предлагаю рассмотреть безопасный способ передавать, синхронизировать, интегрировать секреты напрямую из Vault в Kubernetes – с помощью метода аутентификации AppRole, используя external-secrets-operator.

Что такое External Secrets Operator

External Secrets Operator — это оператор Kubernetes, который интегрирует внешние системы управления секретами, такие как HashiCorp Vault и многие другие. Оператор считывает информацию из внешних API и автоматически создает секреты Kubernetes.

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

В статье пошагово пройдёмся по каждому этапу.

Шаг 1: Создаём Kubernetes кластер

Для начала создадим Kubernetes кластер.

Не буду подробно на этом останавливаться: про создание Kubernetes кластера в Яндекс облаке с помощью Terragrunt можно прочитать в моём файле на GitHub.

Шаг 2: Устанавливаем HashiCorp Vault

Если у вас уже стоит Hashicorp Vault, пропускайте раздел.

helm install vault oci://registry-1.docker.io/bitnamicharts/vault --version 0.2.1 -n vault --create-namespace

Начнётся инициализация и распечатывание хранилища. Ждем, когда vault-server-0 перейдет в состояние Running.

$ kubectl get pods --namespace "vault" -l app.kubernetes.io/instance=vault NAME                              READY   STATUS    RESTARTS   AGE vault-injector-6f8fb5dcff-9zhgm   1/1     Running   0          51s vault-server-0                    0/1     Running   0          51s 

Инициализируйте один сервер хранилища с количеством общих ключей по умолчанию и пороговым значением ключа по умолчанию:

$ kubectl exec -ti vault-server-0 -n vault -- vault operator init Unseal Key 1: ircItR+/QeLTsve4J3zqw9SPGhC5rDAuxZcz4jr8u4N/ Unseal Key 2: T/L9vkkm3+uo0H9XxADNAsr+YUCSPctnLC/011S79AVg Unseal Key 3: gmSD+fb5A+4P7N3QLDtoLtzuiXnQuhE/Y4uB1RTlRU6h Unseal Key 4: nMTwQQy81EPS8eS//kNicnvE5botOc5jyjHVuuqGkva7 Unseal Key 5: 1roVdnVqvZnHroOijEZFMiueeGE1WF2WWRUWJ4MWCjyF  Initial Root Token: hvs.bY28dn6cKyp6JCvPLTV3Eufb

В выходных данных отображаются общие ключи и сгенерированный исходный корневой ключ. Распечатайте сервер HashiCorp vault с общими ключами до тех пор, пока не будет достигнуто пороговое значение ключа:

kubectl exec -ti vault-server-0 -n vault -- vault operator unseal # ... Unseal Key 1 kubectl exec -ti vault-server-0 -n vault -- vault operator unseal # ... Unseal Key 2 kubectl exec -ti vault-server-0 -n vault -- vault operator unseal # ... Unseal Key 3

В отдельном терминале пробросьте порт 8200 от HashiCorp Vault.

Шаг 3: настраиваем AppRole

Экспортируйте адрес HashiCorp Vault.

export VAULT_ADDR=http://127.0.0.1:8200

Вы можете настроить AppRole в Vault из CLI либо через Terraform-код. Настроим AppRole Vault через Terraform. Создайте setting-approle-vault.tf со следующим содержимым:

# Включение engine kv из CLI. resource "vault_mount" "kvv2-secret" {   path        = "secret"   type        = "kv"   options     = { version = "2" }   description = "KV Version 2 secret engine mount" }  # Включение approle из CLI. resource "vault_auth_backend" "approle" {   type = "approle" }  # Создание политики для чтения по пути secret/data/postgres resource "vault_policy" "secret-read-policy" {   name = "read-policy"    policy = <<EOT path "secret/data/postgres" {   capabilities = ["read", "list"] } EOT }  # Создание роль для approle. resource "vault_approle_auth_backend_role" "secret" {   backend        = vault_auth_backend.approle.path   role_name      = "secret"   token_policies = ["read-policy"] }  # Создание секретного идентификатора (secret_id) resource "vault_approle_auth_backend_role_secret_id" "id" {   backend   = vault_auth_backend.approle.path   role_name = vault_approle_auth_backend_role.secret.role_name }  # Получение id роли approle (role_id) output "role_id" {   value     = vault_approle_auth_backend_role.secret.role_id   sensitive = true }  # Получение секретного идентификатора (secret_id) output "secret_id" {   value     = vault_approle_auth_backend_role_secret_id.id.secret_id   sensitive = true } 

Пример setting-approle-vault.tf есть в директории vault-resource. Экспортируем токен (в данном случае root-токен).

export VAULT_TOKEN=hvs.bY28dn6cKyp6JCvPLTV3Eufb

Применим конфигурацию Terraform.

terraform init terraform apply

Создайте секрет из CLI.

$ vault kv put secret/postgres POSTGRES_USER=admin POSTGRES_PASSWORD=123456 ==== Secret Path ==== secret/data/postgres  ======= Metadata ======= Key                Value ---                ----- created_time       2023-06-13T04:02:53.164920751Z custom_metadata    <nil> deletion_time      n/a destroyed          false version            1 

Обратим внимание, что Secret Path имеет значение secret/data/postgres с data , так как используется KV версии 2. Именно этот Secret Path прописываем в external-secret.yaml. В итоге должно получиться как на скриншоте.

Выводим на экран терминала role-id и secret-id и запоминаем их значение.

terraform output role_id terraform output secret_id

Если вам интересно настроить AppRole в Vault из CLI, настройка описана в отдельном файле create-approle-vault-cli.md

Шаг 4: настраиваем External Secrets Operator

Добавляем helm repo External Secrets Operator.

helm repo add external-secrets https://charts.external-secrets.io

Устанавливаем External Secrets Operator.

helm install external-secrets \ external-secrets/external-secrets \     --wait \     -n external-secrets \     --create-namespace \     --version 0.8.3 \     --set installCRDs=true

Настраиваем external-secrets.

  • Указываем role_id в файле external-secrets/secret-store.yaml в поле roleId.

  • Указываем secret_id в файле external-secrets/vault-secret.yaml в поле secret-id. Файлы конфигурации в директории external-secrets подробно документированы.

Применяем yaml из директории external-secrets.

kubectl apply -f external-secrets/vault-secret.yaml kubectl apply -f external-secrets/secret-store.yaml kubectl apply -f external-secrets/external-secret.yaml

Дебаг: ClusterSecretStore c названием vault-backend должен иметь CAPABILITIES - ReadWrite.

$ kubectl get ClusterSecretStore vault-backend NAME            AGE     STATUS   CAPABILITIES   READY vault-backend   3m19s   Valid    ReadWrite      True

Если ExternalSecret имеет статус SecretSyncedError, смотрим describe.

$ kubectl get ExternalSecret -n external-secrets external-secret NAMESPACE          NAME              STORE           REFRESH INTERVAL   STATUS              READY external-secrets   external-secret   vault-backend   5s                 SecretSyncedError   False

Смотрим describe ExternalSecret.

kubectl describe ExternalSecret -n external-secrets external-secret

Если видим ошибку permission denied, значит, неправильно настроили пути.

Code: 403. Errors:  * 1 error occurred:   * permission denied

Шаг 5: тестируем AppRole, используя Vault CLI

Входим в Vault, используя role_id и secret_id

$ vault write auth/approle/login role_id="" secret_id="" Key                     Value ---                     ----- token                   hvs.CAESILZuXjEGHKTTUD7WjNKDXijGSDLrWTWvE6xzB6O2BXrxGh4KHGh2cy5tZEZhNFVIODdhUktjRDViQVFaUmswc20 token_accessor          tNDR6kn0R3rM1idryFOkSBmi token_duration          768h token_renewable         true token_policies          ["default" "read-policy"] identity_policies       [] policies                ["default" "read-policy"] token_meta_role_name    data 

Получаем список секретов.

vault kv list secret

Прочитаем секрет.

vault kv get secret/postgres

Если получаем ошибку, то меняем политику и проверяем правильность путей.

resource "vault_policy" "secret-read-policy" {   name = "read-policy"    policy = <<EOT path "*" {   capabilities = ["read", "list"] } EOT } 

В итоге мы создали механизм синхронизации секретов из HashiCorp Vault  в Kubernetes секреты, используя external-secrets-operator. Это позволяет безопасно и централизованно хранить ключи и удобно управлять ими.

External Secrets Operator также имеет множество других функций и Custom Resources, которые позволяют настроить секреты в Kubernetes так, как это лучше всего соответствует вашим потребностям.

Полезные ссылки


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


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *