Описание Secret Manager

Введение

Secret Manager в Altflow отвечает за безопасное хранение и выдачу секретов: паролей, токенов, ключей доступа к реестрам, ключей подписи и других чувствительных данных. В качестве Secret Manager используется OpenBao (форк HashiCorp Vault).

OpenBao используется в двух сценариях:

  • хранение системных секретов Altflow, которые нужны helm-чартам и компонентам в Kubernetes;
  • хранение пользовательских секретов сборок: данных доступа к git-репозиторию и OCI registry для конкретного TaskID.

В проекте используется KV-хранилище версии 1 без версионирования. При обновлении секрета старое значение перезаписывается, поэтому перед изменениями нужно сохранять резервную копию.

Цели

  • Обеспечить безопасное хранение секретов приложений.
  • Обеспечить безопасное хранение данных аутентификации в git-репозиториях и OCI-реестрах пользователей системы сборки.
  • Упростить доступ к секретам для компонентов системы.
  • Поддержать аудит и контроль доступа к секретам.

Архитектура

Система состоит из следующих частей:

  • OpenBao: основное хранилище секретов.
  • External Secrets Operator: синхронизирует системные секреты из OpenBao в Kubernetes Secrets.
  • Kubernetes Secrets: источник секретов для компонентов Altflow внутри кластера.
  • Клиенты OpenBao: компоненты, которые напрямую читают или записывают секреты сборок.

Компоненты Altflow в Kubernetes обычно не обращаются напрямую к OpenBao за системными секретами. Эти секреты синхронизируются External Secrets Operator и затем монтируются в pod как переменные окружения или файлы.

Общие принципы работы с OpenBao

  • Mount point: kv.
  • Тип хранилища: KV v1.
  • Путь системных секретов: kv/altflow-system-helm-secrets/.
  • Путь секретов сборок: kv/altflow-builds/.
  • Секреты хранятся как JSON-объекты.
  • Доступ ограничивается policies OpenBao.
  • Для доступа из Kubernetes используется AppRole или Kubernetes auth.
  • Для подключений к OpenBao должен использоваться TLS.

Структура секретов в OpenBao

Системные helm-секреты

Системные секреты лежат по пути:

kv/altflow-system-helm-secrets/<имя_ключа>

Актуальные шаблоны находятся по ссылке.

КлючПоляНазначение
api-lite-secretsamqp_password, amqp_username, metrics_token, trackdb_password, trackdb_username, loki_login, loki_passwd, auth_tokenУпрощённый набор секретов API.
api-secretsamqp_password, amqp_username, trackdb_password, trackdb_username, metrics_token, sysdb_password, sysdb_user, loki_passwd, loki_login, keycloak_client_id, keycloak_client_secret, token_secretОсновной набор секретов API.
distributor-secretsHARBOR_NAME, HARBOR_TOKEN, OPENBAO_TOKEN, PASSWORD, POSTGRES_PASSWORD, POSTGRES_USER, RABBITMQ_PASSWORD, RABBITMQ_USER, USERNAMEСекреты Distributor для Harbor, OpenBao, TrackDB, PostgreSQL и RabbitMQ.
internal-registrydockerconfigDocker config для доступа к внутреннему registry.
oci-registrydockerconfigDocker config для доступа к внешнему OCI registry.
rabbitmq-secretspassword, erlangCookieПароль RabbitMQ и Erlang cookie.
saver-secretsamqp_password, amqp_username, open_bao_token, trackdb_password, trackdb_usernameСекреты Saver для RabbitMQ, OpenBao и TrackDB.
sign-job-credsVAULT_TOKENТокен OpenBao для задач подписи.
stager-secretsPASSWORD, RABBITMQ_PASSWORD, RABBITMQ_USER, USERNAMEСекреты Stager для TrackDB и RabbitMQ.
tracker-secretsTRACKDB_PASSWORD, TRACKDB_USERСекреты Tracker для TrackDB.

Пример загрузки системного секрета:

bao kv put kv/altflow-system-helm-secrets/api-secrets @api-secrets.json

Секреты сборок

Пользователи системы сборки образов при обращении к API передают информацию о расположении git-репозитория и данные для доступа к нему и к OCI registry назначения. Эта информация хранится по схеме:

kv/altflow-builds/<buildID>/git
kv/altflow-builds/<buildID>/registry

Пример структуры данных:

altflow-builds/
└── <buildID>/
    ├── git/
    │   ├── username: gituser
    │   └── password: gitpassword
    └── registry/
        ├── username: registryuser
        └── password: registrypassword

Получение секретов git:

curl -s \
  --header "X-Vault-Token: <token>" \
  --header "Content-Type: application/json" \
  https://<vault_address>/v1/kv/altflow-builds/<buildID>/git | jq -r '.data'

Запись секретов git:

curl -s \
  --request POST \
  --header "X-Vault-Token: <token>" \
  --header "Content-Type: application/json" \
  --data '{"username": "<gituser>", "password": "<gitpassword>"}' \
  https://<vault_address>/v1/kv/altflow-builds/<buildID>/git

Для registry используется тот же формат:

curl -s \
  --request POST \
  --header "X-Vault-Token: <token>" \
  --header "Content-Type: application/json" \
  --data '{"username": "<registryuser>", "password": "<registrypassword>"}' \
  https://<vault_address>/v1/kv/altflow-builds/<buildID>/registry

Аутентификация

Для прямого доступа к OpenBao приложения используют токен из переменной среды SM_BAO_TOKEN. Запрос должен передавать токен в заголовке X-Vault-Token.

curl -s \
  --header "X-Vault-Token: ${SM_BAO_TOKEN}" \
  --header "Content-Type: application/json" \
  https://<vault_address>/v1/kv/<path>

Для доступа из Kubernetes используется AppRole или Kubernetes auth. Роль должна выдавать токен с минимально необходимыми правами на чтение системных секретов и создание/обновление секретов сборок.

Пример policy:

path "kv/altflow-system-helm-secrets/*" {
  capabilities = ["read", "list"]
}

path "kv/altflow-builds" {
  capabilities = ["create", "update", "read", "list"]
}

path "kv/altflow-builds/*" {
  capabilities = ["create", "update", "read", "list"]
}

Интеграция с Kubernetes

В Kubernetes системные секреты синхронизируются через External Secrets Operator. Для каждого компонента создаётся ExternalSecret, который читает поля из OpenBao и создаёт или обновляет обычный Kubernetes Secret.

Пример:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: altflow-api-secrets
spec:
  refreshInterval: 1m
  secretStoreRef:
    name: openbao-store
    kind: SecretStore
  target:
    name: altflow-api-secrets
  data:
    - secretKey: amqp_password
      remoteRef:
        key: kv/altflow-system-helm-secrets/api-secrets
        property: amqp_password

После синхронизации компонент использует Kubernetes Secret:

env:
  - name: amqp_password
    valueFrom:
      secretKeyRef:
        name: altflow-api-secrets
        key: amqp_password

Этапы создания SecretStore и ExternalSecret выполняются helm-чартами. Вручную нужно подготовить значения секретов в OpenBao и учётные данные для доступа Kubernetes к OpenBao.

Ключ подписи OCI-образов

Для создания ключа подписи используется transit engine:

bao secrets enable -description='key for sign oci-images altflow test env' transit
bao write transit/keys/altflow-test-key \
  derived=false \
  exportable=true \
  allow_plaintext_backup=true \
  type=ecdsa-p256 \
  auto_rotate_period=0s

Создание и обновление секретов

При первоначальной загрузке администратор:

  1. Получает root-токен или другой административный токен OpenBao.
  2. Включает KV v1 на mount point kv.
  3. Загружает JSON-шаблоны в kv/altflow-system-helm-secrets/.
  4. Создаёт policies и настраивает AppRole или Kubernetes auth.
  5. Передаёт данные доступа helm-чартам, которые создают SecretStore и ExternalSecret.

При регулярном обновлении нужно заменить соответствующий JSON целиком. Так как используется KV v1, частичное обновление без сохранения остальных полей может привести к потере данных. Перед изменением секрета нужно сохранить текущую версию.

Требования к секретам

  • Пароли и токены должны генерироваться CSPRNG, например openssl rand -base64 18.
  • JWT/HMAC-секреты должны быть не короче 32 байт, рекомендуется 64 байта.
  • Docker config в полях dockerconfig должен быть валидным JSON, сохранённым как строка.
  • Секреты не должны попадать в Git в заполненном виде; в репозитории допустимы только пустые шаблоны.
  • Ротация паролей, токенов и JWT-секретов должна выполняться регулярно, рекомендуемый интервал — не реже одного раза в 90 дней.

Мониторинг и аудит

  • В OpenBao должен быть включён audit log.
  • Нужно отслеживать неуспешные попытки аутентификации и подозрительные обращения к секретам.
  • В Kubernetes нужно контролировать состояние ExternalSecret и ошибки синхронизации.