Рецепт 27. OpenBao в Kubernetes с PostgreSQL-бэкендом на CloudNativePG

OpenBao (форк Vault от Linux Foundation) хранит секреты в PostgreSQL, а кластер держит CloudNativePG: подключение по клиентским TLS-сертификатам без паролей, схема таблиц под HA-режим и подводный камень с ротацией сертификатов.
Опубликовано:

Оригинал: CNPG Recipe 27 — Running OpenBao on Kubernetes with a CloudNativePG PostgreSQL backend — 17 августа 2026. Адаптивный перевод; манифесты и SQL — как в оригинале.

OpenBao — форк HashiCorp Vault под эгидой Linux Foundation. У него есть storage-бэкенд postgresql, который превращает обычный PostgreSQL-кластер в зашифрованное key-value хранилище для секретов. Смысл связки прост: хранилище секретов работает поверх той же БД, которую вы уже умеете эксплуатировать, а Raft-кластер разворачивать не нужно.

Кластер PostgreSQL

Ключевые моменты манифеста — три реплики, синхронная репликация и аутентификация по сертификатам вместо паролей:

YAML
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: openbao-db
  namespace: openbao
spec:
  instances: 3
  imageCatalogRef:
    apiGroup: postgresql.cnpg.io
    kind: ClusterImageCatalog
    name: postgresql-minimal-trixie
    major: 18
  postgresql:
    synchronous:
      method: any
      number: 1
    pg_hba:
      - hostssl openbao openbao all cert
      - hostssl openbao openbao-rw all cert
Нажмите, чтобы развернуть и увидеть больше

Здесь всё сходится с тем, что разбирали раньше: image catalog из Рецепта 22, минимальный образ PostgreSQL 18 из статьи про релиз, а сертификаты для ролей выпускаются через DatabaseRole из Рецепта 25. Паролей в схеме нет вообще — правила pg_hba требуют cert.

Схема таблиц

В HA-режиме (ha_enabled = "true") OpenBao ожидает две таблицы: хранилище ключей и таблицу блокировок для выбора активного узла.

SQL
CREATE TABLE IF NOT EXISTS openbao_kv_store (
  parent_path TEXT NOT NULL,
  path        TEXT NOT NULL,
  key         TEXT NOT NULL,
  value       BYTEA,
  CONSTRAINT openbao_kv_store_pkey PRIMARY KEY (path, key)
);

CREATE TABLE IF NOT EXISTS openbao_ha_locks (
  ha_key       TEXT NOT NULL,
  ha_identity  TEXT NOT NULL,
  ha_value     TEXT,
  valid_until  TIMESTAMP WITH TIME ZONE NOT NULL,
  CONSTRAINT openbao_ha_locks_pkey PRIMARY KEY (ha_key)
);
Нажмите, чтобы развернуть и увидеть больше

Таблицы создаются заранее, а роли openbao-rw выдаются только права DML — без CREATE. Это принцип наименьших привилегий из Рецепта 3 в чистом виде: приложение не должно уметь менять схему.

Конфигурация OpenBao

HCL
storage "postgresql" {
  connection_url    = "postgres://openbao-rw@openbao-db-rw:5432/openbao?sslmode=verify-full&sslcert=/etc/openbao/certs/tls.crt&sslkey=/etc/openbao/certs/tls.key&sslrootcert=/etc/openbao/ca/ca.crt"
  table             = "openbao_kv_store"
  ha_table          = "openbao_ha_locks"
  ha_enabled        = "true"
  skip_create_table = "true"
}
Нажмите, чтобы развернуть и увидеть больше

Два обязательных момента:

Секреты монтируются с defaultMode: 0640: libpq отказывается работать с приватным ключом, у которого права шире, но при этом нужно оставаться в рамках дефолтов Kubernetes.

Высокая доступность и распечатывание

Три реплики OpenBao с обязательной podAntiAffinity по хостам. Важная особенность, которую легко упустить:

Состояние Shamir у каждого пода своё и живёт в памяти. Поэтому распечатывать (unseal) нужно каждый под отдельно, подавая все три ключа по очереди:

BASH
# Инициализация — только на первом поде
kubectl exec -it -n openbao openbao-0 -- bao operator init

# Распечатывание: три ключа на каждый под
kubectl exec -it -n openbao openbao-0 -- bao operator unseal <UNSEAL_KEY_1>
kubectl exec -it -n openbao openbao-0 -- bao operator unseal <UNSEAL_KEY_2>
kubectl exec -it -n openbao openbao-0 -- bao operator unseal <UNSEAL_KEY_3>

# Повторить для openbao-1 и openbao-2
Нажмите, чтобы развернуть и увидеть больше

Из этого следует практическое: любой перезапуск пода требует повторного unseal вручную, если не настроено авто-распечатывание. Планируйте это до продакшена.

Подводный камень: ротация сертификатов

Самая ценная часть рецепта. Клиентские сертификаты живут 90 дней и автоматически обновляются примерно за 7 дней до истечения. Но:

storage-бэкенд postgresql в OpenBao открывает пул соединений один раз при старте процесса и больше никогда не перечитывает файлы сертификатов.

То есть сертификат на диске обновится, а работающий процесс продолжит использовать старый — и в какой-то момент потеряет доступ к базе. Лечится rolling restart подов OpenBao в течение окна обновления (те самые 7 дней). Ставьте себе напоминание или автоматизацию — иначе получите отказ хранилища секретов в момент, который сложно связать с причиной.

Итог

Схема получается аккуратной: секреты лежат в PostgreSQL под управлением CloudNativePG, доступ идёт по TLS-сертификатам без единого пароля, права роли урезаны до DML, а HA обеспечивается таблицей блокировок. Две вещи, которые нужно держать в голове при эксплуатации, — ручной unseal каждого пода и обязательный перезапуск при ротации сертификатов.

Начать поиск

Введите ключевые слова для поиска статей

↑↓
↵
ESC
⌘K Горячая клавиша