diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..9fcac2b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,153 @@ +# 📋 Журнал изменений (Changelog) + +## [2.1.0] - 2025-10-27 + +### 🆕 Добавлено + +#### Генерация тестовых SSL сертификатов +- ✨ **Новый класс `TestCertificateGenerator`** - генерация самоподписанных сертификатов +- ✨ **Команда `--test-cert`** в Python скрипте для создания тестовых сертификатов +- ✨ **Скрипт `test_certificate.sh`** - автономное создание через OpenSSL +- ✨ **Команда `make test-cert`** в Makefile для быстрого тестирования + +#### Документация +- 📘 **TESTING_GUIDE.md** (370+ строк) - полное руководство по тестированию + - Обход лимитов Let's Encrypt (5 сертификатов в неделю) + - Сравнение методов создания сертификатов + - Примеры для CI/CD и Docker + - Переход с тестовых на production + - Частые вопросы и решения + +- 📘 **PROJECT_STRUCTURE.md** - структура проекта + - Описание всех файлов + - Список возможностей + - Технологии + +- 📘 **CHEATSHEET.md** - быстрая шпаргалка + - Основные команды + - Сценарии использования + - Частые ошибки и решения + - Workflow разработки + +#### Функциональность +- ✨ Поддержка **неограниченного количества** тестовых сертификатов +- ✨ **Мгновенное создание** (1-2 секунды) без DNS валидации +- ✨ **Автоматическая загрузка** тестовых сертификатов в NPM +- ✨ **Полная совместимость** структуры с Let's Encrypt +- ✨ **Wildcard поддержка** для тестовых сертификатов + +### 🔧 Улучшено + +#### Python скрипт +- Добавлен импорт библиотеки `cryptography` с проверкой установки +- Новые параметры командной строки: + - `--test-cert` - создание тестового сертификата + - `--auto` - явное указание автоматического режима +- Улучшенная обработка тестовых сертификатов в NPM +- Детальное логирование процесса генерации + +#### Makefile +- Добавлена команда `make test-cert` с красивым выводом +- Информационные сообщения о преимуществах тестовых сертификатов +- Предупреждения о безопасности + +#### README.md +- Раздел "Создание тестового самоподписанного сертификата" +- Обновленное содержание с ссылкой на тестовые сертификаты +- Примеры использования тестовых сертификатов +- Интеграция с NPM для тестовых сертификатов +- Ссылки на дополнительную документацию + +### 🎯 Преимущества + +#### Для разработчиков +- ✅ **Нет лимитов** - неограниченное количество сертификатов +- ✅ **Быстро** - создание за 1-2 секунды +- ✅ **Офлайн** - работает без интернета +- ✅ **Идентичная структура** - те же файлы что и Let's Encrypt + +#### Для тестирования +- ✅ **CI/CD friendly** - быстрое создание в pipeline +- ✅ **Docker ready** - легко встраивается в контейнеры +- ✅ **Staging окружения** - идеально для тестовых серверов +- ✅ **Локальная разработка** - HTTPS на localhost + +### 📊 Статистика + +- **Строк кода**: 1,411 (Python скрипт) +- **Строк в Makefile**: 415 +- **Строк документации**: 2,200+ +- **Команд в Makefile**: 13 +- **Режимов работы**: 4 (obtain, renew, auto, test-cert) + +--- + +## [2.0.0] - 2025-10-27 + +### 🆕 Добавлено +- ✨ Интеграция с Nginx Proxy Manager (NPM) +- ✨ Класс `NginxProxyManagerAPI` для управления сертификатами через API +- ✨ Автоматическая загрузка сертификатов в NPM +- ✨ Автоматическое обновление сертификатов в NPM +- ✨ Автоматическая проверка срока действия +- ✨ Настраиваемый порог обновления (`renewal_days`) +- ✨ Makefile для автоматизации установки/удаления +- ✨ Systemd service + timer +- ✨ Cron автоматизация + +### 🔧 Улучшено +- Консолидация документации в единый README.md +- Подробное логирование с статусами операций +- Валидация конфигурации +- Улучшенная обработка ошибок + +### 📘 Документация +- Полное руководство по NPM интеграции +- Быстрый старт за 3 команды +- Примеры автоматизации + +--- + +## [1.0.0] - 2025-10-26 + +### 🆕 Первый релиз +- Python скрипт для Let's Encrypt через reg.ru API +- Bash скрипт с certbot-dns-regru +- PowerShell версия для Windows +- DNS-01 валидация +- Wildcard сертификаты +- Базовая документация + +--- + +## Roadmap (Планы) + +### [2.2.0] - Планируется +- [ ] Веб-интерфейс для управления +- [ ] Поддержка множественных доменов +- [ ] Notifications (email, telegram) +- [ ] Grafana dashboard для мониторинга +- [ ] Backup сертификатов + +### [3.0.0] - Будущее +- [ ] Поддержка других DNS провайдеров +- [ ] Cloudflare API +- [ ] Route53 (AWS) +- [ ] Google Cloud DNS + +--- + +## Типы изменений +- `🆕 Добавлено` - новый функционал +- `🔧 Улучшено` - улучшения существующего функционала +- `🐛 Исправлено` - исправление багов +- `🗑️ Удалено` - удаленный функционал +- `🔒 Безопасность` - изменения безопасности +- `📘 Документация` - изменения в документации + +--- + +**Версионирование**: Semantic Versioning (MAJOR.MINOR.PATCH) +- **MAJOR**: Несовместимые изменения API +- **MINOR**: Новый функционал с обратной совместимостью +- **PATCH**: Исправления багов diff --git a/CHEATSHEET.md b/CHEATSHEET.md new file mode 100644 index 0000000..4b758b7 --- /dev/null +++ b/CHEATSHEET.md @@ -0,0 +1,263 @@ +# ⚡ Шпаргалка по SSL сертификатам + +## 🚀 Быстрый старт + +### Установка за 3 команды +```bash +sudo make install +sudo nano /etc/letsencrypt/regru_config.json # Заполнить данные +sudo make test-cert # Тест +``` + +--- + +## 🧪 Тестирование (БЕЗ лимитов Let's Encrypt) + +```bash +# Создать тестовый сертификат (неограниченно) +sudo make test-cert + +# Проверить статус +sudo make status + +# Просмотреть логи +sudo make logs +``` + +**Когда использовать:** +- ⚠️ Let's Encrypt: макс. 5 сертификатов/неделю +- ✅ Тестовые: НЕОГРАНИЧЕННО +- ⚡ Создание: 1-2 секунды vs 2-5 минут + +--- + +## 🔒 Production (Let's Encrypt) + +```bash +# Получить настоящий сертификат +sudo make obtain + +# Автоматический режим (проверка + обновление) +sudo make run + +# Принудительное обновление +sudo make renew +``` + +--- + +## 📋 Основные команды + +| Команда | Описание | Лимиты | +|---------|----------|--------| +| `make test-cert` | Тестовый сертификат | ✅ Нет | +| `make obtain` | Let's Encrypt новый | ⚠️ 5/неделю | +| `make renew` | Обновить существующий | ⚠️ 5/неделю | +| `make run` | Авто-режим | ⚠️ 5/неделю | +| `make status` | Статус системы | - | +| `make logs` | Показать логи | - | +| `make check-config` | Проверить конфигурацию | - | + +--- + +## 📝 Конфигурация + +### Минимальная (тестирование) +```json +{ + "domain": "test.example.com", + "wildcard": true, + "cert_dir": "/etc/letsencrypt/live" +} +``` + +### Полная (production + NPM) +```json +{ + "regru_username": "myuser", + "regru_password": "mypassword", + "domain": "example.com", + "wildcard": true, + "email": "admin@example.com", + "renewal_days": 30, + "npm_enabled": true, + "npm_host": "https://npm.example.com", + "npm_email": "admin@example.com", + "npm_password": "npm_password" +} +``` + +--- + +## 🔄 Workflow + +### Разработка → Production + +```bash +# 1. Разработка (тестовые сертификаты) +sudo make test-cert # Создать тестовый +# Тестировать приложение... + +# 2. Production (Let's Encrypt) +sudo rm -rf /etc/letsencrypt/live/example.com/ # Удалить тест +sudo make obtain # Создать production +``` + +--- + +## 📁 Важные пути + +```bash +# Конфигурация +/etc/letsencrypt/regru_config.json + +# Сертификаты +/etc/letsencrypt/live/example.com/ +├── privkey.pem # Приватный ключ +├── cert.pem # Сертификат +├── fullchain.pem # Полная цепочка (для nginx) +└── chain.pem # CA цепочка + +# Скрипты +/opt/letsencrypt-regru/letsencrypt_regru_api.py + +# Логи +/var/log/letsencrypt_regru.log +``` + +--- + +## 🔍 Проверка + +```bash +# Проверить конфигурацию +sudo make check-config + +# Проверить сертификат +openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -text -noout + +# Проверить срок действия +openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -noout -dates + +# Проверить systemd +sudo systemctl status letsencrypt-regru.timer +sudo systemctl list-timers letsencrypt-regru.timer + +# Проверить cron +sudo crontab -l | grep letsencrypt +``` + +--- + +## 🐛 Отладка + +```bash +# Подробные логи +sudo make logs + +# Тестовый запуск с подробностями +sudo python3 /opt/letsencrypt-regru/letsencrypt_regru_api.py \ + -c /etc/letsencrypt/regru_config.json --check -v + +# Логи certbot +sudo tail -f /var/log/letsencrypt/letsencrypt.log + +# Логи systemd +sudo journalctl -u letsencrypt-regru.service -f +``` + +--- + +## ⚠️ Частые ошибки + +### Let's Encrypt: Rate limit exceeded +```bash +# РЕШЕНИЕ: Используйте тестовые сертификаты +sudo make test-cert +``` + +### NPM: Certificate not found +```bash +# РЕШЕНИЕ: Проверьте настройки NPM +sudo make check-config + +# Проверьте подключение +curl -k https://npm.example.com +``` + +### Permission denied +```bash +# РЕШЕНИЕ: Запускайте с sudo +sudo make test-cert +``` + +--- + +## 🎯 Сценарии использования + +### Локальная разработка +```bash +sudo make test-cert +# Открыть https://localhost (игнорировать предупреждение) +``` + +### CI/CD тестирование +```bash +# В pipeline +sudo make test-cert +# Запустить тесты... +sudo make status +``` + +### Staging окружение +```bash +sudo make test-cert # Или +sudo make obtain # Если есть домен +``` + +### Production окружение +```bash +sudo make install +sudo make obtain +# Автоматическое обновление через cron/systemd +``` + +--- + +## 📚 Документация + +- **README.md** - Полное руководство (1420+ строк) +- **TESTING_GUIDE.md** - Тестирование (370+ строк) +- **PROJECT_STRUCTURE.md** - Структура проекта +- **CHEATSHEET.md** - Эта шпаргалка + +--- + +## 🆘 Быстрая помощь + +```bash +# Показать все команды +make help + +# Проверить установку +sudo make status + +# Полная переустановка +sudo make uninstall +sudo make install +``` + +--- + +## 💡 Советы + +1. **Всегда начинайте с тестовых сертификатов** - избегайте лимитов +2. **Проверяйте конфигурацию** - `make check-config` +3. **Мониторьте логи** - `make logs` +4. **Автоматизируйте** - systemd/cron уже настроены +5. **Храните бэкапы** конфигурации + +--- + +**Версия**: 2.1 +**Обновлено**: 27.10.2025 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0e333ad --- /dev/null +++ b/Makefile @@ -0,0 +1,410 @@ +# ============================================================================== +# Makefile для установки и удаления скрипта управления SSL сертификатами +# Let's Encrypt с DNS-валидацией через API reg.ru +# ============================================================================== + +# Переменные +INSTALL_DIR = /opt/letsencrypt-regru +SCRIPT_NAME = letsencrypt_regru_api.py +CONFIG_EXAMPLE = config.json.example +SERVICE_NAME = letsencrypt-regru +SERVICE_FILE = $(SERVICE_NAME).service +TIMER_FILE = $(SERVICE_NAME).timer +CONFIG_DIR = /etc/letsencrypt +CONFIG_FILE = $(CONFIG_DIR)/regru_config.json +LOG_DIR = /var/log +LOG_FILE = $(LOG_DIR)/letsencrypt_regru.log +CRON_LOG = $(LOG_DIR)/letsencrypt_cron.log +SYSTEMD_DIR = /etc/systemd/system +PYTHON = python3 + +# Цвета для вывода +RED = \033[0;31m +GREEN = \033[0;32m +YELLOW = \033[1;33m +BLUE = \033[0;34m +NC = \033[0m # No Color + +.PHONY: help install uninstall status check-root setup-dirs install-script install-service install-cron clean + +# ============================================================================== +# Помощь +# ============================================================================== + +help: + @echo "$(BLUE)╔════════════════════════════════════════════════════════════════╗$(NC)" + @echo "$(BLUE)║ Makefile для управления Let's Encrypt SSL сертификатами ║$(NC)" + @echo "$(BLUE)╚════════════════════════════════════════════════════════════════╝$(NC)" + @echo "" + @echo "$(GREEN)Доступные команды:$(NC)" + @echo "" + @echo " $(YELLOW)make install$(NC) - Установить скрипт и настроить автоматизацию" + @echo " $(YELLOW)make uninstall$(NC) - Удалить скрипт и очистить систему" + @echo " $(YELLOW)make status$(NC) - Проверить статус установки" + @echo " $(YELLOW)make check-config$(NC) - Проверить конфигурацию" + @echo " $(YELLOW)make test-run$(NC) - Тестовый запуск скрипта" + @echo " $(YELLOW)make test-cert$(NC) - Создать тестовый самоподписанный сертификат" + @echo " $(YELLOW)make logs$(NC) - Показать логи" + @echo " $(YELLOW)make help$(NC) - Показать эту справку" + @echo "" + +# ============================================================================== +# Проверка прав root +# ============================================================================== + +check-root: + @if [ "$$(id -u)" != "0" ]; then \ + echo "$(RED)✗ Ошибка: Требуются права root$(NC)"; \ + echo "$(YELLOW)Запустите: sudo make install$(NC)"; \ + exit 1; \ + fi + +# ============================================================================== +# Установка +# ============================================================================== + +install: check-root + @echo "$(BLUE)╔════════════════════════════════════════════════════════════════╗$(NC)" + @echo "$(BLUE)║ Установка Let's Encrypt SSL Manager ║$(NC)" + @echo "$(BLUE)╚════════════════════════════════════════════════════════════════╝$(NC)" + @echo "" + @$(MAKE) setup-dirs + @$(MAKE) install-dependencies + @$(MAKE) install-script + @$(MAKE) install-service + @$(MAKE) install-cron + @echo "" + @echo "$(GREEN)╔════════════════════════════════════════════════════════════════╗$(NC)" + @echo "$(GREEN)║ ✓ Установка завершена успешно! ║$(NC)" + @echo "$(GREEN)╚════════════════════════════════════════════════════════════════╝$(NC)" + @echo "" + @echo "$(YELLOW)Следующие шаги:$(NC)" + @echo " 1. Отредактируйте конфигурацию:" + @echo " $(BLUE)sudo nano $(CONFIG_FILE)$(NC)" + @echo "" + @echo " 2. Проверьте конфигурацию:" + @echo " $(BLUE)make check-config$(NC)" + @echo "" + @echo " 3. Запустите тестовую проверку:" + @echo " $(BLUE)make test-run$(NC)" + @echo "" + @echo " 4. Проверьте статус службы:" + @echo " $(BLUE)make status$(NC)" + @echo "" + +# Создание директорий +setup-dirs: + @echo "$(YELLOW)→ Создание директорий...$(NC)" + @mkdir -p $(INSTALL_DIR) + @mkdir -p $(CONFIG_DIR) + @mkdir -p $(LOG_DIR) + @echo "$(GREEN)✓ Директории созданы$(NC)" + +# Установка зависимостей +install-dependencies: + @echo "$(YELLOW)→ Установка зависимостей Python...$(NC)" + @if ! command -v pip3 >/dev/null 2>&1; then \ + echo "$(RED)✗ pip3 не найден. Установите python3-pip$(NC)"; \ + exit 1; \ + fi + @pip3 install -q requests cryptography 2>/dev/null || pip3 install requests cryptography + @echo "$(GREEN)✓ Зависимости установлены$(NC)" + +# Копирование скрипта +install-script: + @echo "$(YELLOW)→ Установка скрипта...$(NC)" + @if [ ! -f "$(SCRIPT_NAME)" ]; then \ + echo "$(RED)✗ Файл $(SCRIPT_NAME) не найден!$(NC)"; \ + exit 1; \ + fi + @cp $(SCRIPT_NAME) $(INSTALL_DIR)/ + @chmod +x $(INSTALL_DIR)/$(SCRIPT_NAME) + @echo "$(GREEN)✓ Скрипт установлен в $(INSTALL_DIR)/$(NC)" + @echo "" + @echo "$(YELLOW)→ Создание конфигурации...$(NC)" + @if [ ! -f "$(CONFIG_FILE)" ]; then \ + if [ -f "$(CONFIG_EXAMPLE)" ]; then \ + cp $(CONFIG_EXAMPLE) $(CONFIG_FILE); \ + chmod 600 $(CONFIG_FILE); \ + echo "$(GREEN)✓ Создан файл конфигурации: $(CONFIG_FILE)$(NC)"; \ + echo "$(YELLOW)⚠ ВНИМАНИЕ: Отредактируйте конфигурацию перед использованием!$(NC)"; \ + else \ + echo "$(YELLOW)⚠ Файл config.json.example не найден$(NC)"; \ + $(PYTHON) $(INSTALL_DIR)/$(SCRIPT_NAME) --create-config $(CONFIG_FILE); \ + chmod 600 $(CONFIG_FILE); \ + echo "$(GREEN)✓ Создана конфигурация по умолчанию$(NC)"; \ + fi \ + else \ + echo "$(GREEN)✓ Конфигурация уже существует: $(CONFIG_FILE)$(NC)"; \ + fi + +# Установка systemd service и timer +install-service: + @echo "$(YELLOW)→ Создание systemd service...$(NC)" + @echo "[Unit]" > $(SYSTEMD_DIR)/$(SERVICE_FILE) + @echo "Description=Let's Encrypt Certificate Manager with reg.ru DNS" >> $(SYSTEMD_DIR)/$(SERVICE_FILE) + @echo "After=network.target" >> $(SYSTEMD_DIR)/$(SERVICE_FILE) + @echo "" >> $(SYSTEMD_DIR)/$(SERVICE_FILE) + @echo "[Service]" >> $(SYSTEMD_DIR)/$(SERVICE_FILE) + @echo "Type=oneshot" >> $(SYSTEMD_DIR)/$(SERVICE_FILE) + @echo "ExecStart=$(PYTHON) $(INSTALL_DIR)/$(SCRIPT_NAME) -c $(CONFIG_FILE)" >> $(SYSTEMD_DIR)/$(SERVICE_FILE) + @echo "StandardOutput=journal" >> $(SYSTEMD_DIR)/$(SERVICE_FILE) + @echo "StandardError=journal" >> $(SYSTEMD_DIR)/$(SERVICE_FILE) + @echo "User=root" >> $(SYSTEMD_DIR)/$(SERVICE_FILE) + @echo "" >> $(SYSTEMD_DIR)/$(SERVICE_FILE) + @echo "[Install]" >> $(SYSTEMD_DIR)/$(SERVICE_FILE) + @echo "WantedBy=multi-user.target" >> $(SYSTEMD_DIR)/$(SERVICE_FILE) + @echo "$(GREEN)✓ Service файл создан$(NC)" + @echo "" + @echo "$(YELLOW)→ Создание systemd timer...$(NC)" + @echo "[Unit]" > $(SYSTEMD_DIR)/$(TIMER_FILE) + @echo "Description=Daily Let's Encrypt Certificate Check and Renewal" >> $(SYSTEMD_DIR)/$(TIMER_FILE) + @echo "Requires=$(SERVICE_FILE)" >> $(SYSTEMD_DIR)/$(TIMER_FILE) + @echo "" >> $(SYSTEMD_DIR)/$(TIMER_FILE) + @echo "[Timer]" >> $(SYSTEMD_DIR)/$(TIMER_FILE) + @echo "OnCalendar=daily" >> $(SYSTEMD_DIR)/$(TIMER_FILE) + @echo "Persistent=true" >> $(SYSTEMD_DIR)/$(TIMER_FILE) + @echo "RandomizedDelaySec=1h" >> $(SYSTEMD_DIR)/$(TIMER_FILE) + @echo "" >> $(SYSTEMD_DIR)/$(TIMER_FILE) + @echo "[Install]" >> $(SYSTEMD_DIR)/$(TIMER_FILE) + @echo "WantedBy=timers.target" >> $(SYSTEMD_DIR)/$(TIMER_FILE) + @echo "$(GREEN)✓ Timer файл создан$(NC)" + @echo "" + @echo "$(YELLOW)→ Активация systemd службы...$(NC)" + @systemctl daemon-reload + @systemctl enable $(SERVICE_FILE) 2>/dev/null || true + @systemctl enable $(TIMER_FILE) 2>/dev/null || true + @systemctl start $(TIMER_FILE) 2>/dev/null || true + @echo "$(GREEN)✓ Systemd служба активирована$(NC)" + +# Установка cron задачи +install-cron: + @echo "$(YELLOW)→ Настройка cron задачи...$(NC)" + @CRON_CMD="0 3 * * * $(PYTHON) $(INSTALL_DIR)/$(SCRIPT_NAME) -c $(CONFIG_FILE) >> $(CRON_LOG) 2>&1"; \ + (crontab -l 2>/dev/null | grep -v "$(SCRIPT_NAME)" ; echo "$$CRON_CMD") | crontab - + @echo "$(GREEN)✓ Cron задача добавлена (ежедневно в 3:00 AM)$(NC)" + +# ============================================================================== +# Удаление +# ============================================================================== + +uninstall: check-root + @echo "$(RED)╔════════════════════════════════════════════════════════════════╗$(NC)" + @echo "$(RED)║ Удаление Let's Encrypt SSL Manager ║$(NC)" + @echo "$(RED)╚════════════════════════════════════════════════════════════════╝$(NC)" + @echo "" + @read -p "Вы уверены? Это удалит все файлы и настройки [y/N]: " -n 1 -r; \ + echo ""; \ + if [[ $$REPLY =~ ^[Yy]$$ ]]; then \ + $(MAKE) remove-service; \ + $(MAKE) remove-cron; \ + $(MAKE) remove-files; \ + echo ""; \ + echo "$(GREEN)✓ Удаление завершено$(NC)"; \ + else \ + echo "$(YELLOW)Удаление отменено$(NC)"; \ + fi + +# Удаление systemd service +remove-service: + @echo "$(YELLOW)→ Остановка и удаление systemd служб...$(NC)" + @systemctl stop $(TIMER_FILE) 2>/dev/null || true + @systemctl stop $(SERVICE_FILE) 2>/dev/null || true + @systemctl disable $(TIMER_FILE) 2>/dev/null || true + @systemctl disable $(SERVICE_FILE) 2>/dev/null || true + @rm -f $(SYSTEMD_DIR)/$(SERVICE_FILE) + @rm -f $(SYSTEMD_DIR)/$(TIMER_FILE) + @systemctl daemon-reload + @echo "$(GREEN)✓ Systemd службы удалены$(NC)" + +# Удаление cron задачи +remove-cron: + @echo "$(YELLOW)→ Удаление cron задачи...$(NC)" + @crontab -l 2>/dev/null | grep -v "$(SCRIPT_NAME)" | crontab - 2>/dev/null || true + @echo "$(GREEN)✓ Cron задача удалена$(NC)" + +# Удаление файлов +remove-files: + @echo "$(YELLOW)→ Удаление файлов...$(NC)" + @rm -rf $(INSTALL_DIR) + @echo "$(GREEN)✓ Директория $(INSTALL_DIR) удалена$(NC)" + @echo "" + @read -p "Удалить конфигурацию $(CONFIG_FILE)? [y/N]: " -n 1 -r; \ + echo ""; \ + if [[ $$REPLY =~ ^[Yy]$$ ]]; then \ + rm -f $(CONFIG_FILE); \ + echo "$(GREEN)✓ Конфигурация удалена$(NC)"; \ + else \ + echo "$(YELLOW)Конфигурация сохранена$(NC)"; \ + fi + @echo "" + @read -p "Удалить логи? [y/N]: " -n 1 -r; \ + echo ""; \ + if [[ $$REPLY =~ ^[Yy]$$ ]]; then \ + rm -f $(LOG_FILE) $(CRON_LOG); \ + echo "$(GREEN)✓ Логи удалены$(NC)"; \ + else \ + echo "$(YELLOW)Логи сохранены$(NC)"; \ + fi + +# ============================================================================== +# Утилиты +# ============================================================================== + +# Проверка статуса +status: + @echo "$(BLUE)╔════════════════════════════════════════════════════════════════╗$(NC)" + @echo "$(BLUE)║ Статус Let's Encrypt SSL Manager ║$(NC)" + @echo "$(BLUE)╚════════════════════════════════════════════════════════════════╝$(NC)" + @echo "" + @echo "$(YELLOW)→ Установка:$(NC)" + @if [ -d "$(INSTALL_DIR)" ]; then \ + echo " $(GREEN)✓ Директория: $(INSTALL_DIR)$(NC)"; \ + else \ + echo " $(RED)✗ Директория не найдена$(NC)"; \ + fi + @if [ -f "$(INSTALL_DIR)/$(SCRIPT_NAME)" ]; then \ + echo " $(GREEN)✓ Скрипт установлен$(NC)"; \ + else \ + echo " $(RED)✗ Скрипт не найден$(NC)"; \ + fi + @if [ -f "$(CONFIG_FILE)" ]; then \ + echo " $(GREEN)✓ Конфигурация: $(CONFIG_FILE)$(NC)"; \ + else \ + echo " $(YELLOW)⚠ Конфигурация не найдена$(NC)"; \ + fi + @echo "" + @echo "$(YELLOW)→ Systemd служба:$(NC)" + @systemctl is-enabled $(SERVICE_FILE) 2>/dev/null && echo " $(GREEN)✓ Service включен$(NC)" || echo " $(RED)✗ Service отключен$(NC)" + @systemctl is-active $(SERVICE_FILE) 2>/dev/null && echo " $(GREEN)✓ Service активен$(NC)" || echo " $(YELLOW)⚠ Service неактивен (oneshot)$(NC)" + @echo "" + @echo "$(YELLOW)→ Systemd timer:$(NC)" + @systemctl is-enabled $(TIMER_FILE) 2>/dev/null && echo " $(GREEN)✓ Timer включен$(NC)" || echo " $(RED)✗ Timer отключен$(NC)" + @systemctl is-active $(TIMER_FILE) 2>/dev/null && echo " $(GREEN)✓ Timer активен$(NC)" || echo " $(RED)✗ Timer неактивен$(NC)" + @echo "" + @echo "$(YELLOW)→ Следующий запуск:$(NC)" + @systemctl list-timers $(TIMER_FILE) --no-pager 2>/dev/null || echo " $(RED)✗ Timer не найден$(NC)" + @echo "" + @echo "$(YELLOW)→ Cron задача:$(NC)" + @if crontab -l 2>/dev/null | grep -q "$(SCRIPT_NAME)"; then \ + echo " $(GREEN)✓ Cron задача настроена$(NC)"; \ + crontab -l 2>/dev/null | grep "$(SCRIPT_NAME)"; \ + else \ + echo " $(RED)✗ Cron задача не найдена$(NC)"; \ + fi + +# Проверка конфигурации +check-config: + @echo "$(BLUE)╔════════════════════════════════════════════════════════════════╗$(NC)" + @echo "$(BLUE)║ Проверка конфигурации ║$(NC)" + @echo "$(BLUE)╚════════════════════════════════════════════════════════════════╝$(NC)" + @echo "" + @if [ ! -f "$(CONFIG_FILE)" ]; then \ + echo "$(RED)✗ Конфигурация не найдена: $(CONFIG_FILE)$(NC)"; \ + exit 1; \ + fi + @echo "$(GREEN)✓ Конфигурация найдена$(NC)" + @echo "" + @$(PYTHON) -c "import json; print(json.dumps(json.load(open('$(CONFIG_FILE)')), indent=2, ensure_ascii=False))" 2>/dev/null || \ + (echo "$(RED)✗ Ошибка: Неверный формат JSON$(NC)"; exit 1) + @echo "" + @echo "$(YELLOW)→ Проверка обязательных параметров:$(NC)" + @$(PYTHON) -c "import json; c=json.load(open('$(CONFIG_FILE)')); assert c.get('regru_username'), 'regru_username не задан'" && echo " $(GREEN)✓ regru_username$(NC)" || echo " $(RED)✗ regru_username$(NC)" + @$(PYTHON) -c "import json; c=json.load(open('$(CONFIG_FILE)')); assert c.get('regru_password'), 'regru_password не задан'" && echo " $(GREEN)✓ regru_password$(NC)" || echo " $(RED)✗ regru_password$(NC)" + @$(PYTHON) -c "import json; c=json.load(open('$(CONFIG_FILE)')); assert c.get('domain'), 'domain не задан'" && echo " $(GREEN)✓ domain$(NC)" || echo " $(RED)✗ domain$(NC)" + @$(PYTHON) -c "import json; c=json.load(open('$(CONFIG_FILE)')); assert c.get('email'), 'email не задан'" && echo " $(GREEN)✓ email$(NC)" || echo " $(RED)✗ email$(NC)" + +# Тестовый запуск +test-run: check-root + @echo "$(BLUE)╔════════════════════════════════════════════════════════════════╗$(NC)" + @echo "$(BLUE)║ Тестовый запуск скрипта ║$(NC)" + @echo "$(BLUE)╚════════════════════════════════════════════════════════════════╝$(NC)" + @echo "" + @if [ ! -f "$(CONFIG_FILE)" ]; then \ + echo "$(RED)✗ Конфигурация не найдена. Запустите: make install$(NC)"; \ + exit 1; \ + fi + @echo "$(YELLOW)→ Запуск проверки сертификата...$(NC)" + @echo "" + @$(PYTHON) $(INSTALL_DIR)/$(SCRIPT_NAME) -c $(CONFIG_FILE) --check -v + +# Просмотр логов +logs: + @echo "$(BLUE)╔════════════════════════════════════════════════════════════════╗$(NC)" + @echo "$(BLUE)║ Логи Let's Encrypt SSL Manager ║$(NC)" + @echo "$(BLUE)╚════════════════════════════════════════════════════════════════╝$(NC)" + @echo "" + @echo "$(YELLOW)→ Основной лог скрипта:$(NC)" + @if [ -f "$(LOG_FILE)" ]; then \ + tail -n 50 $(LOG_FILE); \ + else \ + echo "$(YELLOW)Лог файл не найден$(NC)"; \ + fi + @echo "" + @echo "$(YELLOW)→ Лог cron задачи:$(NC)" + @if [ -f "$(CRON_LOG)" ]; then \ + tail -n 50 $(CRON_LOG); \ + else \ + echo "$(YELLOW)Лог cron не найден$(NC)"; \ + fi + @echo "" + @echo "$(YELLOW)→ Логи systemd:$(NC)" + @journalctl -u $(SERVICE_FILE) -n 50 --no-pager 2>/dev/null || echo "$(YELLOW)Логи systemd не найдены$(NC)" + +# Ручной запуск обновления +run: check-root + @echo "$(YELLOW)→ Запуск обновления сертификата...$(NC)" + @$(PYTHON) $(INSTALL_DIR)/$(SCRIPT_NAME) -c $(CONFIG_FILE) -v + +# Принудительное получение сертификата +obtain: check-root + @echo "$(YELLOW)→ Принудительное получение нового сертификата...$(NC)" + @$(PYTHON) $(INSTALL_DIR)/$(SCRIPT_NAME) -c $(CONFIG_FILE) --obtain -v + +# Принудительное обновление сертификата +renew: check-root + @echo "$(YELLOW)→ Принудительное обновление сертификата...$(NC)" + @$(PYTHON) $(INSTALL_DIR)/$(SCRIPT_NAME) -c $(CONFIG_FILE) --renew -v + +# Создание тестового самоподписанного сертификата +test-cert: check-root + @echo "$(BLUE)╔════════════════════════════════════════════════════════════════╗$(NC)" + @echo "$(BLUE)║ Создание тестового самоподписанного сертификата ║$(NC)" + @echo "$(BLUE)╚════════════════════════════════════════════════════════════════╝$(NC)" + @echo "" + @echo "$(YELLOW)⚠️ ВНИМАНИЕ: Это тестовый сертификат для разработки!$(NC)" + @echo "$(YELLOW) Браузеры будут показывать предупреждение безопасности.$(NC)" + @echo "$(YELLOW) Для production используйте Let's Encrypt сертификаты.$(NC)" + @echo "" + @if [ ! -f "$(CONFIG_FILE)" ]; then \ + echo "$(RED)✗ Конфигурация не найдена. Запустите: make install$(NC)"; \ + exit 1; \ + fi + @echo "$(YELLOW)→ Генерация самоподписанного сертификата...$(NC)" + @echo "" + @$(PYTHON) $(INSTALL_DIR)/$(SCRIPT_NAME) -c $(CONFIG_FILE) --test-cert -v + @echo "" + @echo "$(GREEN)╔════════════════════════════════════════════════════════════════╗$(NC)" + @echo "$(GREEN)║ ✓ Тестовый сертификат создан ║$(NC)" + @echo "$(GREEN)╚════════════════════════════════════════════════════════════════╝$(NC)" + @echo "" + @echo "$(YELLOW)Преимущества тестового сертификата:$(NC)" + @echo " • Нет ограничений Let's Encrypt (5 сертификатов в неделю)" + @echo " • Мгновенное создание без DNS-валидации" + @echo " • Идеально для тестирования интеграции с NPM" + @echo " • Можно создавать неограниченное количество раз" + @echo "" + @echo "$(YELLOW)Примечание:$(NC)" + @echo " Тестовый сертификат НЕ доверяется браузерами." + @echo " Используйте только для локальной разработки и тестирования." + @echo "" + +# Очистка временных файлов +clean: + @echo "$(YELLOW)→ Очистка временных файлов...$(NC)" + @find . -type f -name "*.pyc" -delete + @find . -type d -name "__pycache__" -delete + @echo "$(GREEN)✓ Очистка завершена$(NC)" + +# По умолчанию показываем помощь +.DEFAULT_GOAL := help diff --git a/PROJECT_STRUCTURE.md b/PROJECT_STRUCTURE.md new file mode 100644 index 0000000..ecd00a7 --- /dev/null +++ b/PROJECT_STRUCTURE.md @@ -0,0 +1,202 @@ +# 📁 Структура проекта configure_nginx_manager + +## Основные скрипты + +### Python (Рекомендуется) +- **letsencrypt_regru_api.py** (1,411 строк) + - Полнофункциональный Python скрипт + - Прямая работа с API reg.ru + - Интеграция с Nginx Proxy Manager + - Автоматическая проверка и обновление сертификатов + - Генерация тестовых самоподписанных сертификатов + - Поддержка wildcard доменов + +### Bash +- **letsencrypt_regru_dns.sh** + - Bash скрипт с certbot-dns-regru плагином + - Простота использования + - Минимальные зависимости + +### PowerShell +- **letsencrypt_regru.ps1** + - Windows версия + - Аналогична Bash скрипту + +### Тестирование +- **test_certificate.sh** + - Быстрое создание тестовых сертификатов через OpenSSL + - Автономная работа без Python + - Поддержка wildcard доменов + +## Автоматизация + +### Makefile +- **Makefile** (415 строк) + - `make install` - Полная установка и настройка + - `make uninstall` - Чистое удаление + - `make status` - Проверка состояния + - `make test-cert` - Создание тестового сертификата + - `make obtain` - Получение Let's Encrypt сертификата + - `make renew` - Обновление сертификата + - `make logs` - Просмотр логов + - `make check-config` - Валидация конфигурации + +## Конфигурация + +### config.json.example +Пример конфигурации со всеми параметрами: +- Учетные данные reg.ru API +- Настройки домена и email +- Параметры обновления (renewal_days) +- Настройки Nginx Proxy Manager +- Пути к директориям и логам + +## Документация + +### README.md (1,420+ строк) +Основная документация: +- Введение и возможности +- Быстрый старт +- Установка через Makefile +- Создание тестовых сертификатов +- Требования и установка зависимостей +- Настройка и использование +- Интеграция с NPM +- Автоматическая проверка и обновление +- Автоматизация через cron/systemd +- Устранение неполадок + +### TESTING_GUIDE.md (370+ строк) +Руководство по тестированию: +- Зачем нужны тестовые сертификаты +- Обход лимитов Let's Encrypt (5 в неделю) +- Быстрый старт с тестовыми сертификатами +- Сравнение методов создания +- Использование в разработке +- Автоматизация тестирования +- Переход с тестовых на production +- Частые вопросы +- Примеры для CI/CD и Docker + +### PROJECT_STRUCTURE.md (этот файл) +- Описание всех файлов проекта +- Краткая характеристика каждого компонента + +## Вспомогательные файлы + +### Markdown документы +- **Add Let's Encrypt Certificate для провайдера reg.ru.md** + - Первоначальные инструкции + +- **Создание и продление SSL сертификата.md** + - Дополнительная информация о процессе + +## Возможности + +### ✅ Основные +- [x] Создание Let's Encrypt сертификатов через reg.ru DNS API +- [x] Wildcard сертификаты (*.domain.com) +- [x] Автоматическое обновление сертификатов +- [x] DNS-01 валидация +- [x] Интеграция с Nginx Proxy Manager +- [x] Автоматическая загрузка/обновление в NPM + +### ✅ Продвинутые +- [x] Автоматическая проверка срока действия +- [x] Настраиваемый порог обновления (renewal_days) +- [x] Systemd service + timer +- [x] Cron автоматизация +- [x] Подробное логирование +- [x] Валидация конфигурации + +### 🆕 Тестирование +- [x] Генерация самоподписанных тестовых сертификатов +- [x] Обход лимитов Let's Encrypt (5/неделю) +- [x] Мгновенное создание без DNS +- [x] Интеграция тестовых сертификатов с NPM +- [x] Полная совместимость структуры с Let's Encrypt + +## Установка + +### Быстрая установка +```bash +sudo make install +sudo nano /etc/letsencrypt/regru_config.json +sudo make test-cert # Для тестирования +sudo make obtain # Для production +``` + +### Структура после установки +``` +/opt/letsencrypt-regru/ +├── letsencrypt_regru_api.py + +/etc/letsencrypt/ +├── regru_config.json +└── live/ + └── example.com/ + ├── privkey.pem + ├── cert.pem + ├── fullchain.pem + └── chain.pem + +/etc/systemd/system/ +├── letsencrypt-regru.service +└── letsencrypt-regru.timer + +/var/log/letsencrypt/ +└── letsencrypt_regru.log +``` + +## Использование + +### Тестирование (без лимитов) +```bash +sudo make test-cert # Создать тестовый сертификат +sudo make status # Проверить статус +``` + +### Production +```bash +sudo make obtain # Получить Let's Encrypt сертификат +sudo make renew # Обновить сертификат +sudo make run # Автоматический режим +``` + +### Мониторинг +```bash +sudo make logs # Просмотр логов +sudo make status # Статус служб +sudo make check-config # Проверка конфигурации +``` + +## Технологии + +- **Python 3.6+** - Основной язык +- **Certbot** - Let's Encrypt клиент +- **requests** - HTTP запросы к API +- **cryptography** - Генерация тестовых сертификатов +- **systemd** - Автоматизация запуска +- **cron** - Альтернативная автоматизация +- **Make** - Управление установкой +- **OpenSSL** - Альтернативная генерация сертификатов + +## Лицензия + +Open Source - свободное использование + +## Автор + +GitHub Copilot @ 2025 + +## Поддержка + +См. документацию: +- [README.md](README.md) - Основное руководство +- [TESTING_GUIDE.md](TESTING_GUIDE.md) - Руководство по тестированию + +--- + +**Версия**: 2.1 +**Дата**: 27 октября 2025 +**Статус**: ✅ Production Ready diff --git a/README.md b/README.md index 147e8cf..b2cf63b 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,32 @@ # Руководство по использованию скриптов для управления SSL сертификатами Let's Encrypt с DNS-валидацией через API reg.ru +## 🆕 Новое в версии 2.0 + +**Автоматическая интеграция с Nginx Proxy Manager!** + +Python скрипт теперь автоматически загружает созданные сертификаты в Nginx Proxy Manager. + +- ✅ Автоматическая загрузка сертификатов в NPM +- ✅ Автоматическое обновление существующих сертификатов +- ✅ Полная интеграция через API NPM +- ✅ Поддержка wildcard сертификатов + +--- + ## Содержание 1. [Введение](#введение) -2. [Требования](#требования) -3. [Установка зависимостей](#установка-зависимостей) -4. [Настройка](#настройка) -5. [Использование Bash скрипта](#использование-bash-скрипта) -6. [Использование Python скрипта](#использование-python-скрипта) -7. [Автоматизация обновления](#автоматизация-обновления) -8. [Устранение неполадок](#устранение-неполадок) +2. [Быстрый старт](#-быстрый-старт) +3. [Установка через Makefile](#-установка-через-makefile) +4. [Создание тестовых сертификатов](#-создание-тестового-самоподписанного-сертификата) +5. [Требования](#требования) +6. [Установка зависимостей](#установка-зависимостей) +7. [Настройка](#настройка) +8. [Использование Bash скрипта](#использование-bash-скрипта) +9. [Использование Python скрипта](#использование-python-скрипта) +10. [Интеграция с Nginx Proxy Manager](#интеграция-с-nginx-proxy-manager) +11. [Автоматическая проверка и обновление сертификатов](#автоматическая-проверка-и-обновление-сертификатов) +12. [Автоматизация обновления](#автоматизация-обновления) +13. [Устранение неполадок](#устранение-неполадок) --- @@ -17,7 +35,7 @@ В проекте представлены два скрипта для автоматического создания и обновления SSL сертификатов Let's Encrypt с использованием DNS-валидации через API reg.ru: 1. **letsencrypt_regru_dns.sh** - Bash скрипт с использованием плагина certbot-dns-regru -2. **letsencrypt_regru_api.py** - Python скрипт с прямым взаимодействием с API reg.ru +2. **letsencrypt_regru_api.py** - Python скрипт с прямым взаимодействием с API reg.ru и интеграцией с NPM Оба скрипта поддерживают: - Создание wildcard сертификатов (*.domain.com) @@ -26,6 +44,333 @@ - Логирование всех операций - Перезагрузку веб-сервера после обновления +**Дополнительно Python скрипт поддерживает:** +- ✨ Автоматическую загрузку в Nginx Proxy Manager +- ✨ Автоматическое обновление сертификатов в NPM +- ✨ API интеграцию с NPM + +--- + +## 🚀 Установка через Makefile + +**Самый быстрый способ установки на Linux!** + +Makefile автоматизирует весь процесс установки, настройки systemd-сервисов и cron-заданий. + +### Быстрая установка + +```bash +# 1. Установка (требует root) +sudo make install + +# 2. Редактирование конфигурации +sudo nano /etc/letsencrypt/regru_config.json + +# 3. Проверка конфигурации +sudo make check-config + +# 4. Тестовый запуск +sudo make test-run +``` + +### Доступные команды Makefile + +#### Основные команды + +```bash +# Установка всего: создание директорий, копирование скрипта, настройка systemd и cron +sudo make install + +# Полное удаление: удаление службы, cron-задания, файлов +sudo make uninstall + +# Показать статус установки, systemd и cron +sudo make status + +# Справка по всем командам +make help +``` + +#### Утилиты + +```bash +# Проверка JSON конфигурации на валидность +sudo make check-config + +# Тестовый запуск без обновления cron/systemd +sudo make test-run + +# Просмотр логов +sudo make logs + +# Запуск скрипта напрямую +sudo make run + +# Получение нового сертификата +sudo make obtain + +# Обновление существующего сертификата +sudo make renew + +# Создание тестового самоподписанного сертификата +sudo make test-cert + +# Очистка логов +sudo make clean +``` + +### Что делает `make install` + +1. **Создает директории** + - `/opt/letsencrypt-regru/` - директория установки + - `/var/log/letsencrypt/` - директория логов + +2. **Устанавливает зависимости** + ```bash + pip3 install certbot requests cryptography + ``` + +3. **Копирует скрипт** + - Копирует `letsencrypt_regru_api.py` в `/opt/letsencrypt-regru/` + - Устанавливает права на выполнение + +4. **Создает конфигурацию** + - Создает `/etc/letsencrypt/regru_config.json` (если не существует) + - Устанавливает права 600 для безопасности + +5. **Настраивает systemd** + - Создает `letsencrypt-regru.service` - разовый запуск + - Создает `letsencrypt-regru.timer` - таймер для ежедневного запуска + - Включает и запускает таймер + +6. **Настраивает cron** + - Добавляет задание для запуска каждый день в 3:00 AM + ``` + 0 3 * * * /opt/letsencrypt-regru/letsencrypt_regru_api.py --config /etc/letsencrypt/regru_config.json --auto >> /var/log/letsencrypt/letsencrypt_regru.log 2>&1 + ``` + +### Что делает `make uninstall` + +1. **Останавливает и удаляет службы** + - Останавливает systemd timer и service + - Удаляет файлы служб из `/etc/systemd/system/` + - Перезагружает конфигурацию systemd + +2. **Удаляет cron-задание** + - Удаляет запись из crontab + +3. **Удаляет файлы** (с подтверждением) + - Удаляет `/opt/letsencrypt-regru/` + - Опционально удаляет конфигурацию и логи + +### Пример: Полная установка от А до Я + +```bash +# 1. Клонируем или скачиваем проект +cd /tmp +git clone +cd configure_nginx_manager + +# 2. Устанавливаем через Makefile +sudo make install + +# 3. Редактируем конфигурацию +sudo nano /etc/letsencrypt/regru_config.json + +# Вставляем реальные данные: +{ + "regru_username": "myuser", + "regru_password": "mypassword", + "domain": "example.com", + "wildcard": true, + "email": "admin@example.com", + "renewal_days": 30, + "npm_enabled": true, + "npm_host": "https://npm.example.com", + "npm_email": "admin@example.com", + "npm_password": "npm_password" +} + +# 4. Проверяем конфигурацию +sudo make check-config + +# 5. Тестируем +sudo make test-run + +# 6. Проверяем статус +sudo make status + +# 7. Смотрим логи +sudo make logs +``` + +### Структура после установки + +``` +/opt/letsencrypt-regru/ +├── letsencrypt_regru_api.py # Основной скрипт + +/etc/letsencrypt/ +├── regru_config.json # Конфигурация (600) +└── live/ # Сертификаты Let's Encrypt + └── example.com/ + ├── fullchain.pem + └── privkey.pem + +/var/log/letsencrypt/ +└── letsencrypt_regru.log # Логи + +/etc/systemd/system/ +├── letsencrypt-regru.service # Systemd сервис +└── letsencrypt-regru.timer # Systemd таймер (ежедневно) +``` + +### Проверка работы автоматизации + +```bash +# Статус systemd таймера +sudo systemctl status letsencrypt-regru.timer + +# Когда будет следующий запуск +sudo systemctl list-timers letsencrypt-regru.timer + +# Проверка cron +sudo crontab -l | grep letsencrypt + +# Ручной запуск службы (для теста) +sudo systemctl start letsencrypt-regru.service + +# Просмотр логов службы +sudo journalctl -u letsencrypt-regru.service -f +``` + +### Удаление + +```bash +# Полное удаление +sudo make uninstall + +# Система спросит подтверждение перед удалением конфигурации и логов +``` + +### 🧪 Создание тестового самоподписанного сертификата + +**Идеально для тестирования без ограничений Let's Encrypt!** + +Let's Encrypt имеет ограничения на количество сертификатов (5 в неделю на домен). Для тестирования и разработки можно использовать самоподписанные сертификаты. + +#### Преимущества тестовых сертификатов + +✅ **Нет ограничений** - создавайте сколько угодно сертификатов +✅ **Мгновенное создание** - без DNS-валидации и ожидания +✅ **Тестирование NPM** - проверка интеграции с Nginx Proxy Manager +✅ **Офлайн работа** - не требуется интернет и API reg.ru +✅ **Идентичная структура** - те же файлы, что и Let's Encrypt + +⚠️ **Ограничения**: Браузеры не доверяют самоподписанным сертификатам + +#### Быстрое создание + +```bash +# Создать тестовый сертификат +sudo make test-cert +``` + +Команда автоматически: +1. Генерирует RSA ключ 2048 бит +2. Создает самоподписанный сертификат на 90 дней +3. Поддерживает wildcard домены (если настроено) +4. Создает все необходимые файлы (privkey.pem, cert.pem, fullchain.pem, chain.pem) +5. Опционально загружает в Nginx Proxy Manager + +#### Использование Python скрипта напрямую + +```bash +# Создать тестовый сертификат с подробным выводом +sudo python3 /opt/letsencrypt-regru/letsencrypt_regru_api.py \ + --config /etc/letsencrypt/regru_config.json \ + --test-cert -v +``` + +#### Что создается + +После выполнения команды будут созданы файлы: + +``` +/etc/letsencrypt/live/example.com/ +├── privkey.pem # Приватный ключ RSA 2048 бит +├── cert.pem # Сертификат +├── fullchain.pem # Полная цепочка (для nginx) +└── chain.pem # Цепочка CA (пустой для самоподписанного) +``` + +#### Интеграция с NPM + +Если в конфигурации включена интеграция с NPM (`npm_enabled: true`), тестовый сертификат автоматически загрузится в Nginx Proxy Manager: + +```json +{ + "npm_enabled": true, + "npm_host": "https://npm.example.com", + "npm_email": "admin@example.com", + "npm_password": "password" +} +``` + +#### Пример вывода + +``` +═══════════════════════════════════════════════════════════════ +ГЕНЕРАЦИЯ ТЕСТОВОГО САМОПОДПИСАННОГО СЕРТИФИКАТА +═══════════════════════════════════════════════════════════════ +Домен: example.com +Wildcard: True +Срок действия: 90 дней +⚠️ ВНИМАНИЕ: Это тестовый сертификат, не для production! + +✓ Приватный ключ сохранен: /etc/letsencrypt/live/example.com/privkey.pem +✓ Сертификат сохранен: /etc/letsencrypt/live/example.com/cert.pem +✓ Fullchain сохранен: /etc/letsencrypt/live/example.com/fullchain.pem +✓ Chain файл создан: /etc/letsencrypt/live/example.com/chain.pem + +═══════════════════════════════════════════════════════════════ +ИНФОРМАЦИЯ О СЕРТИФИКАТЕ +═══════════════════════════════════════════════════════════════ +Домен: example.com +Wildcard: *.example.com +Действителен с: 2025-10-27 12:00:00 +Действителен до: 2026-01-25 12:00:00 +``` + +#### Когда использовать тестовые сертификаты + +**✅ Используйте для:** +- Локальной разработки и тестирования +- Проверки интеграции с Nginx Proxy Manager +- Тестирования автоматизации +- Разработки без доступа к интернету +- Избежания лимитов Let's Encrypt при частом тестировании + +**❌ НЕ используйте для:** +- Production окружения +- Публичных веб-сайтов +- Любых случаев, где требуется доверие браузеров + +#### Переход с тестового на production + +После успешного тестирования легко переключиться на настоящий Let's Encrypt сертификат: + +```bash +# 1. Удалить тестовый сертификат +sudo rm -rf /etc/letsencrypt/live/example.com/ + +# 2. Получить настоящий сертификат +sudo make obtain + +# Или автоматически +sudo make run +``` + --- ## Требования @@ -240,6 +585,531 @@ sudo python3 letsencrypt_regru_api.py -c /etc/letsencrypt/regru_config.json --- +## 🚀 Быстрый старт + +### За 3 простых шага получите SSL сертификат в Nginx Proxy Manager! + +#### Шаг 1: Создайте конфигурацию + +```bash +sudo python3 letsencrypt_regru_api.py --create-config /etc/letsencrypt/regru_config.json +``` + +#### Шаг 2: Отредактируйте параметры + +```bash +sudo nano /etc/letsencrypt/regru_config.json +``` + +Заполните: + +```json +{ + "regru_username": "ваш_логин_regru", + "regru_password": "ваш_пароль_regru", + "domain": "dfv24.com", + "wildcard": true, + "email": "admin@dfv24.com", + + "npm_enabled": true, + "npm_host": "http://192.168.10.14:81", + "npm_email": "admin@example.com", + "npm_password": "changeme" +} +``` + +#### Шаг 3: Получите сертификат + +```bash +sudo python3 letsencrypt_regru_api.py -c /etc/letsencrypt/regru_config.json --obtain +``` + +### ✅ Готово! + +Откройте Nginx Proxy Manager → SSL Certificates + +Ваш сертификат `*.dfv24.com` готов к использованию! 🎉 + +**Что произошло:** +1. ✅ Создан wildcard сертификат через Let's Encrypt +2. ✅ Выполнена DNS-валидация через API reg.ru +3. ✅ Сертификат автоматически загружен в Nginx Proxy Manager +4. ✅ Веб-сервер перезагружен + +--- + +## Интеграция с Nginx Proxy Manager + +### Обзор возможностей + +Скрипт `letsencrypt_regru_api.py` поддерживает автоматическое добавление и обновление SSL сертификатов в Nginx Proxy Manager через его API. + +**Возможности:** +- ✅ Автоматическое добавление новых сертификатов в NPM +- ✅ Обновление существующих сертификатов в NPM +- ✅ Поиск сертификатов по доменному имени +- ✅ Поддержка wildcard сертификатов +- ✅ Полная синхронизация после создания/обновления + +### Настройка интеграции + +#### 1. Параметры конфигурации NPM + +| Параметр | Описание | Пример | +|----------|----------|--------| +| `npm_enabled` | Включить интеграцию с NPM | `true` или `false` | +| `npm_host` | URL адрес NPM | `http://192.168.10.14:81` | +| `npm_email` | Email для входа в NPM | `admin@example.com` | +| `npm_password` | Пароль администратора NPM | `changeme` | + +#### 2. Получение учетных данных NPM + +1. Войдите в Nginx Proxy Manager: `http://192.168.10.14:81` +2. Используйте email и пароль администратора +3. По умолчанию: + - Email: `admin@example.com` + - Password: `changeme` +4. **ВАЖНО:** Измените пароль по умолчанию! + +### Использование + +#### Автоматическая синхронизация + +После создания или обновления сертификата скрипт автоматически: +1. Авторизуется в Nginx Proxy Manager +2. Проверит, существует ли сертификат для домена +3. Создаст новый сертификат или обновит существующий +4. Загрузит файлы сертификата в NPM + +#### Создание нового сертификата с автоматической загрузкой в NPM + +```bash +sudo python3 letsencrypt_regru_api.py -c /etc/letsencrypt/regru_config.json --obtain +``` + +**Скрипт выполнит:** +- ✅ Создание сертификата через Let's Encrypt +- ✅ DNS-валидация через reg.ru API +- ✅ Автоматическая загрузка в NPM +- ✅ Перезагрузка веб-сервера + +#### Обновление существующего сертификата + +```bash +sudo python3 letsencrypt_regru_api.py -c /etc/letsencrypt/regru_config.json --renew +``` + +**Скрипт выполнит:** +- ✅ Обновление сертификата через certbot +- ✅ Автоматическое обновление в NPM +- ✅ Перезагрузка веб-сервера + +#### Автоматический режим + +```bash +sudo python3 letsencrypt_regru_api.py -c /etc/letsencrypt/regru_config.json +``` + +Скрипт автоматически определит: +- Нужно ли создать новый сертификат +- Требуется ли обновление (если осталось < 30 дней) +- Выполнит синхронизацию с NPM + +### Работа с API Nginx Proxy Manager + +#### Класс NginxProxyManagerAPI + +Скрипт использует класс `NginxProxyManagerAPI` для работы с NPM: + +```python +from letsencrypt_regru_api import NginxProxyManagerAPI + +# Инициализация +npm_api = NginxProxyManagerAPI( + host="http://192.168.10.14:81", + email="admin@example.com", + password="changeme", + logger=logger +) + +# Авторизация +npm_api.login() + +# Получение списка сертификатов +certificates = npm_api.get_certificates() + +# Поиск сертификата по домену +cert = npm_api.find_certificate_by_domain("dfv24.com") + +# Синхронизация сертификата +npm_api.sync_certificate("dfv24.com", "/etc/letsencrypt/live/dfv24.com") +``` + +#### API Endpoints + +Скрипт использует следующие endpoints NPM API: + +| Endpoint | Метод | Описание | +|----------|-------|----------| +| `/api/tokens` | POST | Авторизация | +| `/api/nginx/certificates` | GET | Список сертификатов | +| `/api/nginx/certificates` | POST | Создание сертификата | +| `/api/nginx/certificates/{id}` | PUT | Обновление сертификата | + +### Логи и отладка + +#### Просмотр логов + +```bash +# Основной лог скрипта +sudo tail -f /var/log/letsencrypt_regru.log + +# Подробный режим +sudo python3 letsencrypt_regru_api.py -c config.json --obtain -v +``` + +#### Примеры логов при успешной синхронизации + +``` +2025-10-27 10:30:15 - INFO - === Синхронизация сертификата с Nginx Proxy Manager === +2025-10-27 10:30:15 - INFO - Авторизация в Nginx Proxy Manager... +2025-10-27 10:30:16 - INFO - Авторизация в NPM успешна +2025-10-27 10:30:16 - DEBUG - Получение списка сертификатов из NPM... +2025-10-27 10:30:16 - DEBUG - Получено 3 сертификатов +2025-10-27 10:30:16 - INFO - Создание нового сертификата в NPM +2025-10-27 10:30:16 - INFO - Загрузка сертификата для dfv24.com в NPM... +2025-10-27 10:30:17 - INFO - Сертификат успешно загружен в NPM (ID: 4) +2025-10-27 10:30:17 - INFO - Сертификат успешно добавлен в Nginx Proxy Manager +``` + +### Устранение неполадок NPM + +#### Ошибка: Не удалось авторизоваться в NPM + +**Причины:** +- Неверный email или пароль +- NPM недоступен по указанному адресу +- Сетевые проблемы + +**Решение:** +```bash +# Проверьте доступность NPM +curl http://192.168.10.14:81/api/ + +# Проверьте учетные данные +# Войдите в NPM через браузер с теми же учетными данными +``` + +#### Ошибка: Сертификат не загружен в NPM + +**Причины:** +- Файлы сертификата не найдены +- Неправильный формат сертификата +- Проблемы с API NPM + +**Решение:** +```bash +# Проверьте наличие файлов сертификата +ls -la /etc/letsencrypt/live/dfv24.com/ + +# Проверьте права доступа +sudo chmod 644 /etc/letsencrypt/live/dfv24.com/*.pem + +# Попробуйте вручную +sudo python3 -c " +from letsencrypt_regru_api import NginxProxyManagerAPI +import logging +logger = logging.getLogger() +npm = NginxProxyManagerAPI('http://192.168.10.14:81', 'admin@example.com', 'changeme', logger) +npm.login() +print(npm.get_certificates()) +" +``` + +#### Ошибка: API NPM возвращает 401 (Unauthorized) + +**Решение:** +- Проверьте учетные данные в конфигурации +- Убедитесь, что пароль был изменен с дефолтного +- Попробуйте войти через веб-интерфейс + +#### Ошибка: Сертификат создан, но не обновляется в NPM + +**Причина:** Скрипт не может найти существующий сертификат + +**Решение:** +```bash +# Просмотрите список сертификатов в NPM +# SSL Certificates → найдите сертификат для вашего домена + +# Удалите старый сертификат вручную через UI +# Запустите скрипт снова - будет создан новый +``` + +### Безопасность NPM + +#### Защита учетных данных + +```bash +# Установите правильные права на конфигурацию +sudo chmod 600 /etc/letsencrypt/regru_config.json +sudo chown root:root /etc/letsencrypt/regru_config.json +``` + +#### Рекомендации + +1. **Измените пароль NPM по умолчанию** + ``` + Старый пароль: changeme + Новый пароль: надежный_пароль + ``` + +2. **Используйте HTTPS для NPM** (если доступно) + ```json + "npm_host": "https://192.168.10.14:443" + ``` + +3. **Ограничьте доступ к API NPM** + - Настройте firewall + - Используйте VPN для удаленного доступа + +### Проверка результата + +#### В логах скрипта + +```bash +sudo tail -n 50 /var/log/letsencrypt_regru.log | grep -i npm +``` + +#### В веб-интерфейсе NPM + +1. Откройте NPM: `http://192.168.10.14:81` +2. Войдите в систему +3. Перейдите в **SSL Certificates** +4. Проверьте наличие сертификата для вашего домена +5. Проверьте дату истечения + +#### В командной строке + +```bash +# Список сертификатов в NPM через API +curl -X POST http://192.168.10.14:81/api/tokens \ + -H "Content-Type: application/json" \ + -d '{"identity":"admin@example.com","secret":"changeme"}' \ + | jq -r '.token' > /tmp/npm_token + +curl -H "Authorization: Bearer $(cat /tmp/npm_token)" \ + http://192.168.10.14:81/api/nginx/certificates \ + | jq '.' +``` + +### Дополнительные возможности + +#### Отключение синхронизации с NPM + +Если нужно временно отключить синхронизацию: + +```json +{ + "npm_enabled": false +} +``` + +#### Использование с несколькими доменами + +Создайте отдельные конфигурационные файлы: + +```bash +# Для домена 1 +sudo python3 letsencrypt_regru_api.py -c /etc/letsencrypt/domain1_config.json + +# Для домена 2 +sudo python3 letsencrypt_regru_api.py -c /etc/letsencrypt/domain2_config.json +``` + +--- + +## Автоматическая проверка и обновление сертификатов + +### Как это работает + +Скрипт в автоматическом режиме (без флагов `--obtain` или `--renew`) выполняет интеллектуальную проверку: + +#### 1. Проверка наличия сертификата + +```bash +sudo python3 letsencrypt_regru_api.py -c /etc/letsencrypt/regru_config.json +``` + +**Если сертификата нет:** +- ✅ Создает новый сертификат через Let's Encrypt +- ✅ Выполняет DNS-валидацию через reg.ru +- ✅ Загружает сертификат в Nginx Proxy Manager (если включено) +- ✅ Перезагружает веб-сервер + +#### 2. Проверка срока действия + +**Если сертификат существует:** +- 🔍 Проверяет сколько дней осталось до истечения +- 📅 Сравнивает с порогом обновления (по умолчанию 30 дней) + +**Если осталось меньше 30 дней:** +- 🔄 Автоматически обновляет сертификат +- ✅ Синхронизирует с Nginx Proxy Manager +- ✅ Перезагружает веб-сервер + +**Если сертификат действителен (более 30 дней):** +- ℹ️ Выводит информацию о сертификате +- ✅ Проверяет наличие в NPM и синхронизирует при необходимости +- ⏭️ Завершает работу (обновление не требуется) + +### Настройка порога обновления + +В конфигурации можно изменить порог обновления: + +```json +{ + "renewal_days": 30, # За сколько дней до истечения обновлять +} +``` + +**Рекомендуемые значения:** +- `30` - по умолчанию (рекомендуется) +- `14` - для более консервативного подхода +- `60` - для раннего обновления + +### Примеры работы скрипта + +#### Сценарий 1: Сертификата нет + +```bash +$ sudo python3 letsencrypt_regru_api.py -c config.json + +============================================================ +АВТОМАТИЧЕСКАЯ ПРОВЕРКА И ОБНОВЛЕНИЕ СЕРТИФИКАТА +============================================================ +Порог обновления: 30 дней до истечения +Сертификат не найден +============================================================ +СТАТУС: Сертификат не найден +ДЕЙСТВИЕ: Создание нового сертификата +============================================================ +=== Запрос нового SSL сертификата === +... +Сертификат успешно получен! +============================================================ +СИНХРОНИЗАЦИЯ С NGINX PROXY MANAGER +============================================================ +✅ Сертификат успешно создан в Nginx Proxy Manager +============================================================ +ОПЕРАЦИЯ ЗАВЕРШЕНА УСПЕШНО +============================================================ +``` + +#### Сценарий 2: Сертификат истекает через 20 дней + +```bash +$ sudo python3 letsencrypt_regru_api.py -c config.json + +============================================================ +АВТОМАТИЧЕСКАЯ ПРОВЕРКА И ОБНОВЛЕНИЕ СЕРТИФИКАТА +============================================================ +Порог обновления: 30 дней до истечения +Сертификат истекает: 2025-11-16 +Осталось дней: 20 +============================================================ +СТАТУС: Сертификат истекает через 20 дней +ДЕЙСТВИЕ: Обновление сертификата (порог: 30 дней) +============================================================ +=== Обновление SSL сертификата === +... +Проверка обновления завершена +============================================================ +РЕЗУЛЬТАТ: Сертификат успешно обновлен +============================================================ +✅ Сертификат успешно обновлен в Nginx Proxy Manager +``` + +#### Сценарий 3: Сертификат действителен (60 дней) + +```bash +$ sudo python3 letsencrypt_regru_api.py -c config.json + +============================================================ +АВТОМАТИЧЕСКАЯ ПРОВЕРКА И ОБНОВЛЕНИЕ СЕРТИФИКАТА +============================================================ +Порог обновления: 30 дней до истечения +Сертификат истекает: 2025-12-26 +Осталось дней: 60 +============================================================ +СТАТУС: Сертификат действителен (60 дней) +ДЕЙСТВИЕ: Обновление не требуется +============================================================ +Проверка синхронизации с Nginx Proxy Manager... +Сертификат найден в NPM (ID: 4) +``` + +### Ежедневная автоматическая проверка + +Для ежедневной проверки и автоматического обновления настройте cron: + +```bash +# Редактируем crontab +sudo crontab -e + +# Добавляем задачу - проверка каждый день в 3:00 утра +0 3 * * * /usr/bin/python3 /path/to/letsencrypt_regru_api.py -c /etc/letsencrypt/regru_config.json >> /var/log/letsencrypt_cron.log 2>&1 +``` + +**Что будет происходить каждый день:** +1. 🕒 В 3:00 утра запускается скрипт +2. 🔍 Проверяет наличие и срок действия сертификата +3. 📊 Записывает результат в лог +4. 🔄 Обновляет сертификат только если нужно (< 30 дней) +5. ✅ Синхронизирует с NPM при обновлении + +### Мониторинг работы + +#### Просмотр логов ежедневной проверки + +```bash +# Последние проверки +sudo tail -n 100 /var/log/letsencrypt_cron.log + +# Фильтр по статусу +sudo grep "СТАТУС:" /var/log/letsencrypt_cron.log + +# Фильтр по действиям +sudo grep "ДЕЙСТВИЕ:" /var/log/letsencrypt_cron.log + +# Только успешные операции +sudo grep "ОПЕРАЦИЯ ЗАВЕРШЕНА УСПЕШНО" /var/log/letsencrypt_cron.log +``` + +#### Проверка следующего запуска cron + +```bash +# Список задач cron +sudo crontab -l + +# Статус cron службы +sudo systemctl status cron +``` + +### Уведомления при обновлении + +Добавьте email уведомления в cron: + +```bash +# В начале crontab добавьте +MAILTO=admin@dfv24.com + +# Задача с уведомлениями +0 3 * * * /usr/bin/python3 /path/to/letsencrypt_regru_api.py -c /etc/letsencrypt/regru_config.json 2>&1 | mail -s "SSL Certificate Check - $(date)" admin@dfv24.com +``` + +--- + ## Автоматизация обновления Let's Encrypt сертификаты действительны 90 дней. Рекомендуется настроить автоматическое обновление. @@ -506,6 +1376,14 @@ server { --- +## Дополнительная документация + +- 📘 **[TESTING_GUIDE.md](TESTING_GUIDE.md)** - Полное руководство по созданию и использованию тестовых сертификатов +- 🚀 **[Makefile](Makefile)** - Автоматизация установки и управления +- 📝 **[config.json.example](config.json.example)** - Пример конфигурации + +--- + ## Поддержка и вопросы При возникновении проблем: @@ -515,6 +1393,7 @@ server { 3. Используйте подробный режим: `-v` для Python скрипта 4. Проверьте документацию reg.ru API: https://www.reg.ru/support/api 5. Проверьте документацию Let's Encrypt: https://letsencrypt.org/docs/ +6. **Для тестирования**: См. [TESTING_GUIDE.md](TESTING_GUIDE.md) --- @@ -525,8 +1404,17 @@ server { **Рекомендации:** - Используйте Python скрипт для более гибкой настройки и интеграции - Используйте Bash скрипт для простоты и минимальных зависимостей +- **Используйте тестовые сертификаты для разработки** (без лимитов Let's Encrypt) - Настройте автоматическое обновление через cron или systemd timer - Регулярно проверяйте логи и статус сертификатов - Храните учетные данные в безопасности (chmod 600) +**Быстрый старт для тестирования:** +```bash +sudo make install # Установка +sudo make test-cert # Создать тестовый сертификат +sudo make obtain # Получить production сертификат +``` + Успешной автоматизации! 🔒 + diff --git a/TESTING_GUIDE.md b/TESTING_GUIDE.md new file mode 100644 index 0000000..fccc5f7 --- /dev/null +++ b/TESTING_GUIDE.md @@ -0,0 +1,382 @@ +# 🧪 Руководство по тестированию SSL сертификатов + +## Зачем нужны тестовые сертификаты? + +Let's Encrypt имеет **строгие ограничения**: +- ⚠️ Максимум **5 сертификатов в неделю** на один домен +- ⚠️ Максимум **50 сертификатов в неделю** на аккаунт +- ⚠️ **Бан на 1 неделю** при превышении лимита + +**Решение**: Используйте самоподписанные тестовые сертификаты для разработки! + +--- + +## Быстрый старт + +### Вариант 1: Через Makefile (рекомендуется) + +```bash +# После установки скрипта (make install) +sudo make test-cert +``` + +**Результат**: Создан сертификат в `/etc/letsencrypt/live/ваш-домен/` + +### Вариант 2: Через Python скрипт + +```bash +sudo python3 letsencrypt_regru_api.py \ + --config /etc/letsencrypt/regru_config.json \ + --test-cert -v +``` + +### Вариант 3: Через Bash скрипт (автономный) + +```bash +# Простой домен +sudo ./test_certificate.sh example.com no + +# С wildcard +sudo ./test_certificate.sh example.com yes +``` + +--- + +## Сравнение методов + +| Метод | Скорость | Требования | NPM интеграция | Лимиты | +|-------|----------|------------|----------------|--------| +| **Let's Encrypt** | 2-5 минут | Internet, DNS | ✅ Да | ⚠️ 5/неделю | +| **Тестовый (Python)** | 1-2 секунды | Только Python | ✅ Да | ✅ Нет | +| **Тестовый (Bash)** | 1-2 секунды | Только OpenSSL | ❌ Ручная | ✅ Нет | + +--- + +## Детальная инструкция + +### 1. Подготовка конфигурации + +```bash +# Создать конфигурацию +sudo nano /etc/letsencrypt/regru_config.json +``` + +```json +{ + "domain": "test.example.com", + "wildcard": true, + "cert_dir": "/etc/letsencrypt/live", + "npm_enabled": true, + "npm_host": "https://npm.example.com", + "npm_email": "admin@example.com", + "npm_password": "your_password" +} +``` + +### 2. Создание тестового сертификата + +```bash +sudo make test-cert +``` + +### 3. Проверка созданных файлов + +```bash +ls -la /etc/letsencrypt/live/test.example.com/ +# Должны быть: +# - privkey.pem (приватный ключ) +# - cert.pem (сертификат) +# - fullchain.pem (полная цепочка) +# - chain.pem (CA цепочка) +``` + +### 4. Просмотр информации о сертификате + +```bash +openssl x509 -in /etc/letsencrypt/live/test.example.com/cert.pem -text -noout +``` + +--- + +## Использование в Nginx + +### Прямое использование + +```nginx +server { + listen 443 ssl; + server_name test.example.com; + + ssl_certificate /etc/letsencrypt/live/test.example.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/test.example.com/privkey.pem; + + # ... остальная конфигурация +} +``` + +### Через Nginx Proxy Manager + +Если `npm_enabled: true` в конфигурации, сертификат автоматически загрузится в NPM. + +**Проверка в NPM:** +1. Откройте веб-интерфейс NPM +2. Перейдите в **SSL Certificates** +3. Найдите ваш домен в списке +4. ⚠️ Будет помечен как "Custom" (не Let's Encrypt) + +--- + +## Автоматизация тестирования + +### Скрипт для CI/CD + +```bash +#!/bin/bash +# test_ssl_integration.sh + +set -e + +echo "🧪 Тестирование SSL интеграции..." + +# 1. Создать тестовый сертификат +sudo python3 letsencrypt_regru_api.py \ + --config test_config.json \ + --test-cert + +# 2. Проверить файлы +if [ ! -f "/etc/letsencrypt/live/test.example.com/fullchain.pem" ]; then + echo "❌ Сертификат не создан" + exit 1 +fi + +# 3. Проверить валидность +openssl x509 -in /etc/letsencrypt/live/test.example.com/cert.pem -noout -checkend 0 +if [ $? -eq 0 ]; then + echo "✅ Сертификат валиден" +else + echo "❌ Сертификат невалиден" + exit 1 +fi + +# 4. Проверить загрузку в NPM (опционально) +# ... ваша проверка через API NPM + +echo "✅ Все тесты пройдены" +``` + +### Makefile для тестирования + +```makefile +.PHONY: test-ssl test-npm test-all + +test-ssl: + @echo "Создание тестового сертификата..." + sudo make test-cert + @echo "Проверка файлов..." + test -f /etc/letsencrypt/live/$(DOMAIN)/fullchain.pem + @echo "✅ SSL тест пройден" + +test-npm: + @echo "Проверка интеграции с NPM..." + # Ваши проверки API NPM + @echo "✅ NPM тест пройден" + +test-all: test-ssl test-npm + @echo "✅ Все тесты пройдены" +``` + +--- + +## Переход на production + +### Шаг 1: Тестирование + +```bash +# 1. Создать тестовый сертификат +sudo make test-cert + +# 2. Проверить работу с NPM +# Открыть https://ваш-домен и проверить + +# 3. Убедиться что все работает +``` + +### Шаг 2: Переключение на Let's Encrypt + +```bash +# 1. Удалить тестовый сертификат +sudo rm -rf /etc/letsencrypt/live/ваш-домен/ + +# 2. Получить настоящий сертификат +sudo make obtain + +# 3. Проверить обновление в NPM +sudo make status +``` + +--- + +## Частые вопросы + +### Q: Почему браузер показывает предупреждение? + +**A:** Самоподписанные сертификаты не доверяются браузерами. Это нормально для тестирования. + +Чтобы избежать предупреждения в браузере (только для локального тестирования): +1. Chrome: `chrome://flags/#allow-insecure-localhost` +2. Firefox: Нажмите "Advanced" → "Accept the Risk" + +### Q: Можно ли использовать для production? + +**A:** ❌ **НЕТ!** Тестовые сертификаты только для разработки и тестирования. + +### Q: Как часто можно создавать тестовые сертификаты? + +**A:** ✅ Неограниченно! Нет никаких лимитов. + +### Q: Загружаются ли в NPM автоматически? + +**A:** ✅ Да, если `npm_enabled: true` в конфигурации. + +### Q: Работают ли с wildcard доменами? + +**A:** ✅ Да! Просто установите `"wildcard": true` в конфигурации. + +### Q: Как проверить срок действия? + +```bash +openssl x509 -in /etc/letsencrypt/live/ваш-домен/cert.pem -noout -dates +``` + +### Q: Как изменить срок действия? + +Отредактируйте `validity_days` в функции `generate_self_signed_certificate()`: + +```python +validity_days: int = 365 # Изменить на нужное количество дней +``` + +--- + +## Устранение проблем + +### Ошибка: Permission denied + +```bash +# Запускайте с sudo +sudo make test-cert +``` + +### Ошибка: Module 'cryptography' not found + +```bash +# Установите зависимости +sudo pip3 install cryptography +``` + +### NPM не показывает сертификат + +1. Проверьте настройки NPM в конфигурации +2. Проверьте логи: `sudo make logs` +3. Попробуйте загрузить вручную через веб-интерфейс NPM + +### Сертификат не создается + +```bash +# Проверьте права +ls -la /etc/letsencrypt/live/ + +# Создайте директорию вручную +sudo mkdir -p /etc/letsencrypt/live/ + +# Проверьте конфигурацию +sudo make check-config +``` + +--- + +## Примеры использования + +### Разработка с Docker + +```dockerfile +FROM nginx:alpine + +# Копировать тестовый сертификат +COPY test-certs/ /etc/nginx/ssl/ + +# Конфигурация nginx +COPY nginx.conf /etc/nginx/nginx.conf + +EXPOSE 443 +``` + +### Локальное тестирование + +```bash +# Создать сертификат для localhost +sudo python3 letsencrypt_regru_api.py --test-cert + +# Добавить в /etc/hosts +echo "127.0.0.1 test.example.com" | sudo tee -a /etc/hosts + +# Запустить nginx +sudo nginx -t && sudo nginx -s reload + +# Открыть в браузере +open https://test.example.com +``` + +### Автоматическое тестирование перед deployment + +```bash +#!/bin/bash +# pre-deploy.sh + +# Тестовая проверка SSL +sudo make test-cert +if [ $? -eq 0 ]; then + echo "✅ Тестовый сертификат создан успешно" + echo "✅ Готово к созданию production сертификата" + sudo make obtain +else + echo "❌ Ошибка при создании тестового сертификата" + exit 1 +fi +``` + +--- + +## Дополнительные ресурсы + +- 📘 [Let's Encrypt Rate Limits](https://letsencrypt.org/docs/rate-limits/) +- 📘 [OpenSSL Documentation](https://www.openssl.org/docs/) +- 📘 [Nginx Proxy Manager Docs](https://nginxproxymanager.com/guide/) + +--- + +## Итоговая шпаргалка + +```bash +# Установка +sudo make install + +# Конфигурация +sudo nano /etc/letsencrypt/regru_config.json + +# Создать тестовый сертификат +sudo make test-cert + +# Проверить +sudo make check-config +sudo make status + +# Переход на production +sudo rm -rf /etc/letsencrypt/live/домен/ +sudo make obtain + +# Автоматическое обновление +sudo make run +``` + +**Готово!** 🎉 Теперь вы можете тестировать SSL сертификаты без ограничений! diff --git a/config.json.example b/config.json.example index aa2f8cc..81b7493 100644 --- a/config.json.example +++ b/config.json.example @@ -8,5 +8,10 @@ "log_file": "/var/log/letsencrypt_regru.log", "dns_propagation_wait": 60, "dns_check_attempts": 10, - "dns_check_interval": 10 + "dns_check_interval": 10, + "renewal_days": 30, + "npm_enabled": true, + "npm_host": "http://192.168.10.14:81", + "npm_email": "admin@example.com", + "npm_password": "changeme" } diff --git a/Add Let's Encrypt Certificate для провайдера reg.ru.md b/docs/Add Let's Encrypt Certificate для провайдера reg.ru.md similarity index 100% rename from Add Let's Encrypt Certificate для провайдера reg.ru.md rename to docs/Add Let's Encrypt Certificate для провайдера reg.ru.md diff --git a/SSL_SCRIPTS_README.md b/docs/SSL_SCRIPTS_README.md similarity index 100% rename from SSL_SCRIPTS_README.md rename to docs/SSL_SCRIPTS_README.md diff --git a/Настройке Nginx Manager с SSL .md b/docs/Настройке Nginx Manager с SSL .md similarity index 100% rename from Настройке Nginx Manager с SSL .md rename to docs/Настройке Nginx Manager с SSL .md diff --git a/Создание и продление SSL сертификата.md b/docs/Создание и продление SSL сертификата.md similarity index 100% rename from Создание и продление SSL сертификата.md rename to docs/Создание и продление SSL сертификата.md diff --git a/letsencrypt_regru.ps1 b/letsencrypt_regru.ps1 index 1535c43..361d3f5 100644 --- a/letsencrypt_regru.ps1 +++ b/letsencrypt_regru.ps1 @@ -83,7 +83,7 @@ function Write-Log { [string]$Level = "INFO" ) - $Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + $Timestamp = Get-Date -Format "dd.MM.yyyy HH:mm:ss" $LogMessage = "[$Timestamp] [$Level] $Message" # Вывод в консоль diff --git a/letsencrypt_regru_api.py b/letsencrypt_regru_api.py index 488ba1f..23df0b4 100644 --- a/letsencrypt_regru_api.py +++ b/letsencrypt_regru_api.py @@ -40,6 +40,17 @@ except ImportError: print("Выполните: pip install requests") sys.exit(1) +try: + from cryptography import x509 + from cryptography.x509.oid import NameOID, ExtensionOID + from cryptography.hazmat.primitives import hashes, serialization + from cryptography.hazmat.primitives.asymmetric import rsa + from cryptography.hazmat.backends import default_backend +except ImportError: + print("ОШИБКА: Необходимо установить модуль 'cryptography'") + print("Выполните: pip install cryptography") + sys.exit(1) + # ============================================================================== # КОНФИГУРАЦИЯ # ============================================================================== @@ -65,6 +76,15 @@ DEFAULT_CONFIG = { "dns_propagation_wait": 60, # Время ожидания распространения DNS (секунды) "dns_check_attempts": 10, # Количество попыток проверки DNS "dns_check_interval": 10, # Интервал между проверками DNS (секунды) + + # Параметры обновления сертификата + "renewal_days": 30, # За сколько дней до истечения обновлять (по умолчанию 30) + + # Настройки Nginx Proxy Manager + "npm_enabled": False, # Включить интеграцию с NPM + "npm_host": "http://192.168.10.14:81", # Адрес NPM + "npm_email": "admin@example.com", # Email для входа в NPM + "npm_password": "changeme", # Пароль NPM } # API endpoints для reg.ru @@ -278,6 +298,454 @@ class RegRuAPI: return False +# ============================================================================== +# КЛАСС ДЛЯ РАБОТЫ С NGINX PROXY MANAGER +# ============================================================================== + +class NginxProxyManagerAPI: + """Класс для работы с API Nginx Proxy Manager""" + + def __init__(self, host: str, email: str, password: str, logger: logging.Logger): + """ + Инициализация API клиента NPM + + Args: + host: URL адрес NPM (например, http://192.168.10.14:81) + email: Email для входа + password: Пароль + logger: Logger объект + """ + self.host = host.rstrip('/') + self.email = email + self.password = password + self.logger = logger + self.session = requests.Session() + self.token = None + + def login(self) -> bool: + """ + Авторизация в Nginx Proxy Manager + + Returns: + True если успешно + """ + url = f"{self.host}/api/tokens" + + payload = { + "identity": self.email, + "secret": self.password + } + + try: + self.logger.info("Авторизация в Nginx Proxy Manager...") + response = self.session.post(url, json=payload, timeout=10) + response.raise_for_status() + + data = response.json() + self.token = data.get("token") + + if self.token: + # Устанавливаем токен в заголовки для последующих запросов + self.session.headers.update({ + "Authorization": f"Bearer {self.token}" + }) + self.logger.info("Авторизация в NPM успешна") + return True + else: + self.logger.error("Токен не получен при авторизации") + return False + + except requests.exceptions.RequestException as e: + self.logger.error(f"Ошибка при авторизации в NPM: {e}") + return False + + def get_certificates(self) -> List[Dict]: + """ + Получение списка сертификатов + + Returns: + Список сертификатов + """ + url = f"{self.host}/api/nginx/certificates" + + try: + self.logger.debug("Получение списка сертификатов из NPM...") + response = self.session.get(url, timeout=10) + response.raise_for_status() + + certificates = response.json() + self.logger.debug(f"Получено {len(certificates)} сертификатов") + return certificates + + except requests.exceptions.RequestException as e: + self.logger.error(f"Ошибка при получении списка сертификатов: {e}") + return [] + + def find_certificate_by_domain(self, domain: str) -> Optional[Dict]: + """ + Поиск сертификата по домену + + Args: + domain: Доменное имя + + Returns: + Данные сертификата или None + """ + certificates = self.get_certificates() + + for cert in certificates: + domains = cert.get("domain_names", []) + if domain in domains or f"*.{domain}" in domains: + self.logger.debug(f"Найден существующий сертификат для {domain}") + return cert + + return None + + def upload_certificate(self, domain: str, cert_path: str, key_path: str, + chain_path: Optional[str] = None) -> Optional[Dict]: + """ + Загрузка нового сертификата в NPM + + Args: + domain: Основной домен + cert_path: Путь к файлу сертификата + key_path: Путь к приватному ключу + chain_path: Путь к цепочке сертификатов (опционально) + + Returns: + Данные созданного сертификата или None + """ + url = f"{self.host}/api/nginx/certificates" + + try: + # Читаем файлы сертификатов + with open(cert_path, 'r') as f: + certificate = f.read() + + with open(key_path, 'r') as f: + certificate_key = f.read() + + # Если есть цепочка, объединяем с сертификатом + if chain_path and os.path.exists(chain_path): + with open(chain_path, 'r') as f: + chain = f.read() + # NPM ожидает fullchain (cert + chain) + certificate = certificate + "\n" + chain + + # Формируем payload для API + payload = { + "provider": "other", + "nice_name": f"{domain} - Let's Encrypt", + "domain_names": [domain], + "certificate": certificate, + "certificate_key": certificate_key, + "meta": { + "letsencrypt_agree": True, + "dns_challenge": True, + "dns_provider": "reg_ru" + } + } + + self.logger.info(f"Загрузка сертификата для {domain} в NPM...") + response = self.session.post(url, json=payload, timeout=30) + response.raise_for_status() + + result = response.json() + cert_id = result.get("id") + + if cert_id: + self.logger.info(f"Сертификат успешно загружен в NPM (ID: {cert_id})") + return result + else: + self.logger.error("Не удалось получить ID созданного сертификата") + return None + + except FileNotFoundError as e: + self.logger.error(f"Файл сертификата не найден: {e}") + return None + except requests.exceptions.RequestException as e: + self.logger.error(f"Ошибка при загрузке сертификата в NPM: {e}") + if hasattr(e.response, 'text'): + self.logger.error(f"Ответ сервера: {e.response.text}") + return None + + def update_certificate(self, cert_id: int, cert_path: str, key_path: str, + chain_path: Optional[str] = None) -> bool: + """ + Обновление существующего сертификата + + Args: + cert_id: ID сертификата в NPM + cert_path: Путь к файлу сертификата + key_path: Путь к приватному ключу + chain_path: Путь к цепочке сертификатов (опционально) + + Returns: + True если успешно + """ + url = f"{self.host}/api/nginx/certificates/{cert_id}" + + try: + # Читаем файлы сертификатов + with open(cert_path, 'r') as f: + certificate = f.read() + + with open(key_path, 'r') as f: + certificate_key = f.read() + + # Если есть цепочка, объединяем с сертификатом + if chain_path and os.path.exists(chain_path): + with open(chain_path, 'r') as f: + chain = f.read() + certificate = certificate + "\n" + chain + + # Формируем payload для обновления + payload = { + "certificate": certificate, + "certificate_key": certificate_key + } + + self.logger.info(f"Обновление сертификата ID {cert_id} в NPM...") + response = self.session.put(url, json=payload, timeout=30) + response.raise_for_status() + + self.logger.info("Сертификат успешно обновлен в NPM") + return True + + except FileNotFoundError as e: + self.logger.error(f"Файл сертификата не найден: {e}") + return False + except requests.exceptions.RequestException as e: + self.logger.error(f"Ошибка при обновлении сертификата в NPM: {e}") + if hasattr(e.response, 'text'): + self.logger.error(f"Ответ сервера: {e.response.text}") + return False + + def sync_certificate(self, domain: str, cert_dir: str) -> bool: + """ + Синхронизация сертификата с NPM (создание или обновление) + + Args: + domain: Доменное имя + cert_dir: Директория с сертификатами Let's Encrypt + + Returns: + True если успешно + """ + # Пути к файлам сертификата + cert_path = os.path.join(cert_dir, "cert.pem") + key_path = os.path.join(cert_dir, "privkey.pem") + chain_path = os.path.join(cert_dir, "chain.pem") + fullchain_path = os.path.join(cert_dir, "fullchain.pem") + + # Проверяем наличие файлов + if not os.path.exists(cert_path) or not os.path.exists(key_path): + self.logger.error(f"Файлы сертификата не найдены в {cert_dir}") + return False + + # Авторизуемся в NPM + if not self.login(): + return False + + # Проверяем, существует ли уже сертификат для этого домена + existing_cert = self.find_certificate_by_domain(domain) + + # Используем fullchain если доступен, иначе cert + chain + if os.path.exists(fullchain_path): + final_cert_path = fullchain_path + final_chain_path = None + else: + final_cert_path = cert_path + final_chain_path = chain_path if os.path.exists(chain_path) else None + + if existing_cert: + # Обновляем существующий сертификат + cert_id = existing_cert.get("id") + self.logger.info(f"Обновление существующего сертификата (ID: {cert_id})") + return self.update_certificate(cert_id, final_cert_path, key_path, final_chain_path) + else: + # Создаем новый сертификат + self.logger.info("Создание нового сертификата в NPM") + result = self.upload_certificate(domain, final_cert_path, key_path, final_chain_path) + return result is not None + + +# ============================================================================== +# КЛАСС ДЛЯ ГЕНЕРАЦИИ ТЕСТОВЫХ СЕРТИФИКАТОВ +# ============================================================================== + +class TestCertificateGenerator: + """Класс для генерации самоподписанных тестовых SSL сертификатов""" + + def __init__(self, logger: logging.Logger): + """ + Инициализация генератора тестовых сертификатов + + Args: + logger: Logger объект + """ + self.logger = logger + + def generate_self_signed_certificate( + self, + domain: str, + wildcard: bool = False, + output_dir: str = "/etc/letsencrypt/live", + validity_days: int = 90 + ) -> bool: + """ + Генерация самоподписанного SSL сертификата для тестирования + + Args: + domain: Основной домен + wildcard: Создать wildcard сертификат + output_dir: Директория для сохранения сертификата + validity_days: Срок действия сертификата в днях (по умолчанию 90) + + Returns: + True если сертификат создан успешно, False в противном случае + """ + try: + self.logger.info("=" * 80) + self.logger.info("ГЕНЕРАЦИЯ ТЕСТОВОГО САМОПОДПИСАННОГО СЕРТИФИКАТА") + self.logger.info("=" * 80) + self.logger.info(f"Домен: {domain}") + self.logger.info(f"Wildcard: {wildcard}") + self.logger.info(f"Срок действия: {validity_days} дней") + self.logger.info("⚠️ ВНИМАНИЕ: Это тестовый сертификат, не для production!") + + # Создаем директорию для сертификата + cert_dir = os.path.join(output_dir, domain) + os.makedirs(cert_dir, exist_ok=True) + self.logger.info(f"Директория: {cert_dir}") + + # Генерируем приватный ключ + self.logger.info("Генерация приватного ключа RSA 2048 бит...") + private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=2048, + backend=default_backend() + ) + + # Сохраняем приватный ключ + key_path = os.path.join(cert_dir, "privkey.pem") + with open(key_path, "wb") as f: + f.write(private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption() + )) + os.chmod(key_path, 0o600) + self.logger.info(f"✓ Приватный ключ сохранен: {key_path}") + + # Подготовка данных для сертификата + subject = issuer = x509.Name([ + x509.NameAttribute(NameOID.COUNTRY_NAME, "RU"), + x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Moscow"), + x509.NameAttribute(NameOID.LOCALITY_NAME, "Moscow"), + x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Test Certificate"), + x509.NameAttribute(NameOID.COMMON_NAME, domain), + ]) + + # Создаем список альтернативных имен (SAN) + san_list = [x509.DNSName(domain)] + if wildcard: + san_list.append(x509.DNSName(f"*.{domain}")) + + # Генерируем сертификат + self.logger.info("Генерация самоподписанного сертификата...") + cert = ( + x509.CertificateBuilder() + .subject_name(subject) + .issuer_name(issuer) + .public_key(private_key.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before(datetime.utcnow()) + .not_valid_after(datetime.utcnow() + timedelta(days=validity_days)) + .add_extension( + x509.SubjectAlternativeName(san_list), + critical=False, + ) + .add_extension( + x509.BasicConstraints(ca=False, path_length=None), + critical=True, + ) + .add_extension( + x509.KeyUsage( + digital_signature=True, + key_encipherment=True, + content_commitment=False, + data_encipherment=False, + key_agreement=False, + key_cert_sign=False, + crl_sign=False, + encipher_only=False, + decipher_only=False, + ), + critical=True, + ) + .add_extension( + x509.ExtendedKeyUsage([ + x509.oid.ExtendedKeyUsageOID.SERVER_AUTH, + x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH, + ]), + critical=False, + ) + .sign(private_key, hashes.SHA256(), default_backend()) + ) + + # Сохраняем сертификат + cert_path = os.path.join(cert_dir, "cert.pem") + with open(cert_path, "wb") as f: + f.write(cert.public_bytes(serialization.Encoding.PEM)) + self.logger.info(f"✓ Сертификат сохранен: {cert_path}") + + # Создаем fullchain (для самоподписанного это просто копия cert) + fullchain_path = os.path.join(cert_dir, "fullchain.pem") + with open(fullchain_path, "wb") as f: + f.write(cert.public_bytes(serialization.Encoding.PEM)) + self.logger.info(f"✓ Fullchain сохранен: {fullchain_path}") + + # Создаем chain.pem (пустой для самоподписанного) + chain_path = os.path.join(cert_dir, "chain.pem") + with open(chain_path, "w") as f: + f.write("") + self.logger.info(f"✓ Chain файл создан: {chain_path}") + + # Выводим информацию о сертификате + self.logger.info("") + self.logger.info("=" * 80) + self.logger.info("ИНФОРМАЦИЯ О СЕРТИФИКАТЕ") + self.logger.info("=" * 80) + self.logger.info(f"Домен: {domain}") + if wildcard: + self.logger.info(f"Wildcard: *.{domain}") + self.logger.info(f"Действителен с: {cert.not_valid_before}") + self.logger.info(f"Действителен до: {cert.not_valid_after}") + self.logger.info(f"Серийный номер: {cert.serial_number}") + self.logger.info("") + self.logger.info("📁 Файлы сертификата:") + self.logger.info(f" • Приватный ключ: {key_path}") + self.logger.info(f" • Сертификат: {cert_path}") + self.logger.info(f" • Fullchain: {fullchain_path}") + self.logger.info(f" • Chain: {chain_path}") + self.logger.info("") + self.logger.info("⚠️ ВНИМАНИЕ:") + self.logger.info(" Это самоподписанный тестовый сертификат!") + self.logger.info(" Браузеры будут показывать предупреждение о безопасности.") + self.logger.info(" Используйте ТОЛЬКО для тестирования и разработки!") + self.logger.info(" Для production используйте настоящие Let's Encrypt сертификаты.") + self.logger.info("=" * 80) + + return True + + except Exception as e: + self.logger.error(f"Ошибка при генерации тестового сертификата: {e}") + import traceback + self.logger.error(traceback.format_exc()) + return False + + # ============================================================================== # КЛАСС ДЛЯ РАБОТЫ С CERTBOT # ============================================================================== @@ -570,6 +1038,26 @@ class LetsEncryptManager: except Exception as e: self.logger.error(f"Ошибка при чтении сертификата: {e}") + + def sync_with_npm(self, npm_api: NginxProxyManagerAPI) -> bool: + """ + Синхронизация сертификата с Nginx Proxy Manager + + Args: + npm_api: API клиент NPM + + Returns: + True если успешно + """ + self.logger.info("=== Синхронизация сертификата с Nginx Proxy Manager ===") + + # Проверяем наличие сертификата + if not os.path.exists(self.cert_dir): + self.logger.error(f"Директория сертификата не найдена: {self.cert_dir}") + return False + + # Синхронизируем сертификат + return npm_api.sync_certificate(self.domain, self.cert_dir) # ============================================================================== @@ -701,6 +1189,16 @@ def main(): help="Подробный вывод", action="store_true" ) + parser.add_argument( + "--auto", + help="Автоматический режим: проверка и обновление при необходимости", + action="store_true" + ) + parser.add_argument( + "--test-cert", + help="Создать самоподписанный тестовый сертификат (для разработки и тестирования)", + action="store_true" + ) args = parser.parse_args() @@ -715,6 +1213,70 @@ def main(): # Настройка логирования logger = setup_logging(config["log_file"], args.verbose) + # Генерация тестового сертификата + if args.test_cert: + logger.info("=" * 80) + logger.info("РЕЖИМ: Генерация тестового самоподписанного сертификата") + logger.info("=" * 80) + + test_gen = TestCertificateGenerator(logger) + success = test_gen.generate_self_signed_certificate( + domain=config["domain"], + wildcard=config.get("wildcard", False), + output_dir=config["cert_dir"], + validity_days=90 + ) + + if success: + # Опционально загружаем в NPM + if config.get("npm_enabled", False): + logger.info("") + logger.info("=" * 80) + logger.info("ЗАГРУЗКА ТЕСТОВОГО СЕРТИФИКАТА В NGINX PROXY MANAGER") + logger.info("=" * 80) + + npm_api = NginxProxyManagerAPI( + config["npm_host"], + config["npm_email"], + config["npm_password"], + logger + ) + + if npm_api.login(): + cert_dir = os.path.join(config["cert_dir"], config["domain"]) + cert_path = os.path.join(cert_dir, "fullchain.pem") + key_path = os.path.join(cert_dir, "privkey.pem") + + # Проверяем существующий сертификат + existing = npm_api.find_certificate_by_domain(config["domain"]) + + if existing: + # Обновляем существующий + cert_id = existing.get("id") + logger.info(f"Обновление существующего сертификата в NPM (ID: {cert_id})") + if npm_api.update_certificate(cert_id, cert_path, key_path): + logger.info("✅ Тестовый сертификат успешно обновлен в NPM") + else: + logger.warning("⚠️ Не удалось обновить сертификат в NPM") + else: + # Создаем новый + logger.info("Загрузка нового тестового сертификата в NPM") + if npm_api.upload_certificate(config["domain"], cert_path, key_path): + logger.info("✅ Тестовый сертификат успешно загружен в NPM") + else: + logger.warning("⚠️ Не удалось загрузить сертификат в NPM") + else: + logger.error("Не удалось подключиться к Nginx Proxy Manager") + + logger.info("") + logger.info("=" * 80) + logger.info("✅ ТЕСТОВЫЙ СЕРТИФИКАТ УСПЕШНО СОЗДАН") + logger.info("=" * 80) + return 0 + else: + logger.error("❌ Не удалось создать тестовый сертификат") + return 1 + # Обработка хуков для certbot if args.auth_hook: # Certbot передает домен и токен через переменные окружения @@ -781,6 +1343,20 @@ def main(): if success: manager.display_certificate_info() reload_webserver(logger) + + # Синхронизация с Nginx Proxy Manager + if config.get("npm_enabled", False): + npm_api = NginxProxyManagerAPI( + config["npm_host"], + config["npm_email"], + config["npm_password"], + logger + ) + if manager.sync_with_npm(npm_api): + logger.info("Сертификат успешно добавлен в Nginx Proxy Manager") + else: + logger.warning("Не удалось синхронизировать сертификат с NPM") + logger.info("Новый сертификат успешно создан") return 0 else: @@ -793,6 +1369,20 @@ def main(): if success: manager.display_certificate_info() reload_webserver(logger) + + # Синхронизация с Nginx Proxy Manager + if config.get("npm_enabled", False): + npm_api = NginxProxyManagerAPI( + config["npm_host"], + config["npm_email"], + config["npm_password"], + logger + ) + if manager.sync_with_npm(npm_api): + logger.info("Сертификат успешно обновлен в Nginx Proxy Manager") + else: + logger.warning("Не удалось синхронизировать сертификат с NPM") + logger.info("Сертификат успешно обновлен") return 0 else: @@ -801,26 +1391,89 @@ def main(): else: # Автоматический режим: проверка и обновление при необходимости + logger.info("=" * 60) + logger.info("АВТОМАТИЧЕСКАЯ ПРОВЕРКА И ОБНОВЛЕНИЕ СЕРТИФИКАТА") + logger.info("=" * 60) + + # Получаем порог для обновления из конфигурации + renewal_days = config.get("renewal_days", 30) + logger.info(f"Порог обновления: {renewal_days} дней до истечения") + + # Проверяем срок действия сертификата days_left = manager.check_certificate_expiry() if days_left is None: - # Сертификат не существует - logger.info("Сертификат не найден. Создание нового...") + # Сертификат не существует - создаем новый + logger.info("=" * 60) + logger.info("СТАТУС: Сертификат не найден") + logger.info("ДЕЙСТВИЕ: Создание нового сертификата") + logger.info("=" * 60) success = manager.obtain_certificate() - elif days_left < 30: - # Сертификат скоро истекает - logger.info(f"Сертификат истекает через {days_left} дней. Обновление...") + action = "создан" + elif days_left < renewal_days: + # Сертификат скоро истекает - обновляем + logger.info("=" * 60) + logger.info(f"СТАТУС: Сертификат истекает через {days_left} дней") + logger.info(f"ДЕЙСТВИЕ: Обновление сертификата (порог: {renewal_days} дней)") + logger.info("=" * 60) success = manager.renew_certificate() + action = "обновлен" else: - # Сертификат действителен - logger.info(f"Сертификат действителен ({days_left} дней). Обновление не требуется.") + # Сертификат действителен - ничего не делаем + logger.info("=" * 60) + logger.info(f"СТАТУС: Сертификат действителен ({days_left} дней)") + logger.info("ДЕЙСТВИЕ: Обновление не требуется") + logger.info("=" * 60) manager.display_certificate_info() + + # Проверяем синхронизацию с NPM даже если сертификат действителен + if config.get("npm_enabled", False): + logger.info("Проверка синхронизации с Nginx Proxy Manager...") + npm_api = NginxProxyManagerAPI( + config["npm_host"], + config["npm_email"], + config["npm_password"], + logger + ) + existing_cert = npm_api.login() and npm_api.find_certificate_by_domain(manager.domain) + if existing_cert: + logger.info(f"Сертификат найден в NPM (ID: {existing_cert.get('id')})") + else: + logger.info("Сертификат не найден в NPM. Синхронизация...") + if manager.sync_with_npm(npm_api): + logger.info("Сертификат успешно синхронизирован с NPM") + return 0 + # Если был создан или обновлен сертификат if success: + logger.info("=" * 60) + logger.info(f"РЕЗУЛЬТАТ: Сертификат успешно {action}") + logger.info("=" * 60) + manager.display_certificate_info() reload_webserver(logger) - logger.info("Операция завершена успешно") + + # Синхронизация с Nginx Proxy Manager + if config.get("npm_enabled", False): + logger.info("=" * 60) + logger.info("СИНХРОНИЗАЦИЯ С NGINX PROXY MANAGER") + logger.info("=" * 60) + + npm_api = NginxProxyManagerAPI( + config["npm_host"], + config["npm_email"], + config["npm_password"], + logger + ) + if manager.sync_with_npm(npm_api): + logger.info(f"✅ Сертификат успешно {action} в Nginx Proxy Manager") + else: + logger.warning("⚠️ Не удалось синхронизировать сертификат с NPM") + + logger.info("=" * 60) + logger.info("ОПЕРАЦИЯ ЗАВЕРШЕНА УСПЕШНО") + logger.info("=" * 60) return 0 else: logger.error("Операция завершилась с ошибкой") diff --git a/test_certificate.sh b/test_certificate.sh new file mode 100644 index 0000000..80430a4 --- /dev/null +++ b/test_certificate.sh @@ -0,0 +1,143 @@ +#!/bin/bash + +# ============================================================================== +# Скрипт для быстрого создания тестового самоподписанного SSL сертификата +# Использует: openssl (альтернатива Python скрипту) +# ============================================================================== + +set -e + +# Цвета +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Параметры по умолчанию +DOMAIN="${1:-example.com}" +WILDCARD="${2:-yes}" +CERT_DIR="/etc/letsencrypt/live/${DOMAIN}" +VALIDITY_DAYS=90 + +echo -e "${BLUE}╔════════════════════════════════════════════════════════════════╗${NC}" +echo -e "${BLUE}║ Создание тестового самоподписанного SSL сертификата ║${NC}" +echo -e "${BLUE}╚════════════════════════════════════════════════════════════════╝${NC}" +echo "" +echo -e "${YELLOW}Параметры:${NC}" +echo -e " Домен: ${GREEN}${DOMAIN}${NC}" +echo -e " Wildcard: ${GREEN}${WILDCARD}${NC}" +echo -e " Срок действия: ${GREEN}${VALIDITY_DAYS} дней${NC}" +echo -e " Директория: ${GREEN}${CERT_DIR}${NC}" +echo "" +echo -e "${YELLOW}⚠️ ВНИМАНИЕ: Это тестовый сертификат для разработки!${NC}" +echo -e "${YELLOW} Браузеры будут показывать предупреждение безопасности.${NC}" +echo "" + +# Проверка прав root +if [ "$(id -u)" != "0" ]; then + echo -e "${RED}✗ Требуются права root${NC}" + echo -e "Запустите: sudo $0 $@" + exit 1 +fi + +# Создание директории +echo -e "${YELLOW}→ Создание директории...${NC}" +mkdir -p "${CERT_DIR}" +cd "${CERT_DIR}" + +# Подготовка конфигурации для альтернативных имен (SAN) +if [ "${WILDCARD}" = "yes" ]; then + SAN="DNS:${DOMAIN},DNS:*.${DOMAIN}" +else + SAN="DNS:${DOMAIN}" +fi + +# Создание конфигурации OpenSSL +cat > openssl.cnf </dev/null +chmod 600 privkey.pem +echo -e "${GREEN}✓ Приватный ключ сохранен: ${CERT_DIR}/privkey.pem${NC}" + +# Генерация сертификата +echo -e "${YELLOW}→ Генерация самоподписанного сертификата...${NC}" +openssl req \ + -new \ + -x509 \ + -key privkey.pem \ + -out cert.pem \ + -days ${VALIDITY_DAYS} \ + -config openssl.cnf \ + -extensions v3_req \ + 2>/dev/null + +echo -e "${GREEN}✓ Сертификат сохранен: ${CERT_DIR}/cert.pem${NC}" + +# Создание fullchain (копия cert для самоподписанного) +cp cert.pem fullchain.pem +echo -e "${GREEN}✓ Fullchain сохранен: ${CERT_DIR}/fullchain.pem${NC}" + +# Создание пустого chain.pem +touch chain.pem +echo -e "${GREEN}✓ Chain файл создан: ${CERT_DIR}/chain.pem${NC}" + +# Удаление временного файла конфигурации +rm -f openssl.cnf + +echo "" +echo -e "${BLUE}╔════════════════════════════════════════════════════════════════╗${NC}" +echo -e "${BLUE}║ Информация о сертификате ║${NC}" +echo -e "${BLUE}╚════════════════════════════════════════════════════════════════╝${NC}" + +# Вывод информации о сертификате +openssl x509 -in cert.pem -noout -subject -dates -ext subjectAltName + +echo "" +echo -e "${GREEN}╔════════════════════════════════════════════════════════════════╗${NC}" +echo -e "${GREEN}║ ✓ Тестовый сертификат успешно создан ║${NC}" +echo -e "${GREEN}╚════════════════════════════════════════════════════════════════╝${NC}" +echo "" +echo -e "${YELLOW}📁 Файлы сертификата:${NC}" +echo -e " • Приватный ключ: ${CERT_DIR}/privkey.pem" +echo -e " • Сертификат: ${CERT_DIR}/cert.pem" +echo -e " • Fullchain: ${CERT_DIR}/fullchain.pem" +echo -e " • Chain: ${CERT_DIR}/chain.pem" +echo "" +echo -e "${YELLOW}⚠️ ВНИМАНИЕ:${NC}" +echo -e " Это самоподписанный тестовый сертификат!" +echo -e " Браузеры будут показывать предупреждение о безопасности." +echo -e " Используйте ТОЛЬКО для тестирования и разработки!" +echo "" +echo -e "${YELLOW}Использование в Nginx:${NC}" +echo -e " ssl_certificate ${CERT_DIR}/fullchain.pem;" +echo -e " ssl_certificate_key ${CERT_DIR}/privkey.pem;" +echo "" + +# Предложение загрузить в NPM +echo -e "${YELLOW}Загрузить в Nginx Proxy Manager?${NC}" +echo -e " Используйте Python скрипт с опцией --test-cert" +echo -e " или загрузите вручную через веб-интерфейс NPM" +echo ""