Проект: {PROJECT_NAME}
Последнее обновление: {ДАТА}
НИКОГДА не коммитьте этот файл в git если он содержит реальные пароли!
Этот файл должен быть в .gitignore:
SECRETS.md
secrets/
*.pem
*.key
credentials.json
НИКОГДА не коммитить секреты в git
- ❌ В коде: api_key = "sk-abc123"
- ✅ Через env: api_key = os.getenv("API_KEY")
Разные секреты для разных окружений
- Development: dev_api_key
- Staging: staging_api_key
- Production: prod_api_key
Минимальные права доступа
- Давать только то, что нужно
- Отзывать неиспользуемые ключи
Регулярная ротация
- Пароли: раз в 90 дней
- API ключи: раз в 180 дней
- SSH ключи: раз в 365 дней
Аудит использования
- Логировать кто и когда использовал секрет
- Мониторить подозрительную активность
| Секрет | Тип | Окружение | Где хранится | Последняя ротация | Следующая ротация |
|---|---|---|---|---|---|
| DATABASE_URL | Password | Production | .env | 2025-10-01 | 2026-01-01 |
| SECRET_KEY | Random | Production | .env | 2025-09-01 | 2025-12-01 |
| OZON_API_KEY | API Key | Production | .env | 2025-11-01 | 2026-05-01 |
| OZON_CLIENT_ID | API Key | Production | .env | 2025-11-01 | - |
| SSH_PRIVATE_KEY | SSH Key | Production | ~/.ssh/id_rsa | 2025-01-01 | 2026-01-01 |
| SSL_CERT | Certificate | Production | /etc/ssl/ | 2025-06-01 | 2026-06-01 |
| {ДРУГИЕ} |
ШАБЛОН для добавления:
| {НАЗВАНИЕ} | {ТИП} | {ENV} | {РАСПОЛОЖЕНИЕ} | {ДАТА_СОЗДАНИЯ} | {ДАТА_РОТАЦИИ} |
# Production
/etc/{PROJECT_NAME}/.env
# Development
/path/to/project/.env (gitignored)
# ========================================
# 🗄️ БАЗА ДАННЫХ
# ========================================
# PostgreSQL (Production)
DATABASE_URL=postgresql://user:STRONG_PASSWORD@localhost:5432/dbname
# ========================================
# 🔐 СЕКРЕТНЫЕ КЛЮЧИ
# ========================================
# Secret key для шифрования (64 символа, случайный)
# Генерация: openssl rand -hex 32
SECRET_KEY=abc123def456...
# JWT token secret
JWT_SECRET=xyz789uvw456...
# ========================================
# 🔌 API КЛЮЧИ
# ========================================
# Ozon API (получить в личном кабинете)
OZON_CLIENT_ID=123456
OZON_API_KEY=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# Wildberries API
WB_API_KEY=your-wb-api-key
# СДЭК API (тестовые ключи - НЕ для production!)
CDEK_ACCOUNT=your-account
CDEK_SECURE_PASSWORD=your-password
# ========================================
# 📧 EMAIL (если используется)
# ========================================
SMTP_USER=your-email@gmail.com
SMTP_PASSWORD=app-specific-password # НЕ основной пароль!
# ========================================
# 📊 МОНИТОРИНГ
# ========================================
# Sentry DSN (error tracking)
SENTRY_DSN=https://...@sentry.io/...
# ========================================
# 🔐 ДРУГИЕ СЕКРЕТЫ
# ========================================
# {ДОБАВЬТЕ_СВОИ}
# SECRET_KEY (64 символа)
openssl rand -hex 32
# JWT_SECRET (128 символов)
openssl rand -base64 96
# Пароль (16 символов, сложный)
openssl rand -base64 16
# UUID (для токенов)
uuidgen
# .env должен читаться только owner
chmod 600 /etc/{PROJECT_NAME}/.env
chown {USER}:{GROUP} /etc/{PROJECT_NAME}/.env
# Проверка
ls -la /etc/{PROJECT_NAME}/.env
# Ожидаем: -rw------- 1 user group
Где получить:
1. Личный кабинет Ozon Seller → Настройки → API
2. Создать ключ
3. Скопировать Client ID и API Key
Права:
- ✅ Чтение заказов (обязательно)
- ✅ Управление отгрузками (если нужно)
- ❌ Финансы (не давать если не нужно)
Хранение:
# .env
OZON_CLIENT_ID=123456
OZON_API_KEY=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Ротация: Раз в 6 месяцев или при компрометации
Проверка работы:
curl -X POST "https://api-seller.ozon.ru/v1/seller/info" \
-H "Client-Id: 123456" \
-H "Api-Key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# Ожидаем: 200 OK с данными продавца
Где получить:
1. Личный кабинет WB → Профиль → Настройки → Доступ к API
2. Создать ключ
3. Скопировать токен
Хранение:
WB_API_KEY=your-wb-api-key
Ротация: Раз в 6 месяцев
Где получить:
1. Тестовые (sandbox):
- Account: z9GRRu7FxmO53CQ9cFfI6qiy32wpfTkd
- Password: w24JTCv4MnAcuRTx0oHjHLDtyt3I6IBq
Хранение:
# Development (sandbox)
CDEK_ACCOUNT=z9GRRu7FxmO53CQ9cFfI6qiy32wpfTkd
CDEK_SECURE_PASSWORD=w24JTCv4MnAcuRTx0oHjHLDtyt3I6IBq
CDEK_API_URL=https://api.edu.cdek.ru/v2
# Production
CDEK_ACCOUNT=your-production-account
CDEK_SECURE_PASSWORD=your-production-password
CDEK_API_URL=https://api.cdek.ru/v2
# Production
DATABASE_URL=postgresql://mp1_user:STRONG_PASSWORD_HERE@localhost:5432/mp1_prod
# Staging
DATABASE_URL=postgresql://mp1_user:DIFFERENT_PASSWORD@localhost:5432/mp1_staging
# Development (может быть SQLite)
DATABASE_URL=sqlite:///./data/dev.db
# PostgreSQL
sudo -u postgres psql
CREATE USER mp1_user WITH PASSWORD 'STRONG_PASSWORD';
CREATE DATABASE mp1_prod OWNER mp1_user;
GRANT ALL PRIVILEGES ON DATABASE mp1_prod TO mp1_user;
Требования:
- Минимум 16 символов
- Буквы верхний/нижний регистр
- Цифры
- Специальные символы
- НЕ словарные слова
Генерация:
openssl rand -base64 20
Ротация: Раз в 90 дней (плановая), сразу при компрометации
| Ключ | Для чего | Где | Passphrase | Создан | Истекает |
|---|---|---|---|---|---|
| ~/.ssh/id_rsa | Production сервер | Control Plane | Да | 2025-01-01 | 2026-01-01 |
| ~/.ssh/deploy_key | GitHub deploy | CI/CD | Нет | 2025-06-01 | - |
| {ДРУГИЕ} |
# С passphrase (рекомендуется для production)
ssh-keygen -t ed25519 -C "your_email@example.com" -f ~/.ssh/id_prod
# Без passphrase (для автоматизации, CI/CD)
ssh-keygen -t ed25519 -N "" -C "deploy_key" -f ~/.ssh/deploy_key
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_*
chmod 644 ~/.ssh/id_*.pub
# Скопировать публичный ключ
ssh-copy-id -i ~/.ssh/id_prod.pub user@server
# Или вручную
cat ~/.ssh/id_prod.pub | ssh user@server "cat >> ~/.ssh/authorized_keys"
Когда:
- Раз в год (плановая)
- При увольнении сотрудника
- При компрометации
Процесс:
# 1. Создать новый ключ
ssh-keygen -t ed25519 -C "new_key" -f ~/.ssh/id_new
# 2. Добавить на сервер
ssh-copy-id -i ~/.ssh/id_new.pub user@server
# 3. Протестировать новый ключ
ssh -i ~/.ssh/id_new user@server
# 4. Удалить старый ключ с сервера
ssh user@server "sed -i '/OLD_KEY_FINGERPRINT/d' ~/.ssh/authorized_keys"
# 5. Удалить локально
rm ~/.ssh/id_old*
Расположение:
/etc/letsencrypt/live/{DOMAIN}/
├── fullchain.pem # Сертификат + цепочка
├── privkey.pem # Приватный ключ
└── cert.pem # Только сертификат
Автообновление (certbot):
# Проверить сертификаты
certbot certificates
# Вручную обновить
certbot renew
# Автообновление (cron)
0 0 1 * * certbot renew --quiet && systemctl reload nginx
Проверка срока действия:
echo | openssl s_client -servername {DOMAIN} -connect {DOMAIN}:443 2>/dev/null | openssl x509 -noout -dates
# Ожидаем:
# notBefore=...
# notAfter=Mar 1 00:00:00 2026 GMT
Алерт за 30 дней до истечения:
# Скрипт проверки
#!/bin/bash
DOMAIN="{DOMAIN}"
EXPIRY=$(echo | openssl s_client -servername $DOMAIN -connect $DOMAIN:443 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))
if [ $DAYS_LEFT -lt 30 ]; then
echo "WARNING: SSL cert expires in $DAYS_LEFT days!"
# Отправить алерт (email/telegram)
fi
| Тип секрета | Период | Следующая ротация |
|---|---|---|
| Пароли БД | 90 дней | {ДАТА + 90 дней} |
| SECRET_KEY | 180 дней | {ДАТА + 180 дней} |
| API ключи | 180 дней | {ДАТА + 180 дней} |
| SSH ключи | 365 дней | {ДАТА + 365 дней} |
| SSL сертификаты | 90 дней | Автообновление |
# 1. Создать новый пароль
NEW_PASSWORD=$(openssl rand -base64 20)
# 2. Изменить в PostgreSQL
sudo -u postgres psql
ALTER USER mp1_user WITH PASSWORD '$NEW_PASSWORD';
# 3. Обновить .env
sed -i "s/DATABASE_URL=.*/DATABASE_URL=postgresql:\/\/mp1_user:$NEW_PASSWORD@localhost:5432\/mp1_prod/" /etc/{PROJECT_NAME}/.env
# 4. Перезапустить приложение
systemctl restart {PROJECT_NAME}
# 5. Проверить работу
curl http://localhost:{PORT}/health
# 6. Задокументировать
echo "$(date): Rotated DB password" >> /var/log/{PROJECT_NAME}/secrets-rotation.log
# 1. Создать новый ключ в личном кабинете Ozon/WB/etc
# 2. Обновить .env (старый и новый ключ одновременно)
OZON_API_KEY=new-key
OZON_API_KEY_OLD=old-key # На случай отката
# 3. Перезапустить приложение
systemctl restart {PROJECT_NAME}
# 4. Протестировать 24 часа
# 5. Удалить старый ключ из .env и из личного кабинета
# 6. Задокументировать
# .env в проекте (gitignored)
/path/to/project/.env
# Защищённый файл
/etc/{PROJECT_NAME}/.env
chmod 600
chown {USER}:{GROUP}
HashiCorp Vault:
# Установка
apt install vault
# Инициализация
vault server -dev
# Сохранить секрет
vault kv put secret/{PROJECT_NAME}/db password="xxx"
# Получить секрет
vault kv get secret/{PROJECT_NAME}/db
AWS Secrets Manager:
# Создать секрет
aws secretsmanager create-secret \
--name /{PROJECT_NAME}/db/password \
--secret-string "xxx"
# Получить секрет
aws secretsmanager get-secret-value \
--secret-id /{PROJECT_NAME}/db/password
# Зашифровать файл с секретами
gpg -c secrets.txt
# Создаст secrets.txt.gpg
# Расшифровать
gpg secrets.txt.gpg
# Спросит passphrase
Предусловие: Создать "запасной вход" заранее
# 1. Создать emergency user
sudo useradd -m -s /bin/bash emergency_admin
sudo usermod -aG sudo emergency_admin
# 2. Сгенерировать сложный пароль
EMERGENCY_PASSWORD=$(openssl rand -base64 20)
# 3. Установить пароль
echo "emergency_admin:$EMERGENCY_PASSWORD" | sudo chpasswd
# 4. Сохранить пароль в SEALED конверте
# Напечатать на бумаге, запечатать, положить в сейф
# 5. SSH ключ для emergency
ssh-keygen -t ed25519 -f ~/.ssh/emergency_key
# Публичный ключ → /home/emergency_admin/.ssh/authorized_keys
# Приватный ключ → на USB флешку в сейф
Использование:
# 1. Взять USB с emergency ключом из сейфа
# 2. Подключиться
ssh -i /path/to/emergency_key emergency_admin@server
# 3. Выполнить необходимые действия
# 4. ЛОГИРОВАТЬ всё что делали
echo "$(date): Emergency access by {ИМЯ}: {ДЕЙСТВИЯ}" >> /var/log/emergency-access.log
# 5. После - ротация emergency пароля
Отозвать скомпрометированный секрет
- API ключ → удалить в личном кабинете
- Пароль БД → сменить
- SSH ключ → удалить из authorized_keys
Сгенерировать новый секрет
- См. раздел "Ротация секретов"
Проверить логи использования
- Кто использовал секрет?
- Были ли подозрительные действия?
Уведомить команду
Проверить нет ли других утечек
- Просканировать git history
- Проверить все логи
Обновить документацию
- Задокументировать инцидент
- Обновить дату ротации
Post-mortem анализ
- Как произошла утечка?
- Как предотвратить в будущем?
Последнее обновление: {ДАТА}
Ответственный за секреты: {ИМЯ}
Emergency контакт: {ТЕЛЕФОН/EMAIL}