Перейти к основному содержимому

Секреты

dagstack/config не хранит секреты — он правильно их передаёт и маскирует в диагностике. Ответственность за хранение секретов остаётся за внешними инструментами (env-переменные, Vault, Kubernetes Secret, облачные менеджеры секретов).

:::caution Planned API Методы маскирования (to_masked_dict / toMaskedDict / ToMaskedMap), константа SECRET_PATTERNS и параметр secret_patterns=/WithSecretPatterns из примеров ниже — запланированный API (Phase 2+). В текущих v0.1/v0.2 биндингах (config-python v0.2.0, config-go v0.1.0, @dagstack/config v0.1.0) он ещё не реализован; нормативный контракт фиксируется в config-spec/_meta/secret_patterns.yaml и ADR-поправке к §4. До реализации выводите в логи только те ключи, которые пометили secret-ами сами. :::

Базовое правило

Секрет — это env-переменная, попадающая в конфиг через ${VAR}. Никогда не пишите значение секрета в app-config.yaml или app-config.${ENV}.yaml:

НЕПРАВИЛЬНО — секрет попадает в git
database:
password: "s3cr3t-prod-pw"
ПРАВИЛЬНО — секрет из окружения, не коммитится
database:
password: "${DB_PASSWORD}"

Auto-маскирование по именам полей

При выводе конфига в логи / диагностику / error-messages — секретные поля автоматически заменяются на [MASKED]. Список паттернов зафиксирован нормативно в config-spec/_meta/secret_patterns.yaml:

Точное совпадениеСуффикс
api_key*_secret
secret_key*_token
access_token*_password
password*_key
client_secret

Примеры полей, которые будут замаскированы:

  • database.password (точное)
  • cache.auth_token (суффикс _token)
  • auth.jwt_secret (суффикс _secret)
  • payment.stripe_api_key (суффикс _key)
  • webhook.signing_password (суффикс _password)

Не маскируется:

  • database.host — не попадает в паттерны.
  • database.url — URL'ы могут содержать учётки (postgresql://user:pass@host), но на это паттерн не рассчитан; передавайте password отдельным полем.
  • Custom-поле internal_key_id — содержит _key, НО: проверяется суффикс _keyсовпадает. Если это не секрет (просто id), переименуйте: internal_id / key_name.

Диагностика маскируется

Когда приложение логирует конфиг на старте для self-check:

import json
print(json.dumps(config.to_masked_dict(), indent=2))
{
"database": {
"host": "localhost",
"user": "app",
"password": "[MASKED]",
"pool_size": 20
},
"payment": {
"provider": "stripe",
"stripe_api_key": "[MASKED]",
"webhook_secret": "[MASKED]"
}
}

В исключениях и ошибках маскирование применяется автоматически — ConfigError в сообщении показывает [MASKED] вместо значения.

.local.yaml — для некоммитаемых переопределений

Если нужно временно переопределить секрет локально для отладки, используйте app-config.local.yaml:

app-config.local.yaml
database:
password: "local-dev-pw" # ok — этот файл в .gitignore

Файл должен быть в .gitignore. Стандартная структура .gitignore в dagstack-проектах содержит:

# dagstack config — локальные переопределения разработчика, не коммитятся.
app-config.local.yaml

Альтернатива — .env файл с переменной, читаемой через ${VAR}:

.env (gitignored)
DB_PASSWORD=local-dev-pw

Публичный менеджер секретов (Vault / K8s Secret)

В production-режиме секреты обычно приходят из централизованного менеджера секретов:

HashiCorp Vault через envconsul / agent:

# envconsul запускает приложение с env-переменными из Vault
envconsul -config=vault.hcl -- python main.py

Kubernetes Secret через env:

kubernetes/deployment.yaml
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
- name: PAYMENT_API_KEY
valueFrom:
secretKeyRef:
name: payment-secrets
key: stripe-api-key

dagstack/config видит ${DB_PASSWORD} в YAML — источник переменной (env / Vault / K8s) для него прозрачен.

В Phase 2+ появятся прямые адаптеры (VaultSource, KubernetesSecretSource) — компонент сможет читать секрет из Vault без env-прослойки. Но env — универсальный «наименьший общий знаменатель» для текущей версии.

Что делать при обнаруженной утечке

Если обнаружили, что секрет попал в git (например, app-config.yaml с паролем в открытом виде или с API-ключом):

  1. Немедленно отозвать секрет в соответствующем сервисе (в БД — сменить пароль пользователя; в API-провайдере — revoke key).
  2. Выпустить новый секрет, обновить env-переменные в CI / production.
  3. Переписать YAML на ${VAR}-подстановку.
  4. Очистить git-историю (git filter-branch / BFG Repo Cleaner) — если репозиторий ещё не публичен.
  5. Если репозиторий был публичный — считайте секрет скомпрометированным независимо от очистки истории.

Добавление своих secret-паттернов

Если приложение использует кастомные поля-секреты, не покрытые стандартным списком, расширьте:

from dagstack.config import Config, SECRET_PATTERNS

custom_patterns = SECRET_PATTERNS + ["connection_string", "*_pin"]
config = Config.load("app-config.yaml", secret_patterns=custom_patterns)

Стандартные 9 паттернов покрывают 95% кейсов; кастомизация — для специфичных терминов вашего домена.

См. также