Secrets Management
Техническое руководство из исходного проекта Notty. Примеры, параметры и эксплуатационные ограничения.
Документ описывает «секретный контур» Notty CMS: где какие секреты живут, как они доставляются в runtime и как их ротировать без простоя.
Реестр секретов
| Имя | Что это | Где используется | Критичность |
|---|---|---|---|
JWT_SECRET |
Подпись JWT для admin сессий и API token | nitro.config.ts → JWT, auth-middleware |
high |
NITRO_JWT_SECRET |
Runtime override для прямого Nitro output | useRuntimeConfig().jwt.secret |
high |
DATABASE_URL |
Креды primary БД | lib/database.ts |
high |
NITRO_DATABASE_URL |
Runtime override для прямого Nitro output | useRuntimeConfig().database.url |
high |
DATABASE_REPLICA_URLS |
Креды read replicas | lib/db-query.ts |
medium |
STORAGE_ACCESS_KEY_ID |
S3/R2 access key | lib/storage/S3StorageProvider.ts |
high |
STORAGE_SECRET_ACCESS_KEY |
S3/R2 secret key | то же | high |
NITRO_STORAGE_ACCESS_KEY_ID |
Runtime override для прямого Nitro output | useRuntimeConfig().storage.accessKeyId |
high |
NITRO_STORAGE_SECRET_ACCESS_KEY |
Runtime override для прямого Nitro output | useRuntimeConfig().storage.secretAccessKey |
high |
STORAGE_CREDENTIALS_JSON |
GCS service account JSON | lib/storage/GCSStorageProvider.ts |
high |
GOOGLE_/GITHUB_/VK_/FACEBOOK_* |
OAuth client_id + secret | lib/oauth.ts |
medium |
| API tokens (в БД) | Внешний доступ к Content/Admin API | lib/api-tokens.ts |
depends |
| Webhook signing keys (в БД) | HMAC-подпись webhook payload | lib/webhooks.ts |
medium |
«В БД» означает, что секрет хранится в Postgres (хешируется или шифруется на прикладном уровне) — он автоматически попадает в бэкап, поэтому бэкап БД тоже должен быть зашифрован при покое.
Принципы
- Никогда в Git.
.env.*(кроме.env.example) — в.gitignore. Любой k8sSecret— в виде шаблона (secret.example.yaml); реальные значения приходят из External Secrets / sealed-secrets / vault-injector. - Один источник правды. Выберите один backend (Vault, AWS Secrets Manager, GCP Secret Manager, 1Password Connect) и ходите всегда туда. Ручные копии в нескольких местах — путь к рассинхрону.
- Запись только через pipeline. Менять секреты ручкой в проде запрещено; все изменения — через CI или утвержденный admin-runbook.
- Read-only для приложения. Notty не должен иметь прав менять секреты в backend'е. Только читать на старте/при reload.
- Шифрование at rest. Бэкапы БД и объектное хранилище — с шифрованием. Доступ — по короткоживущим IAM-токенам, а не по статическим ключам.
Контуры
docker-compose / VM
.env-файл с правами600(chmod 600 .env), владелец — пользователь, под которым запущен Docker.JWT_SECRETиPOSTGRES_PASSWORDгенерируются один раз черезdeployment/scripts/secret-rotate-jwt.sh --initиopenssl rand -base64 32.- Резервная копия
.envхранится в защищённом месте (например, password-менеджер команды), не в repo. - Опционально подключите
docker secret(Swarm) или--env-file /run/secrets/...с tmpfs-mount.
Kubernetes
Рекомендуемый паттерн — External Secrets Operator (ESO) с источником в Vault / AWS Secrets Manager / GCP Secret Manager:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: notty-secrets
namespace: notty
spec:
refreshInterval: 5m
secretStoreRef:
name: cluster-secret-store
kind: ClusterSecretStore
target:
name: notty-secrets
creationPolicy: Owner
data:
- secretKey: JWT_SECRET
remoteRef: { key: notty/prod/jwt }
- secretKey: NITRO_JWT_SECRET
remoteRef: { key: notty/prod/jwt }
- secretKey: DATABASE_URL
remoteRef: { key: notty/prod/db_url }
- secretKey: NITRO_DATABASE_URL
remoteRef: { key: notty/prod/db_url }
- secretKey: STORAGE_ACCESS_KEY_ID
remoteRef: { key: notty/prod/storage_access_key }
- secretKey: NITRO_STORAGE_ACCESS_KEY_ID
remoteRef: { key: notty/prod/storage_access_key }
- secretKey: STORAGE_SECRET_ACCESS_KEY
remoteRef: { key: notty/prod/storage_secret_key }
- secretKey: NITRO_STORAGE_SECRET_ACCESS_KEY
remoteRef: { key: notty/prod/storage_secret_key }
Альтернативы:
- Sealed Secrets — секреты лежат в Git зашифрованными; меняются через
kubeseal. Проще, но без centralised audit. - Vault Agent Injector — sidecar вкатывает секреты в файл, приложение их
читает. Notty не умеет читать секреты из файла, поэтому потребуется маленький
init-скрипт, который превратит файл в env (или запишет значения в
.env).
Cloud-managed
| Платформа | Где хранить | Как достать в runtime |
|---|---|---|
| AWS ECS / EKS | AWS Secrets Manager | ECS task secrets:, EKS — ESO + IRSA |
| GCP Cloud Run | Secret Manager | --update-secrets=ENV=name:latest |
| Fly.io / Railway | platform secrets | flagged как secret, доступны как env |
Ротация
Общий принцип: секрет считается скомпрометированным, если хоть кто-то прочёл его в обход pipeline. Ротация — рутина, не ЧП.
| Секрет | Cadence | Как |
|---|---|---|
JWT_SECRET |
90 дней | См. runbooks/secret-rotation.md. После ротации все сессии сбрасываются. |
DATABASE_URL (пароль) |
90 дней | Сменить пароль в managed PG → обновить secret backend → rolling update Notty. |
STORAGE_* ключи |
180 дней / по политике cloud | Сначала добавить новый ключ → раскатать → удалить старый. |
| OAuth client secret | при компрометации | Сгенерировать новый в OAuth-провайдере → обновить env → redeploy. |
| API tokens (в БД) | при инциденте | Через UI/API revoke + создать новый. |
| Webhook signing keys | при компрометации | Через UI/API regenerate; сообщите получателям. |
Что нельзя делать:
- Менять
JWT_SECRETв.envбез перезапуска сервера — старый секрет останется в памяти процесса. - Делать ротацию в час пик: rolling restart возможен, но клиенты с активными сессиями получат 401 после истечения cache в gateway.
- Хранить «прошлый»
JWT_SECRETв env как dual-key: Notty не поддерживает multi-key верификацию. Любая ротация = принудительный re-login.
Boundary checks
Каждый квартал прогоняйте:
-
git ls-files+ сканер (gitleaks/trufflehog) на repo и tags. - Проверьте, что docker image не содержит
.env(см..dockerignore). - Audit
who can readдля secret backend — список не растёт без ревью. - Бэкапы БД зашифрованы; ключ шифрования в KMS, а не в коде.
- Сверка реестра секретов с тем, что реально читается в runtime
(
/api/health+kubectl describe pod).
Источник: docs/operations/secrets-management.md. Снимок документации исходного проекта. Технический справочник сохраняет язык оригинала.