Чтение секретов из кода и CSI

Когда хочется обойтись без Agent: приложение само ходит в Vault, либо секреты монтируются томом через CSI.

Прямая интеграция — приложение само логинится в Vault клиентской библиотекой и читает секреты; CSI-провайдер монтирует секреты как файлы тома средствами Kubernetes.

Vault Agent удобен, но иногда приложение работает с Vault напрямую: для гибкости, в не-k8s среде или при единичном чтении на старте. Разберём оба подхода.

Чтение из кода: общий паттерн

Логика одинакова в любом языке: настроить адрес и токен (или залогиниться), прочитать путь, достать поле. Псевдокод на Python (это иллюстрация API, не запускается в браузере):

import hvac  # официальный Python-клиент Vault

client = hvac.Client(url="https://vault.internal:8200")

# логин через AppRole
client.auth.approle.login(
    role_id=ROLE_ID,
    secret_id=SECRET_ID,
)

# чтение динамических кред БД
resp = client.read("database/creds/readonly")
db_user = resp["data"]["username"]
db_pass = resp["data"]["password"]

connect_to_db(db_user, db_pass)

Ключевой момент — секреты получают при старте, не зашивают в образ. Для динамических кредов приложение должно ещё уметь обновлять их до истечения аренды (или отдать это Agent).

Где взять токен в коде

Самое сложное в прямом подходе — первичная аутентификация (secret zero). Варианты: AppRole с SecretID через response wrapping, Kubernetes auth по ServiceAccount-токену пода, облачная identity (AWS/GCP) ВМ. Хардкодить токен нельзя — это возвращает исходную проблему.

CSI: секреты как том

Secrets Store CSI Driver — другой подход в Kubernetes: секреты монтируются в под как файлы тома, без sidecar. Описывается ресурсом SecretProviderClass:

apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: orders-db
spec:
  provider: vault
  parameters:
    roleName: "orders"
    vaultAddress: "https://vault.internal:8200"
    objects: |
      - objectName: "db-password"
        secretPath: "secret/data/orders/db"
        secretKey: "password"

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

Как работает под капотом и выбор подхода

CSI-драйвер при монтировании тома логинится в Vault (через k8s auth) и пишет запрошенные секреты в tmpfs-том пода. Отличие от Injector: нет постоянного sidecar — обновление секретов зависит от ротации тома и устроено иначе, чем непрерывное продление Agent. Поэтому для динамических кредов с активным продлением чаще выбирают Agent/Injector, а CSI удобен для статических секретов и единообразного монтирования.

ПодходКогда
код напрямую (hvac и др.)не-k8s, гибкость, единичное чтение
Vault Agent / Injectorдинамика, продление аренды, без правки кода
CSI Driverстатика как том, k8s-нативное монтирование

Частые ошибки

  • Хардкодить токен в коде/образе — secret zero не решён, утечёт вместе с артефактом.
  • Читать секреты один раз и держать вечно для динамики — креды протухнут.
  • Ждать от CSI поведения Agent по непрерывному продлению аренды — это разные модели.

Итог

  • Из кода: залогиниться (AppRole/k8s/облако) и прочитать секреты при старте, не хардкодя токен.
  • CSI Driver монтирует секреты файлами тома без sidecar — удобно для статики.
  • Для динамических кредов с продлением аренды обычно выбирают Agent/Injector.
Проверьте себя
1. Что самое сложное при чтении секретов из кода напрямую?
AПарсинг JSON
BПервичная аутентификация (secret zero) — где безопасно взять способ логина
CСкорость чтения
DВыбор языка
2. Чем CSI Driver отличается от Vault Agent Injector?
ACSI шифрует данные
BCSI монтирует секреты как том без постоянного sidecar; модель обновления иная
CCSI требует root-токен
DОни идентичны
3. Почему нельзя хардкодить токен Vault в образе приложения?
AОбраз станет больше
BЭто возвращает исходную проблему — секрет утечёт вместе с артефактом
CVault это запрещает технически
DТокены не работают из кода