Короткий ответ
Секреты не должны лежать в репозитории ни в каком виде — их выносят в секрет-хранилище (например, HashiCorp Vault, AWS/GCP Secrets Manager) или, как минимум, в защищённые переменные окружения CI/CD и оркестратора, с разными значениями и правами доступа на каждое окружение.
Как это работает подробнее
Приложение при старте запрашивает нужные секреты у хранилища по токену/роли сервиса (например, через Vault Agent или SDK) либо получает их как переменные окружения, инжектированные платформой деплоя (Kubernetes Secrets, облачный Secrets Manager), а не читает значения из файлов в git.
Пример кода
config.yaml
apiVersion: v1
kind: Secret
metadata:
name: service-db-credentials
type: Opaque
data:
password: <base64-encoded-value>
# Приложение читает пароль как переменную окружения, смонтированную из Secret,
# и никогда не логирует весь объект конфигурации целикомЧто использовать на практике
- Практика. Для production стоит разделять секреты по уровню доступа (отдельная роль для чтения секретов только нужного сервиса), включать ротацию (особенно для долгоживущих ключей доступа к облаку) и аудит обращений к хранилищу; для dev допустимо использовать упрощённый .env с тестовыми значениями, явно исключённый из git.
Подводные камни
- Риск. Используют один и тот же секрет на всех окружениях, из-за чего компрометация dev-окружения даёт доступ к production; хранят секреты в CI-переменных без ограничения видимости по веткам/окружениям, из-за чего они попадают в логи сборки или доступны форкам.