Приложение получает от Stronghold уникальную пару «логин — пароль» к PostgreSQL с ограниченным сроком действия. Stronghold создаёт пользователя в базе данных при запросе и удаляет его по истечении аренды. Пароль не хранится в манифестах и в объектах Secret.

Цель

Настроить выдачу динамических учётных данных PostgreSQL приложению myapp, которое работает в неймспейсе myapp кластера Deckhouse Platform, и доставить их в под с автоматическим продлением.

Предварительные требования

Для выполнения примера нужны:

  • Stronghold, развёрнутый в DP, и токен с правами на настройку механизмов секретов, политик и методов аутентификации.
  • Сервер PostgreSQL, доступный из подов Stronghold, и учётная запись с правом создавать роли (CREATEROLE), например stronghold.
  • Доступ к кластеру через d8 k.

В DP метод аутентификации Kubernetes для текущего кластера создаётся автоматически по пути kubernetes_local. Если вы используете другой путь, замените kubernetes_local в командах ниже.

Шаг 1. Настройте механизм секретов database

Включите механизм секретов database и подключите его к PostgreSQL:

  1. Включите механизм секретов:

    d8 stronghold secrets enable database
  2. Настройте подключение к PostgreSQL:

    d8 stronghold write database/config/myapp-postgres plugin_name="postgresql-database-plugin" allowed_roles="myapp" connection_url="postgresql://{{username}}:{{password}}@postgres.db.svc:5432/myapp?sslmode=require" username="stronghold" password="<INITIAL_PASSWORD>" password_authentication="scram-sha-256"

    <INITIAL_PASSWORD> — текущий пароль учётной записи stronghold. На следующем шаге Stronghold заменит его сгенерированным.

  3. Смените пароль служебной учётной записи, чтобы он был известен только Stronghold:

    d8 stronghold write -force database/rotate-root/myapp-postgres

Шаг 2. Создайте роль базы данных

Роль задаёт SQL-запросы для создания пользователя и срок жизни учётных данных:

d8 stronghold write database/roles/myapp db_name="myapp-postgres" creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" revocation_statements="REVOKE ALL ON ALL TABLES IN SCHEMA public FROM \"{{name}}\"; DROP ROLE IF EXISTS \"{{name}}\";" default_ttl="1h" max_ttl="24h"
  • default_ttl — срок аренды при выдаче и при каждом продлении;
  • max_ttl — максимальный срок жизни учётных данных; после него приложению нужны новые учётные данные.

Шаг 3. Создайте политику

Создайте политику, которая разрешает читать учётные данные роли myapp:

d8 stronghold policy write myapp-db - <<'POLICY'
path "database/creds/myapp" {
  capabilities = ["read"]
}
POLICY

Продление и отзыв собственных аренд и токена разрешены встроенной политикой default.

Шаг 4. Создайте роль метода Kubernetes

Создайте ServiceAccount для приложения и свяжите его с политикой через роль метода Kubernetes:

  1. Создайте неймспейс и ServiceAccount:

    d8 k create namespace myapp
    d8 k -n myapp create serviceaccount myapp
  2. Свяжите ServiceAccount с политикой:

    d8 stronghold write auth/kubernetes_local/role/myapp bound_service_account_names=myapp bound_service_account_namespaces=myapp policies=myapp-db ttl=1h

Шаг 5. Доставьте учётные данные в под

Для динамических учётных данных используйте Stronghold Agent в режиме sidecar: он продлевает аренду и перерисовывает файл, когда учётные данные меняются. Способы, которые подставляют значения только при старте пода (env-injector, init-контейнер), подходят, только если под перезапускается чаще, чем истекает max_ttl.

Конфигурация Agent для этого сценария:

stronghold {
  address = "https://stronghold.example.com"
}

auto_auth {
  method "kubernetes" {
    mount_path = "auth/kubernetes_local"
    config = {
      role = "myapp"
    }
  }
}

template {
  destination = "/secrets/db.env"
  contents    = <<-EOT
  {{ with secret "database/creds/myapp" }}
  DB_USER={{ .Data.username }}
  DB_PASSWORD={{ .Data.password }}
  {{ end }}
  EOT
}

Манифест пода с Agent в режиме sidecar, томом emptyDir в памяти и ConfigMap с конфигурацией приведён в разделе «Доставка секретов в поды Kubernetes».

Шаг 6. Настройте реакцию приложения на смену учётных данных

Agent продлевает аренду каждые default_ttl, пока не будет достигнут max_ttl. После этого Agent запрашивает новые учётные данные и перезаписывает файл /secrets/db.env. Старый пользователь удаляется при отзыве аренды.

Приложение должно:

  • перечитывать файл при изменении и переподключаться к базе данных;
  • либо завершаться при ошибке аутентификации, чтобы Kubernetes перезапустил контейнер с новыми учётными данными.

Выберите max_ttl так, чтобы смена учётных данных происходила не чаще, чем приложение способно корректно переподключиться.

Проверка

Проверьте результат:

  1. Запросите учётные данные вручную с токеном администратора:

    d8 stronghold read database/creds/myapp

    В ответе должны быть поля lease_id, lease_duration, username и password.

  2. Проверьте, что пользователь создан в PostgreSQL:

    SELECT rolname, rolvaliduntil FROM pg_roles WHERE rolname LIKE 'v-%';
  3. Убедитесь, что в поде появился файл с учётными данными:

    d8 k -n myapp exec deploy/myapp -c app -- cat /secrets/db.env
  4. Просмотрите активные аренды роли:

    d8 stronghold list sys/leases/lookup/database/creds/myapp/

Очистка

Удалите объекты, созданные в примере:

  1. Удалите тестовый неймспейс в кластере: d8 k delete namespace myapp.

  2. Отзовите все выданные учётные данные роли:

    d8 stronghold lease revoke -prefix database/creds/myapp/
  3. Удалите роль, политику и конфигурацию:

    d8 stronghold delete auth/kubernetes_local/role/myapp
    d8 stronghold policy delete myapp-db
    d8 stronghold delete database/roles/myapp
    d8 stronghold delete database/config/myapp-postgres

Дополнительные ресурсы