Почти у каждого проекта есть момент, который вспоминаешь с неловкостью: ранний коммит, в котором в репозитории лежит файл .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 — у него всё.

Что делать:

  1. Включить encryption at rest для etcd. В managed-кластерах (GKE, EKS, AKS) это обычно включено по умолчанию.
  2. Использовать External Secrets Operator — он синхронизирует секреты из Vault или облачного secrets manager в Kubernetes Secrets, и вам не нужно хранить чувствительные данные в манифестах.
  3. Ограничить 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

Не нужно переписывать всё сразу. Постепенный путь:

  1. Прогоните gitleaks по репозиторию — убедитесь, что в истории нет утечек
  2. Выберите инструмент по размеру команды: Doppler или Infisical для небольших проектов, Vault или облачный secrets manager для инфраструктуры с Kubernetes
  3. Мигрируйте самые критичные секреты первыми — платёжные ключи, пароли от продакшен-базы
  4. Настройте автоматическую проверку в CI — никаких секретов в коде
  5. Настройте ротацию хотя бы для базы данных

Когда в 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

Общий принцип один: секреты не должны жить в файлах на серверах и тем более в репозитории. Централизованное хранилище с аудитом, ротацией и гранулярным доступом — это не усложнение, а то, что делает инфраструктуру управляемой.