Почти у каждого проекта есть момент, который вспоминаешь с неловкостью: ранний коммит, в котором в репозитории лежит файл .env с паролями от базы данных и API-ключами. Иногда это обнаруживается через пять минут, иногда — через год, когда кто-то запускает git log не в той ветке.
Secrets Management — это не параноя и не энтерпрайзная заморочка. Это базовая гигиена, без которой продакшен-проект рано или поздно получает проблему.
Почему .env на сервере — это не решение
Самый распространённый подход: положить .env на продакшен-сервер, добавить его в .gitignore и считать, что всё в порядке. Звучит разумно, но у этого подхода есть конкретные дыры.
Нет аудита. Кто и когда поменял пароль от базы? Кто добавил новый ключ от платёжной системы? С плоским файлом это невозможно отследить. Когда что-то ломается или утекает — непонятно, откуда тянуть.
Нет ротации. Секреты годами лежат неизменными. Уволился разработчик, который имел доступ к серверу, — а его условный токен продолжает работать. Менять вручную всё, что он знал, никто не будет: слишком много и непонятно, где искать.
Нет изоляции между окружениями. Если один разработчик имеет доступ к .env на продакшене, он автоматически видит все продакшен-секреты. Хотя для работы ему нужны только переменные стейджинга.
Копирование файлов. Очень быстро .env начинает жить в нескольких местах: на сервере, в Slack-переписке, в личных заметках кого-то из команды. Вместо одного источника правды — хаос.
Что такое секреты и как их классифицировать
Прежде чем выбирать инструмент, полезно разобраться, что вообще относится к секретам:
- Учётные данные баз данных — логины, пароли, строки подключения
- API-ключи — Stripe, SendGrid, Twilio, любые внешние сервисы
- Токены аутентификации — JWT secrets, OAuth client secrets
- Сертификаты и приватные ключи — TLS, SSH, GPG
- Конфигурация инфраструктуры — эндпоинты внутренних сервисов, которые не должны быть публичными
Не всё это одинаково критично, и смешивать их в одну кучу неудобно. Хороший secrets manager позволяет разделить их по проектам, окружениям и командам.
Инструменты: от простого к серьёзному
HashiCorp Vault
Vault — промышленный стандарт для тех, кто хочет полный контроль. Это self-hosted решение: вы сами разворачиваете и обслуживаете его.
Что умеет:
- Хранить секреты с версионированием
- Выдавать динамические учётные данные (например, временный логин к PostgreSQL, который истекает через час)
- Аутентифицировать сервисы через Kubernetes, AWS IAM, GitHub и другие провайдеры
- Вести полный аудит-лог
Пример получения секрета через CLI:
vault kv get secret/myapp/database
Или через HTTP API:
curl \
-H "X-Vault-Token: $VAULT_TOKEN" \
http://vault.internal:8200/v1/secret/data/myapp/database
Go-приложение читает секреты напрямую из Vault при старте — никаких файлов на диске. Если секрет меняется, достаточно обновить его в Vault и перезапустить сервис.
Оборотная сторона: Vault нужно поднять, настроить, обеспечить высокую доступность. Для небольшого проекта это может быть избыточно.
AWS Secrets Manager
Если инфраструктура уже на AWS, Secrets Manager — логичный выбор. Не нужно ничего разворачивать самому, всё управляется через IAM.
Стоимость: примерно $0.40 в месяц за один секрет плюс $0.05 за 10 000 API-вызовов. Для среднего проекта это копейки.
Пример на Python:
import boto3
import json
def get_secret(secret_name: str) -> dict:
client = boto3.client('secretsmanager', region_name='eu-central-1')
response = client.get_secret_value(SecretId=secret_name)
return json.loads(response['SecretString'])
db_config = get_secret('myapp/production/database')
Автоматическая ротация — встроенная фича. Для RDS AWS сам меняет пароль и обновляет секрет по расписанию.
Google Secret Manager и Azure Key Vault
Аналоги для соответствующих облаков. Концепция та же: централизованное хранилище, доступ через IAM, аудит, версионирование. Если уже работаете в GCP или Azure, имеет смысл не тащить дополнительный инструмент.
Doppler
SaaS-решение, которое хорошо подходит для небольших и средних команд. Не нужно разворачивать инфраструктуру, есть удобный UI, интеграция с GitHub Actions, Kubernetes, Heroku, Render и десятками других платформ.
Принцип работы: секреты хранятся в Doppler, приложение при старте получает их через Doppler CLI или SDK. Doppler CLI может выступать как оболочка над запуском:
doppler run -- node server.js
Все переменные окружения автоматически подставляются в процесс. Приложение даже не знает, откуда они берутся.
Бесплатный тариф есть, платный — от $7 за пользователя в месяц.
Infisical
Open-source альтернатива Doppler. Можно использовать как cloud-сервис или развернуть self-hosted. Активно развивается, есть SDK для основных языков.
Как правильно передавать секреты в приложение
Есть несколько паттернов, и у каждого свои плюсы и минусы.
Переменные окружения при старте
Классический способ: секреты передаются как environment variables. Приложение читает их через os.getenv или аналог.
Плюс: не нужно менять логику приложения.
Минус: переменные окружения видны через /proc/<pid>/environ и могут попасть в логи при ошибке.
Чтение при старте из secrets manager
Приложение при инициализации само обращается к Vault или Secrets Manager, получает нужные значения и кэширует их в памяти.
func initConfig(ctx context.Context) (*Config, error) {
secret, err := vaultClient.KVv2("secret").Get(ctx, "myapp/database")
if err != nil {
return nil, fmt.Errorf("fetch secret: %w", err)
}
return &Config{
DBPassword: secret.Data["password"].(string),
}, nil
}
Плюс: нет файлов, нет переменных окружения с секретами.
Минус: нужна зависимость от secrets manager при старте. Если Vault недоступен — приложение не стартует.
Sidecar-паттерн в Kubernetes
В Kubernetes популярен подход с init-контейнером или sidecar, который при запуске пода получает секреты из Vault и записывает их в shared volume как временные файлы.
Vault Agent Injector делает это автоматически через аннотации:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-inject-secret-database: "secret/myapp/database"
vault.hashicorp.com/role: "myapp"
Приложение читает файл /vault/secrets/database — и не знает ни о каком Vault.
Kubernetes Secrets: что с ними не так
В Kubernetes есть встроенный тип ресурса Secret. Многие думают, что этого достаточно.
Проблема в том, что Kubernetes Secrets по умолчанию хранятся в etcd в base64-кодировке — это не шифрование. Любой, кто может читать секреты в namespace, видит их в открытом виде. И если кто-то получит доступ к etcd — у него всё.
Что делать:
- Включить encryption at rest для etcd. В managed-кластерах (GKE, EKS, AKS) это обычно включено по умолчанию.
- Использовать External Secrets Operator — он синхронизирует секреты из Vault или облачного secrets manager в Kubernetes Secrets, и вам не нужно хранить чувствительные данные в манифестах.
- Ограничить RBAC. Доступ к секретам должен быть только у тех сервисов и пользователей, которым он действительно нужен.
Пример External Secrets:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: myapp-database
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: myapp-database
data:
- secretKey: password
remoteRef:
key: secret/myapp/database
property: password
Ротация секретов: почему это важно
Даже если секрет не утёк, его стоит менять регулярно. Причина простая: вы никогда не знаете наверняка, что он не скомпрометирован. Ротация ограничивает окно, в котором старый секрет может быть использован злоумышленником.
Практические рекомендации:
- API-ключи внешних сервисов — ротировать при смене сотрудников, имевших к ним доступ, или каждые 90 дней
- Пароли баз данных — автоматическая ротация раз в месяц
- JWT secrets — настроить короткое время жизни токенов, а сам secret менять раз в квартал
- Сертификаты — использовать Let's Encrypt с автопродлением или cert-manager в Kubernetes
Good secrets manager сам уведомит об истекающих секретах и поможет автоматизировать ротацию.
Типичные ошибки
Хардкод в коде. password = "super_secret_123" — это встречается даже в production-коде опытных команд. Инструменты типа gitleaks или trufflehog помогают ловить такие вещи до попадания в репозиторий:
gitleaks detect --source . --verbose
Добавьте это в pre-commit хук или CI-пайплайн.
Логирование секретов. Разработчик добавляет отладочный вывод console.log(config) — и в логах оказываются пароли. Правило простое: никогда не логировать объекты конфигурации целиком.
Один секрет для всех окружений. Продакшен-ключ от Stripe не должен быть в стейджинге. У каждого окружения — свои секреты. Это позволяет выдавать разработчикам доступ к стейджингу без риска скомпрометировать продакшен.
Избыточные права. Приложение использует только одну таблицу базы данных, но подключается под root. Принцип минимальных привилегий работает и здесь: каждый сервис должен иметь доступ только к тем ресурсам, которые ему действительно нужны.
Отсутствие аудита. Если нет логов того, кто и когда обращался к секрету — расследовать инцидент будет невозможно.
С чего начать, если сейчас всё в .env
Не нужно переписывать всё сразу. Постепенный путь:
- Прогоните
gitleaks по репозиторию — убедитесь, что в истории нет утечек
- Выберите инструмент по размеру команды: Doppler или Infisical для небольших проектов, Vault или облачный secrets manager для инфраструктуры с Kubernetes
- Мигрируйте самые критичные секреты первыми — платёжные ключи, пароли от продакшен-базы
- Настройте автоматическую проверку в CI — никаких секретов в коде
- Настройте ротацию хотя бы для базы данных
Когда в REEXY ведут разработку для клиентов — корпоративные сайты, интернет-магазины, интеграции с внешними сервисами — secrets management закладывается в архитектуру с самого начала, а не добавляется потом. Это один из тех вопросов, где исправлять дороже, чем сделать правильно сразу.
Выбор инструмента: краткая шпаргалка
| Ситуация |
Что выбрать |
| Маленькая команда, нет DevOps-экспертизы |
Doppler или Infisical Cloud |
| Инфраструктура на AWS |
AWS Secrets Manager |
| Инфраструктура на GCP |
Google Secret Manager |
| Kubernetes, нужен полный контроль |
HashiCorp Vault + External Secrets |
| Нужно self-hosted и open-source |
Infisical self-hosted |
Общий принцип один: секреты не должны жить в файлах на серверах и тем более в репозитории. Централизованное хранилище с аудитом, ротацией и гранулярным доступом — это не усложнение, а то, что делает инфраструктуру управляемой.