From c5bcd7e8b726d2c4a26392a06a3aeb305faa9365 Mon Sep 17 00:00:00 2001 From: Dmitriy Fofanov Date: Thu, 30 Oct 2025 09:47:23 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA?= =?UTF-8?q?=D0=B0=20=D1=82=D0=B5=D1=81=D1=82=D0=BE=D0=B2=D0=BE=D0=B3=D0=BE?= =?UTF-8?q?=20=D1=81=D0=B5=D1=80=D1=82=D0=B8=D1=84=D0=B8=D0=BA=D0=B0=D1=82?= =?UTF-8?q?=D0=B0=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20=D0=BA=D0=BE=D0=BC?= =?UTF-8?q?=D0=B0=D0=BD=D0=B4=D1=83=20--staging.=20=D0=A3=D0=BB=D1=83?= =?UTF-8?q?=D1=87=D1=88=D0=B5=D0=BD=D0=BE=20=D0=BB=D0=BE=D0=B3=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=BE=D1=86?= =?UTF-8?q?=D0=B5=D1=81=D1=81=D0=B0=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D1=81=D0=B5=D1=80=D1=82=D0=B8=D1=84=D0=B8?= =?UTF-8?q?=D0=BA=D0=B0=D1=82=D0=B0=20=D0=B8=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B5?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D1=8B=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D1=82=D0=B5=D1=81=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=B2=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D0=B8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 76 +++++++- letsencrypt_regru.sh | 61 +++++-- letsencrypt_regru_api.py | 383 +++++++++++++++++++++++++++++---------- 3 files changed, 409 insertions(+), 111 deletions(-) diff --git a/README.md b/README.md index bfe8612..4c4e5a9 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,14 @@ letsencrypt-regru --auto #### 🧪 Команды диагностики и тестирования ```bash +# Получить тестовый сертификат Let's Encrypt (staging) +# - Полностью идентичный процесс с production +# - БЕЗ лимитов на количество запросов (неограниченно!) +# - Идеально для тестирования автоматизации и DNS +# - Браузеры не доверяют (staging CA) +# - НЕ загружается в Nginx Proxy Manager +letsencrypt-regru --staging + # Проверить доступ к API reg.ru # - Тестирует подключение к API # - Показывает текущий IP адрес @@ -138,6 +146,7 @@ letsencrypt-regru --help # Включить подробный вывод (verbose mode) letsencrypt-regru --obtain -v letsencrypt-regru --check -v +letsencrypt-regru --staging -v ``` #### ⚙️ Служебные команды (внутреннее использование) @@ -1686,6 +1695,53 @@ letsencrypt-regru --auto --- +### Команда `--staging` + +**Назначение**: Получает тестовый сертификат из staging окружения Let's Encrypt. + +**Что делает:** +1. Запускает полный процесс получения сертификата Let's Encrypt +2. Использует staging CA (Certificate Authority) вместо production +3. Создает TXT запись через API reg.ru (как и в production) +4. Ожидает распространения DNS (60 секунд) +5. Let's Encrypt staging проверяет DNS запись +6. Получает сертификат от staging CA +7. НЕ загружает сертификат в Nginx Proxy Manager + +**Пример использования:** +```bash +letsencrypt-regru --staging +letsencrypt-regru --staging -v # с подробным выводом +``` + +**Преимущества:** +- ✅ **НЕТ ЛИМИТОВ** - неограниченное количество запросов (в отличие от production) +- ✅ **Полная идентичность** - процесс на 100% идентичен production +- ✅ **Реальный DNS** - тестирует весь процесс DNS-валидации +- ✅ **Безопасное тестирование** - не расходует лимиты production (5/неделя) +- ✅ **Быстрая отладка** - можно запускать сколько угодно раз + +**⚠️ Ограничения:** +- Браузеры не доверяют staging CA (покажут предупреждение) +- Сертификат нельзя использовать на production сайтах +- Подходит только для тестирования автоматизации + +**Когда использовать:** +- ✅ Первая настройка системы +- ✅ Тестирование DNS интеграции +- ✅ Отладка автоматизации +- ✅ Проверка работы hooks +- ✅ Разработка и staging окружения + +**Переход на production:** +После успешного тестирования просто запустите: +```bash +sudo letsencrypt-regru --obtain +``` +Staging сертификат будет заменен на production. + +--- + ### Команда `--test-cert` **Назначение**: Создает тестовый самоподписанный сертификат. @@ -1703,14 +1759,28 @@ letsencrypt-regru --test-cert **Преимущества:** - ✅ Не требует интернет соединения -- ✅ Мгновенное создание -- ✅ Нет лимитов Let's Encrypt (5 сертификатов/неделя) -- ✅ Идеально для разработки и тестирования +- ✅ Мгновенное создание (1-2 секунды) +- ✅ Не требует API reg.ru +- ✅ Не требует DNS +- ✅ Идеально для локальной разработки **⚠️ Внимание:** - Браузеры покажут предупреждение "сертификат не доверенный" +- Не тестирует DNS и автоматизацию - Не использовать в production! +**Сравнение: --staging vs --test-cert** + +| Параметр | --staging | --test-cert | +|----------|-----------|-------------| +| Интернет | ✅ Требуется | ❌ Не требуется | +| API reg.ru | ✅ Требуется | ❌ Не требуется | +| DNS проверка | ✅ Полная | ❌ Нет | +| Лимиты | ✅ Нет | ✅ Нет | +| Время создания | ~2-3 минуты | ~1-2 секунды | +| Тестирует автоматизацию | ✅ Да | ❌ Нет | +| Использование | Тест перед production | Локальная разработка | + --- ### Команда `--test-api` diff --git a/letsencrypt_regru.sh b/letsencrypt_regru.sh index d4b32d1..47a1c2a 100644 --- a/letsencrypt_regru.sh +++ b/letsencrypt_regru.sh @@ -457,40 +457,75 @@ display_summary() { echo " • Логи: ${LOG_DIR}" echo " • Сертификаты: ${CERT_DIR}" echo "" - echo "🔧 Доступные команды:" + echo "🔧 Основные команды:" echo " • letsencrypt-regru --check # Проверить срок действия сертификата" - echo " • letsencrypt-regru --obtain # Получить новый сертификат" + echo " • letsencrypt-regru --obtain # Получить новый production сертификат" echo " • letsencrypt-regru --renew # Обновить существующий сертификат" - echo " • letsencrypt-regru --test-cert # Создать тестовый самоподписанный сертификат" echo " • letsencrypt-regru --auto # Автоматическая проверка и обновление" + echo "" + echo "🧪 Команды тестирования:" + echo " • letsencrypt-regru --staging # Тестовый Let's Encrypt (БЕЗ лимитов!)" + echo " • letsencrypt-regru --test-cert # Самоподписанный (локальная разработка)" echo " • letsencrypt-regru --test-api # Проверить доступ к API reg.ru" - echo " • letsencrypt-regru --test-dns # Тестовое создание DNS записи TXT" - echo " • letsencrypt-regru --auth-hook # Certbot auth hook (внутреннее)" - echo " • letsencrypt-regru --cleanup-hook # Certbot cleanup hook (внутреннее)" - echo " • letsencrypt-regru --help # Показать справку" + echo " • letsencrypt-regru --test-dns # Протестировать DNS записи" + echo "" + echo "📋 Дополнительные команды:" + echo " • letsencrypt-regru --help # Показать полную справку" + echo " • letsencrypt-regru --obtain -v # Подробный вывод (verbose)" + echo "" + echo "💡 Рекомендуемый workflow:" + echo " 1. letsencrypt-regru --test-api # Проверить API" + echo " 2. letsencrypt-regru --test-dns # Проверить DNS" + echo " 3. letsencrypt-regru --staging # Тестовый сертификат (сколько угодно раз)" + echo " 4. letsencrypt-regru --obtain # Production сертификат" echo "" echo "⏰ Автоматическое обновление:" echo " • Сервис запускается каждые 12 часов" echo " • Управление: systemctl status letsencrypt-regru.timer" echo "" - echo "📊 Просмотр логов:" - echo " • journalctl -u letsencrypt-regru -f" - echo " • tail -f ${LOG_DIR}/letsencrypt_regru.log" + echo "� Просмотр логов:" + echo " • journalctl -u letsencrypt-regru -f # Системные логи (реальное время)" + echo " • tail -f ${LOG_DIR}/letsencrypt_regru.log # Файл логов" echo "" - echo "📖 Документация:" + echo "�📖 Документация:" echo " • README: ${APP_DIR}/README.md" - echo " • Docs: ${APP_DIR}/docs/" + echo " • GitHub: https://github.com/DFofanov/configure_nginx_manager" + echo "" + + echo "🔍 Сравнение режимов тестирования:" + echo "" + echo " --staging (рекомендуется для тестирования):" + echo " ✅ Полный процесс Let's Encrypt" + echo " ✅ Тестирует DNS и автоматизацию" + echo " ✅ БЕЗ лимитов (неограниченно)" + echo " ⚠️ Браузеры не доверяют (staging CA)" + echo " ⏱ ~2-3 минуты" + echo "" + echo " --test-cert (для локальной разработки):" + echo " ✅ Мгновенное создание (~1 сек)" + echo " ✅ Работает без интернета" + echo " ❌ НЕ тестирует DNS/автоматизацию" + echo " ⚠️ Браузеры не доверяют (самоподпись)" + echo "" + echo " --test-dns (проверка DNS):" + echo " ✅ Тестирует только DNS" + echo " ✅ Не создает сертификат" + echo " ⏱ ~1-2 минуты" echo "" if grep -q '"npm_enabled": true' "${CONFIG_DIR}/config.json" 2>/dev/null; then echo "🔗 Интеграция с Nginx Proxy Manager: ВКЛЮЧЕНА" - echo " Сертификаты будут автоматически синхронизироваться с NPM" + echo " Production сертификаты будут автоматически синхронизироваться с NPM" + echo " (Staging и test-cert сертификаты НЕ загружаются в NPM)" echo "" fi msg_warn "ВАЖНО: Отредактируйте конфигурацию при необходимости:" echo " nano ${CONFIG_DIR}/config.json" echo "" + echo "🎉 Готово к использованию! Начните с команды:" + echo " letsencrypt-regru --test-api" + echo "" } # Функция обновления diff --git a/letsencrypt_regru_api.py b/letsencrypt_regru_api.py index b0fb995..59d0aa2 100644 --- a/letsencrypt_regru_api.py +++ b/letsencrypt_regru_api.py @@ -974,35 +974,50 @@ class LetsEncryptManager: Returns: True если успешно """ - self.logger.info("=== DNS Challenge: Добавление TXT записи ===") - - # Извлекаем основной домен из validation_domain - # Убираем wildcard если есть - base_domain = validation_domain.replace("*.", "") - - # Для DNS-01 challenge всегда используем _acme-challenge - subdomain = "_acme-challenge" - - self.logger.info(f"Домен: {base_domain}, Поддомен: {subdomain}") - - # Добавляем TXT запись - success = self.api.add_txt_record(base_domain, subdomain, validation_token) - - if success: + try: + self.logger.info("=== DNS Challenge: Добавление TXT записи ===") + + # Извлекаем основной домен из validation_domain + # Убираем wildcard если есть + base_domain = validation_domain.replace("*.", "") + + # Для DNS-01 challenge всегда используем _acme-challenge + subdomain = "_acme-challenge" + + self.logger.info(f"Validation Domain: {validation_domain}") + self.logger.info(f"Base Domain: {base_domain}") + self.logger.info(f"Subdomain: {subdomain}") + self.logger.info(f"Token: {validation_token[:20]}...") + + # Добавляем TXT запись + self.logger.info("Добавление TXT записи через API reg.ru...") + success = self.api.add_txt_record(base_domain, subdomain, validation_token) + + if not success: + self.logger.error("Не удалось добавить TXT запись") + return False + + self.logger.info("✅ TXT запись успешно добавлена") + # Ждем распространения DNS wait_time = self.config.get("dns_propagation_wait", 60) self.logger.info(f"Ожидание распространения DNS ({wait_time} секунд)...") time.sleep(wait_time) # Проверяем DNS запись (используем base_domain для проверки) + self.logger.info("Проверка распространения DNS...") if self.verify_dns_record_external(base_domain, subdomain, validation_token): - self.logger.info("DNS валидация готова") + self.logger.info("✅ DNS запись подтверждена через публичные DNS") return True else: - self.logger.warning("DNS запись не распространилась вовремя, но продолжаем...") + self.logger.warning("⚠️ DNS запись не обнаружена через публичные DNS, но продолжаем...") + self.logger.warning("Let's Encrypt может использовать свои DNS серверы") return True - - return False + + except Exception as e: + self.logger.error(f"💥 Ошибка в dns_challenge_hook: {e}") + self.logger.exception("Traceback:") + return False def dns_cleanup_hook(self, validation_domain: str, validation_token: str) -> bool: """ @@ -1081,14 +1096,24 @@ class LetsEncryptManager: """ return self.verify_dns_record_external(self.domain, subdomain, expected_value) - def obtain_certificate(self) -> bool: + def obtain_certificate(self, staging: bool = False) -> bool: """ Получение нового сертификата + Args: + staging: Использовать staging окружение Let's Encrypt (для тестирования) + Returns: True если успешно """ - self.logger.info("=== Запрос нового SSL сертификата ===") + if staging: + self.logger.info("=== Запрос ТЕСТОВОГО SSL сертификата (Let's Encrypt Staging) ===") + self.logger.warning("⚠️ ВНИМАНИЕ: Это тестовый сертификат из staging окружения!") + self.logger.warning("⚠️ Браузеры не будут доверять этому сертификату") + self.logger.warning("⚠️ Используйте для тестирования DNS и автоматизации") + self.logger.warning("⚠️ Staging НЕ имеет лимитов запросов (в отличие от production)") + else: + self.logger.info("=== Запрос нового SSL сертификата ===") # Формируем список доменов domains = [self.domain] @@ -1102,17 +1127,28 @@ class LetsEncryptManager: # Создаём временные wrapper скрипты для hooks import tempfile + # Получаем путь к конфигурации из аргументов командной строки + config_path = None + for i, arg in enumerate(sys.argv): + if arg in ['-c', '--config'] and i + 1 < len(sys.argv): + config_path = os.path.abspath(sys.argv[i + 1]) + break + + if not config_path: + self.logger.error("Не указан путь к конфигурации. Используйте --config /path/to/config.json") + return False + # Auth hook wrapper auth_hook_script = tempfile.NamedTemporaryFile(mode='w', suffix='.sh', delete=False) auth_hook_script.write('#!/bin/bash\n') - auth_hook_script.write(f'{sys.executable} {os.path.abspath(__file__)} --auth-hook\n') + auth_hook_script.write(f'{sys.executable} {os.path.abspath(__file__)} --config {config_path} --auth-hook\n') auth_hook_script.close() os.chmod(auth_hook_script.name, 0o755) # Cleanup hook wrapper cleanup_hook_script = tempfile.NamedTemporaryFile(mode='w', suffix='.sh', delete=False) cleanup_hook_script.write('#!/bin/bash\n') - cleanup_hook_script.write(f'{sys.executable} {os.path.abspath(__file__)} --cleanup-hook\n') + cleanup_hook_script.write(f'{sys.executable} {os.path.abspath(__file__)} --config {config_path} --cleanup-hook\n') cleanup_hook_script.close() os.chmod(cleanup_hook_script.name, 0o755) @@ -1127,16 +1163,28 @@ class LetsEncryptManager: "--agree-tos", "--non-interactive", "--expand", - ] + domain_args + ] + + # Добавляем --staging для тестового окружения + if staging: + cmd.append("--staging") + cmd.append("--break-my-certs") # Разрешает перезапись production сертификатов staging версиями + + cmd.extend(domain_args) self.logger.info("=" * 80) - self.logger.info("ЗАПУСК CERTBOT") + if staging: + self.logger.info("ЗАПУСК CERTBOT (STAGING MODE)") + else: + self.logger.info("ЗАПУСК CERTBOT") self.logger.info("=" * 80) + self.logger.info(f"Режим: {'STAGING (тестовый)' if staging else 'PRODUCTION (боевой)'}") self.logger.info(f"Команда: {' '.join(cmd)}") self.logger.info(f"Python: {sys.executable}") self.logger.info(f"Скрипт: {os.path.abspath(__file__)}") - self.logger.info(f"Auth hook: {sys.executable} {os.path.abspath(__file__)} --auth-hook") - self.logger.info(f"Cleanup hook: {sys.executable} {os.path.abspath(__file__)} --cleanup-hook") + self.logger.info(f"Конфигурация: {config_path}") + self.logger.info(f"Auth hook: {sys.executable} {os.path.abspath(__file__)} --config {config_path} --auth-hook") + self.logger.info(f"Cleanup hook: {sys.executable} {os.path.abspath(__file__)} --config {config_path} --cleanup-hook") self.logger.info("=" * 80) try: @@ -1341,7 +1389,59 @@ def main(): # Парсинг аргументов командной строки parser = argparse.ArgumentParser( - description="Автоматическое управление SSL сертификатами Let's Encrypt через API reg.ru" + description="Автоматическое управление SSL сертификатами Let's Encrypt через API reg.ru", + epilog=""" +════════════════════════════════════════════════════════════════════════════════ +ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ +════════════════════════════════════════════════════════════════════════════════ + +Основные команды: + %(prog)s -c config.json --check Проверить срок действия + %(prog)s -c config.json --obtain Получить production сертификат + %(prog)s -c config.json --renew Обновить сертификат + %(prog)s -c config.json --auto Авто-режим (для cron/systemd) + +Команды тестирования: + %(prog)s -c config.json --staging Тестовый Let's Encrypt (БЕЗ лимитов!) + %(prog)s -c config.json --test-cert Самоподписанный (локально) + %(prog)s -c config.json --test-api Проверить API reg.ru + %(prog)s -c config.json --test-dns Проверить DNS записи + +Отладка: + %(prog)s -c config.json --obtain -v Подробный вывод + +════════════════════════════════════════════════════════════════════════════════ +РЕКОМЕНДУЕМЫЙ WORKFLOW +════════════════════════════════════════════════════════════════════════════════ + +1. Проверка настройки: + %(prog)s -c config.json --test-api ✓ API доступен? + %(prog)s -c config.json --test-dns ✓ DNS работает? + +2. Тестирование (неограниченно): + %(prog)s -c config.json --staging ✓ Полный процесс SSL + +3. Production: + %(prog)s -c config.json --obtain ✓ Боевой сертификат + +════════════════════════════════════════════════════════════════════════════════ +СРАВНЕНИЕ РЕЖИМОВ ТЕСТИРОВАНИЯ +════════════════════════════════════════════════════════════════════════════════ + + --staging Полный Let's Encrypt, БЕЗ лимитов, ~2-3 мин, тестирует всё + --test-cert Самоподпись, мгновенно, БЕЗ интернета, для локальной разработки + --test-dns Только DNS, ~1-2 мин, не создает сертификат + +════════════════════════════════════════════════════════════════════════════════ +ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ +════════════════════════════════════════════════════════════════════════════════ + +Документация: https://github.com/DFofanov/configure_nginx_manager +Поддержка: https://github.com/DFofanov/configure_nginx_manager/issues +Лимиты LE: 5 сертификатов/неделю на домен (production only, staging БЕЗ лимитов) + + """, + formatter_class=argparse.RawDescriptionHelpFormatter ) parser.add_argument( "-c", "--config", @@ -1353,54 +1453,69 @@ def main(): help="Создать пример файла конфигурации", metavar="FILE" ) - parser.add_argument( - "--obtain", - help="Получить новый сертификат", - action="store_true" - ) - parser.add_argument( - "--renew", - help="Обновить существующий сертификат", - action="store_true" - ) - parser.add_argument( + # Основные команды + main_group = parser.add_argument_group('Основные команды') + main_group.add_argument( "--check", help="Проверить срок действия сертификата", action="store_true" ) - parser.add_argument( + main_group.add_argument( + "--obtain", + help="Получить новый production сертификат Let's Encrypt", + action="store_true" + ) + main_group.add_argument( + "--renew", + help="Обновить существующий сертификат", + action="store_true" + ) + main_group.add_argument( + "--auto", + help="Автоматический режим: проверка и обновление при необходимости (для cron/systemd)", + action="store_true" + ) + + # Команды тестирования + test_group = parser.add_argument_group('Команды тестирования') + test_group.add_argument( + "--staging", + help="Получить тестовый сертификат Let's Encrypt (staging CA, БЕЗ лимитов)", + action="store_true" + ) + test_group.add_argument( + "--test-cert", + help="Создать самоподписанный сертификат (локальная разработка, БЕЗ интернета)", + action="store_true" + ) + test_group.add_argument( + "--test-api", + help="Проверить доступ к API reg.ru (показывает IP, баланс)", + action="store_true" + ) + test_group.add_argument( + "--test-dns", + help="Протестировать создание/удаление DNS записи (полная симуляция SSL процесса)", + action="store_true" + ) + + # Служебные команды + service_group = parser.add_argument_group('Служебные команды (внутреннее использование)') + service_group.add_argument( "--auth-hook", - help="Внутренний хук для DNS аутентификации (используется certbot)", + help="Certbot authentication hook (создание DNS записи)", action="store_true" ) - parser.add_argument( + service_group.add_argument( "--cleanup-hook", - help="Внутренний хук для очистки DNS (используется certbot)", + help="Certbot cleanup hook (удаление DNS записи)", action="store_true" ) + + # Дополнительные параметры parser.add_argument( "-v", "--verbose", - help="Подробный вывод", - action="store_true" - ) - parser.add_argument( - "--auto", - help="Автоматический режим: проверка и обновление при необходимости", - action="store_true" - ) - parser.add_argument( - "--test-cert", - help="Создать самоподписанный тестовый сертификат (для разработки и тестирования)", - action="store_true" - ) - parser.add_argument( - "--test-api", - help="Протестировать подключение к API reg.ru", - action="store_true" - ) - parser.add_argument( - "--test-dns", - help="Протестировать создание и удаление DNS записи (полный цикл как при SSL)", + help="Подробный вывод для диагностики", action="store_true" ) @@ -1627,45 +1742,77 @@ def main(): # Обработка хуков для certbot if args.auth_hook: - logger.info("=" * 80) - logger.info("🔑 AUTH HOOK ВЫЗВАН") - logger.info("=" * 80) - - # Certbot передает домен и токен через переменные окружения - domain = os.environ.get("CERTBOT_DOMAIN") - token = os.environ.get("CERTBOT_VALIDATION") - - logger.info(f"CERTBOT_DOMAIN: {domain}") - logger.info(f"CERTBOT_VALIDATION: {token[:20]}..." if token else "CERTBOT_VALIDATION: None") - - if domain and token: + try: + logger.info("=" * 80) + logger.info("🔑 AUTH HOOK ВЫЗВАН") + logger.info("=" * 80) + + # Certbot передает домен и токен через переменные окружения + domain = os.environ.get("CERTBOT_DOMAIN") + token = os.environ.get("CERTBOT_VALIDATION") + + logger.info(f"CERTBOT_DOMAIN: {domain}") + logger.info(f"CERTBOT_VALIDATION: {token[:20]}..." if token else "CERTBOT_VALIDATION: None") + + if not domain or not token: + logger.error("CERTBOT_DOMAIN или CERTBOT_VALIDATION не установлены") + logger.error("Переменные окружения:") + for key in os.environ: + if key.startswith("CERTBOT_"): + logger.error(f" {key}: {os.environ[key]}") + return 1 + api = RegRuAPI(config["regru_username"], config["regru_password"], logger) manager = LetsEncryptManager(config, api, logger) success = manager.dns_challenge_hook(domain, token) - return 0 if success else 1 - else: - logger.error("CERTBOT_DOMAIN или CERTBOT_VALIDATION не установлены") + + if success: + logger.info("✅ AUTH HOOK ЗАВЕРШЕН УСПЕШНО") + return 0 + else: + logger.error("❌ AUTH HOOK ЗАВЕРШИЛСЯ С ОШИБКОЙ") + return 1 + + except Exception as e: + logger.error(f"💥 КРИТИЧЕСКАЯ ОШИБКА В AUTH HOOK: {e}") + logger.exception("Traceback:") return 1 if args.cleanup_hook: - logger.info("=" * 80) - logger.info("🧹 CLEANUP HOOK ВЫЗВАН") - logger.info("=" * 80) - - domain = os.environ.get("CERTBOT_DOMAIN") - token = os.environ.get("CERTBOT_VALIDATION") - - logger.info(f"CERTBOT_DOMAIN: {domain}") - logger.info(f"CERTBOT_VALIDATION: {token[:20]}..." if token else "CERTBOT_VALIDATION: None") - - if domain and token: + try: + logger.info("=" * 80) + logger.info("🧹 CLEANUP HOOK ВЫЗВАН") + logger.info("=" * 80) + + domain = os.environ.get("CERTBOT_DOMAIN") + token = os.environ.get("CERTBOT_VALIDATION") + + logger.info(f"CERTBOT_DOMAIN: {domain}") + logger.info(f"CERTBOT_VALIDATION: {token[:20]}..." if token else "CERTBOT_VALIDATION: None") + + if not domain or not token: + logger.error("CERTBOT_DOMAIN или CERTBOT_VALIDATION не установлены") + logger.error("Переменные окружения:") + for key in os.environ: + if key.startswith("CERTBOT_"): + logger.error(f" {key}: {os.environ[key]}") + return 1 + api = RegRuAPI(config["regru_username"], config["regru_password"], logger) manager = LetsEncryptManager(config, api, logger) success = manager.dns_cleanup_hook(domain, token) - return 0 if success else 1 - else: - logger.error("CERTBOT_DOMAIN или CERTBOT_VALIDATION не установлены") - return 1 + + if success: + logger.info("✅ CLEANUP HOOK ЗАВЕРШЕН УСПЕШНО") + return 0 + else: + logger.warning("⚠️ CLEANUP HOOK ЗАВЕРШИЛСЯ С ПРЕДУПРЕЖДЕНИЕМ (не критично)") + return 0 # Cleanup hook не должен блокировать получение сертификата + + except Exception as e: + logger.error(f"💥 ОШИБКА В CLEANUP HOOK: {e}") + logger.exception("Traceback:") + return 0 # Cleanup hook не должен блокировать получение сертификата # Проверка прав root if os.geteuid() != 0: @@ -1710,9 +1857,55 @@ def main(): logger.info(f"Сертификат действителен ({days_left} дней)") return 0 + elif args.staging: + # Получение ТЕСТОВОГО сертификата из staging окружения + logger.info("") + logger.info("🧪" * 40) + logger.info("РЕЖИМ STAGING: Тестовый сертификат Let's Encrypt") + logger.info("🧪" * 40) + logger.info("") + logger.info("📋 ИНФОРМАЦИЯ О STAGING РЕЖИМЕ:") + logger.info(" • Сертификат будет выдан staging CA (не доверенный)") + logger.info(" • Браузеры покажут предупреждение о безопасности") + logger.info(" • НЕТ лимитов на количество запросов (в отличие от production)") + logger.info(" • Идеально для тестирования автоматизации и DNS") + logger.info(" • Полностью идентичный процесс с production") + logger.info("") + logger.info("⚠️ НЕ используйте staging сертификаты на production сайтах!") + logger.info("") + + success = manager.obtain_certificate(staging=True) + + if success: + logger.info("") + logger.info("=" * 80) + logger.info("✅ ТЕСТОВЫЙ СЕРТИФИКАТ УСПЕШНО ПОЛУЧЕН") + logger.info("=" * 80) + logger.info("") + logger.info("📂 Расположение: /etc/letsencrypt/live/%s/" % config['domain']) + logger.info("") + logger.info("🔄 Следующие шаги:") + logger.info(" 1. ✅ Проверьте что процесс прошел успешно") + logger.info(" 2. ✅ Убедитесь что DNS записи создаются корректно") + logger.info(" 3. ✅ Проверьте автоматизацию") + logger.info(" 4. 🚀 Когда всё готово - получите production сертификат:") + logger.info(" sudo letsencrypt-regru --obtain") + logger.info("") + logger.info("💡 ВАЖНО: Staging сертификаты хранятся в той же директории,") + logger.info(" что и production. Для получения production сертификата") + logger.info(" просто запустите команду --obtain") + logger.info("") + + # Синхронизация с NPM (если включено) + if config.get("npm_enabled", False): + logger.warning("⚠️ Staging сертификат НЕ загружается в Nginx Proxy Manager") + logger.warning(" (staging сертификаты не предназначены для production)") + + return 0 if success else 1 + elif args.obtain: # Принудительное получение нового сертификата - success = manager.obtain_certificate() + success = manager.obtain_certificate(staging=False) if success: manager.display_certificate_info() reload_webserver(logger)