Добавлен скрипт для автоматизации получения и обновления SSL сертификатов Let's Encrypt с использованием API reg.ru. Реализованы функции для работы с DNS-валидацией, логирования, проверки сертификатов и управления ими. Также создан bash-скрипт для упрощения процесса получения wildcard сертификата и обновления существующих сертификатов. Добавлена документация по настройке Nginx Proxy Manager с использованием полученного сертификата.
This commit is contained in:
56
Add Let's Encrypt Certificate для провайдера reg.ru.md
Normal file
56
Add Let's Encrypt Certificate для провайдера reg.ru.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Инструкция по созданию Let's Encrypt сертификата с DNS Challenge для провайдера reg.ru в Nginx Proxy Manager
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Предпосылки
|
||||||
|
- Доступ к Nginx Proxy Manager (NPM)
|
||||||
|
- Доступ к аккаунту reg.ru с правами управления DNS-записями
|
||||||
|
- API-ключ для управления DNS в reg.ru (если есть автоматическая интеграция)
|
||||||
|
- Нужно получить сертификат для `*.dfv24.com` (wildcard сертификат)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 1. Получение API-ключа для reg.ru
|
||||||
|
|
||||||
|
1. Войдите в панель управления reg.ru
|
||||||
|
2. Перейдите в раздел управления API (если поддерживается)
|
||||||
|
3. Создайте или найдите API-ключ с правом редактирования DNS записей
|
||||||
|
4. Сохраните API-ключ и секрет (Client ID и API Token)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 2. Настройка Nginx Proxy Manager для использования DNS Challenge reg.ru
|
||||||
|
|
||||||
|
1. В админке NPM перейдите в **SSL Certificates → Add SSL Certificate**
|
||||||
|
2. Выберите **Let's Encrypt** -> **DNS Challenge**
|
||||||
|
3. В поле **Provider** выберите `reg_ru` или `custom` (если провайдера нет, потребуется писать скрипт)
|
||||||
|
4. В поля API впишите необходимые параметры:
|
||||||
|
- Client ID
|
||||||
|
- API Token
|
||||||
|
5. В поле **Domain Names** укажите:
|
||||||
|
`*.dfv24.com` (для wildcard сертификата)
|
||||||
|
и основной домен `dfv24.com`
|
||||||
|
6. Включите остальные опции (Terms of Service, Email)
|
||||||
|
7. Нажмите **Save** для запроса сертификата
|
||||||
|
8. NPM автоматически добавит DNS TXT-записи для подтверждения владения доменом через API reg.ru
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 3. Проверка и автоматическое продление
|
||||||
|
|
||||||
|
- После успешного создания сертификата NPM будет автоматически обновлять его через DNS Challenge.
|
||||||
|
- Для успешного продления важно, чтобы API-ключ был действующим, а NPM имел доступ к DNS управлению.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Если в NPM нет готовой интеграции с reg.ru
|
||||||
|
|
||||||
|
- Используйте внешний скрипт для обновления TXT записей DNS в reg.ru, настраиваемый в NPM через **Custom DNS Provider**.
|
||||||
|
- Нужна настройка curl-запросов к API reg.ru для добавления/удаления TXT записей.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Итог
|
||||||
|
|
||||||
|
Для wildcard сертификатов Let's Encrypt у reg.ru необходимо использовать DNS Challenge, используя API провайдера для автоматического управления DNS записями.
|
||||||
|
В Nginx Proxy Manager настройте DNS Challenge с учётом особенностей reg.ru для бесшовного получения и продления сертификатов.
|
||||||
555
README.md
555
README.md
@@ -1,79 +1,532 @@
|
|||||||
# Подробная инструкция по настройке Nginx Proxy Manager с одним глобальным SSL сертификатом для всех доменов dfv24.com
|
# Руководство по использованию скриптов для управления SSL сертификатами Let's Encrypt с DNS-валидацией через API reg.ru
|
||||||
|
|
||||||
## Предпосылки
|
## Содержание
|
||||||
- Установлен и запущен [Nginx Proxy Manager](http://192.168.10.14:81/)
|
1. [Введение](#введение)
|
||||||
- Основной домен: dfv24.com
|
2. [Требования](#требования)
|
||||||
- Хостинг и DNS записи домена находятся на reg.ru
|
3. [Установка зависимостей](#установка-зависимостей)
|
||||||
- Нужно использовать один SSL сертификат (например, wildcard) для всех поддоменов dfv24.com
|
4. [Настройка](#настройка)
|
||||||
|
5. [Использование Bash скрипта](#использование-bash-скрипта)
|
||||||
|
6. [Использование Python скрипта](#использование-python-скрипта)
|
||||||
|
7. [Автоматизация обновления](#автоматизация-обновления)
|
||||||
|
8. [Устранение неполадок](#устранение-неполадок)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Шаг 1. Покупка и получение SSL Wildcard сертификата для dfv24.com
|
## Введение
|
||||||
1. На reg.ru или любом другом удостоверяющем центре (CA) закажите wildcard сертификат на домен вида `*.dfv24.com`.
|
|
||||||
2. Получите файлы сертификата:
|
В проекте представлены два скрипта для автоматического создания и обновления SSL сертификатов Let's Encrypt с использованием DNS-валидации через API reg.ru:
|
||||||
- Главный сертификат (CRT)
|
|
||||||
- Промежуточные сертификаты (CA Bundle)
|
1. **letsencrypt_regru_dns.sh** - Bash скрипт с использованием плагина certbot-dns-regru
|
||||||
- Приватный ключ (KEY)
|
2. **letsencrypt_regru_api.py** - Python скрипт с прямым взаимодействием с API reg.ru
|
||||||
|
|
||||||
|
Оба скрипта поддерживают:
|
||||||
|
- Создание wildcard сертификатов (*.domain.com)
|
||||||
|
- Автоматическое обновление сертификатов
|
||||||
|
- DNS-валидацию через API reg.ru
|
||||||
|
- Логирование всех операций
|
||||||
|
- Перезагрузку веб-сервера после обновления
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Шаг 2. Импорт вашего SSL сертификата в Nginx Proxy Manager
|
## Требования
|
||||||
1. Авторизуйтесь в Nginx Proxy Manager на http://192.168.10.14:81/
|
|
||||||
2. Перейдите в раздел **SSL Certificates** → кнопку **Add SSL Certificate**
|
### Общие требования
|
||||||
3. Выберите **Custom** (пользовательский сертификат)
|
- Операционная система: Linux (Ubuntu/Debian/CentOS)
|
||||||
4. В поля вставьте:
|
- Права: root или sudo
|
||||||
- **Certificate** — основной CRT + CA Bundle (если CA Bundle раздельно, склейте в один файл или вставляйте последовательно)
|
- Домен зарегистрирован на reg.ru
|
||||||
- **Key** — содержимое приватного ключа
|
- Доступ к API reg.ru (имя пользователя и пароль)
|
||||||
- Имя сертификата задайте, например, `dfv24_wildcard`
|
|
||||||
5. Сохраните
|
### Для Bash скрипта
|
||||||
|
- certbot
|
||||||
|
- certbot-dns-regru (плагин)
|
||||||
|
- curl
|
||||||
|
- jq
|
||||||
|
- openssl
|
||||||
|
|
||||||
|
### Для Python скрипта
|
||||||
|
- Python 3.6+
|
||||||
|
- certbot
|
||||||
|
- pip3
|
||||||
|
- Модули Python: requests, cryptography
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Шаг 3. Настройка прокси-хостов с использованием глобального сертификата
|
## Установка зависимостей
|
||||||
|
|
||||||
1. Перейдите в **Proxy Hosts** → **Add Proxy Host**
|
### Ubuntu/Debian
|
||||||
2. Заполните поля:
|
|
||||||
- **Domain Names**: Например, `sub1.dfv24.com` (для первого поддомена)
|
|
||||||
- **Scheme**: http или https, в зависимости от бекенда
|
|
||||||
- **Forward Hostname / IP**: IP или DNS адрес вашего внутреннего сервиса
|
|
||||||
- **Forward Port**: порт сервиса (например, 80 или 443)
|
|
||||||
3. Включите **SSL** → Отметьте **Use a shared SSL certificate** (если такая опция доступна) или выберите ранее импортированный сертификат из списка
|
|
||||||
4. Активируйте: **Block Common Exploits**, **Websockets Support**, выставьте Redirect HTTP to HTTPS, если требуется
|
|
||||||
5. Сохраните прокси-хост
|
|
||||||
|
|
||||||
6. Повторите для всех поддоменов, указывая нужные домены и выбирая тот же wildcard SSL сертификат
|
```bash
|
||||||
|
# Обновление пакетов
|
||||||
|
sudo apt-get update
|
||||||
|
|
||||||
|
# Установка базовых зависимостей
|
||||||
|
sudo apt-get install -y certbot curl jq openssl python3 python3-pip
|
||||||
|
|
||||||
|
# Для Python скрипта
|
||||||
|
sudo pip3 install certbot-dns-regru requests cryptography
|
||||||
|
|
||||||
|
# Для Bash скрипта (если используете)
|
||||||
|
sudo pip3 install certbot-dns-regru
|
||||||
|
```
|
||||||
|
|
||||||
|
### CentOS/RHEL
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Обновление пакетов
|
||||||
|
sudo yum update -y
|
||||||
|
|
||||||
|
# Установка EPEL репозитория
|
||||||
|
sudo yum install -y epel-release
|
||||||
|
|
||||||
|
# Установка базовых зависимостей
|
||||||
|
sudo yum install -y certbot curl jq openssl python3 python3-pip
|
||||||
|
|
||||||
|
# Установка плагина
|
||||||
|
sudo pip3 install certbot-dns-regru requests cryptography
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Шаг 4. Настройка DNS записей на reg.ru
|
## Настройка
|
||||||
|
|
||||||
1. Войдите в панель управления доменом на reg.ru
|
### 1. Получение учетных данных API reg.ru
|
||||||
2. Создайте или отредактируйте DNS записи типа A:
|
|
||||||
- `dfv24.com` → IP вашего Nginx Proxy Manager (например, 192.168.10.14)
|
1. Войдите в личный кабинет на сайте [reg.ru](https://www.reg.ru)
|
||||||
- `*.dfv24.com` → тот же IP или конкретные поддомены, если есть специальные
|
2. Перейдите в раздел "Управление API"
|
||||||
3. Сохраните изменения
|
3. Используйте ваше имя пользователя и пароль для доступа к API
|
||||||
4. Дождитесь обновления DNS (от нескольких минут до 24 часов)
|
4. Убедитесь, что у вас есть права на управление DNS-записями
|
||||||
|
|
||||||
|
### 2. Настройка конфигурации
|
||||||
|
|
||||||
|
#### Для Python скрипта
|
||||||
|
|
||||||
|
Создайте файл конфигурации на основе примера:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Создание примера конфигурации
|
||||||
|
sudo python3 letsencrypt_regru_api.py --create-config /etc/letsencrypt/regru_config.json
|
||||||
|
|
||||||
|
# Редактирование конфигурации
|
||||||
|
sudo nano /etc/letsencrypt/regru_config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Отредактируйте параметры:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"regru_username": "your_actual_username",
|
||||||
|
"regru_password": "your_actual_password",
|
||||||
|
"domain": "dfv24.com",
|
||||||
|
"wildcard": true,
|
||||||
|
"email": "admin@dfv24.com",
|
||||||
|
"cert_dir": "/etc/letsencrypt/live",
|
||||||
|
"log_file": "/var/log/letsencrypt_regru.log",
|
||||||
|
"dns_propagation_wait": 60,
|
||||||
|
"dns_check_attempts": 10,
|
||||||
|
"dns_check_interval": 10
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Для Bash скрипта
|
||||||
|
|
||||||
|
Отредактируйте переменные в начале скрипта:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo nano letsencrypt_regru_dns.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Измените следующие параметры:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
REGRU_USERNAME="your_actual_username"
|
||||||
|
REGRU_PASSWORD="your_actual_password"
|
||||||
|
DOMAIN="dfv24.com"
|
||||||
|
WILDCARD_DOMAIN="*.dfv24.com"
|
||||||
|
EMAIL="admin@dfv24.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Установка прав доступа
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Для Bash скрипта
|
||||||
|
sudo chmod +x letsencrypt_regru_dns.sh
|
||||||
|
|
||||||
|
# Для Python скрипта
|
||||||
|
sudo chmod +x letsencrypt_regru_api.py
|
||||||
|
|
||||||
|
# Защита файла конфигурации
|
||||||
|
sudo chmod 600 /etc/letsencrypt/regru_config.json
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Шаг 5. Тест и проверка работы
|
## Использование Bash скрипта
|
||||||
|
|
||||||
1. В браузере откройте любой из поддоменов `https://sub1.dfv24.com`
|
### Первый запуск (получение сертификата)
|
||||||
2. Сертификат должен быть валидным, выданным на wildcard `*.dfv24.com`
|
|
||||||
3. Проверьте работу прокси и корректность подстановки сертификата
|
```bash
|
||||||
4. При необходимости проверьте логи Nginx Proxy Manager и исправьте ошибки
|
sudo ./letsencrypt_regru_dns.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Скрипт автоматически:
|
||||||
|
1. Проверит зависимости
|
||||||
|
2. Создаст файл с учетными данными
|
||||||
|
3. Установит плагин certbot-dns-regru (если не установлен)
|
||||||
|
4. Проверит наличие существующего сертификата
|
||||||
|
5. Получит новый сертификат или обновит существующий
|
||||||
|
6. Перезагрузит веб-сервер
|
||||||
|
|
||||||
|
### Просмотр логов
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo tail -f /var/log/letsencrypt_regru.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ручная проверка сертификата
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo certbot certificates
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Дополнительно
|
## Использование Python скрипта
|
||||||
|
|
||||||
- Если в Nginx Proxy Manager нет GUI опции выбора общего сертификата, можно вручную сконфигурировать конфиги через директорий `/data/nginx/proxy_host` и прописать SSL сертификат для всех хостов.
|
### Создание примера конфигурации
|
||||||
- При обновлении сертификата — повторно импортировать его в Nginx Proxy Manager.
|
|
||||||
- Можно использовать LetsEncrypt для автоматического получения wildcard сертификата с помощью DNS-валидации (если поддерживается вашим DNS-провайдером).
|
```bash
|
||||||
|
sudo python3 letsencrypt_regru_api.py --create-config /etc/letsencrypt/regru_config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Получение нового сертификата
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# С использованием конфигурационного файла
|
||||||
|
sudo python3 letsencrypt_regru_api.py -c /etc/letsencrypt/regru_config.json --obtain
|
||||||
|
|
||||||
|
# С подробным выводом
|
||||||
|
sudo python3 letsencrypt_regru_api.py -c /etc/letsencrypt/regru_config.json --obtain -v
|
||||||
|
```
|
||||||
|
|
||||||
|
### Обновление существующего сертификата
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo python3 letsencrypt_regru_api.py -c /etc/letsencrypt/regru_config.json --renew
|
||||||
|
```
|
||||||
|
|
||||||
|
### Проверка срока действия сертификата
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo python3 letsencrypt_regru_api.py -c /etc/letsencrypt/regru_config.json --check
|
||||||
|
```
|
||||||
|
|
||||||
|
### Автоматический режим (проверка и обновление)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Скрипт сам определит, нужно ли обновление
|
||||||
|
sudo python3 letsencrypt_regru_api.py -c /etc/letsencrypt/regru_config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Опции командной строки
|
||||||
|
|
||||||
|
```
|
||||||
|
-c, --config FILE Путь к файлу конфигурации (JSON)
|
||||||
|
--create-config FILE Создать пример файла конфигурации
|
||||||
|
--obtain Получить новый сертификат
|
||||||
|
--renew Обновить существующий сертификат
|
||||||
|
--check Проверить срок действия сертификата
|
||||||
|
-v, --verbose Подробный вывод
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Итог
|
## Автоматизация обновления
|
||||||
|
|
||||||
Используйте один wildcard сертификат для всех поддоменов, импортируйте его как пользовательский сертификат в Nginx Proxy Manager, при создании прокси-хостов выбирайте его в настройках SSL. Управляйте DNS записями на reg.ru, направляя domен на IP Nginx Proxy Manager.
|
Let's Encrypt сертификаты действительны 90 дней. Рекомендуется настроить автоматическое обновление.
|
||||||
Это позволит юридически использовать единый сертификат для всех сервисов с различными поддоменами под вашим доменом dfv24.com.
|
|
||||||
|
### Использование cron (для обоих скриптов)
|
||||||
|
|
||||||
|
#### Для Python скрипта
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Открыть crontab
|
||||||
|
sudo crontab -e
|
||||||
|
|
||||||
|
# Добавить задачу (проверка каждый день в 3:00 AM)
|
||||||
|
0 3 * * * /usr/bin/python3 /path/to/letsencrypt_regru_api.py -c /etc/letsencrypt/regru_config.json >> /var/log/letsencrypt_cron.log 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Для Bash скрипта
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Открыть crontab
|
||||||
|
sudo crontab -e
|
||||||
|
|
||||||
|
# Добавить задачу (проверка каждый день в 3:00 AM)
|
||||||
|
0 3 * * * /path/to/letsencrypt_regru_dns.sh >> /var/log/letsencrypt_cron.log 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Использование systemd timer
|
||||||
|
|
||||||
|
Создайте systemd service и timer для более гибкого управления:
|
||||||
|
|
||||||
|
#### Создание service файла
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo nano /etc/systemd/system/letsencrypt-regru.service
|
||||||
|
```
|
||||||
|
|
||||||
|
Содержимое:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=Let's Encrypt Certificate Renewal with reg.ru DNS
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/bin/python3 /path/to/letsencrypt_regru_api.py -c /etc/letsencrypt/regru_config.json
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Создание timer файла
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo nano /etc/systemd/system/letsencrypt-regru.timer
|
||||||
|
```
|
||||||
|
|
||||||
|
Содержимое:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=Daily Let's Encrypt Certificate Check and Renewal
|
||||||
|
Requires=letsencrypt-regru.service
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnCalendar=daily
|
||||||
|
Persistent=true
|
||||||
|
RandomizedDelaySec=1h
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Активация timer
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Перезагрузка systemd
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
|
||||||
|
# Включение и запуск timer
|
||||||
|
sudo systemctl enable letsencrypt-regru.timer
|
||||||
|
sudo systemctl start letsencrypt-regru.timer
|
||||||
|
|
||||||
|
# Проверка статуса
|
||||||
|
sudo systemctl status letsencrypt-regru.timer
|
||||||
|
sudo systemctl list-timers
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Устранение неполадок
|
||||||
|
|
||||||
|
### Проблема: Ошибка аутентификации API reg.ru
|
||||||
|
|
||||||
|
**Решение:**
|
||||||
|
- Проверьте правильность имени пользователя и пароля
|
||||||
|
- Убедитесь, что у вашего аккаунта есть доступ к API
|
||||||
|
- Проверьте, что домен находится под управлением вашего аккаунта
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверка доступа к API
|
||||||
|
curl -X POST "https://api.reg.ru/api/regru2/user/get_balance" \
|
||||||
|
-d "username=YOUR_USERNAME" \
|
||||||
|
-d "password=YOUR_PASSWORD" \
|
||||||
|
-d "output_format=json"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Проблема: DNS запись не распространяется
|
||||||
|
|
||||||
|
**Решение:**
|
||||||
|
- Увеличьте параметр `dns_propagation_wait` в конфигурации (например, до 120 секунд)
|
||||||
|
- Проверьте DNS записи вручную:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nslookup -type=TXT _acme-challenge.dfv24.com
|
||||||
|
# или
|
||||||
|
dig TXT _acme-challenge.dfv24.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### Проблема: Certbot не установлен
|
||||||
|
|
||||||
|
**Решение:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ubuntu/Debian
|
||||||
|
sudo apt-get install certbot
|
||||||
|
|
||||||
|
# CentOS/RHEL
|
||||||
|
sudo yum install certbot
|
||||||
|
|
||||||
|
# Или через snap
|
||||||
|
sudo snap install --classic certbot
|
||||||
|
```
|
||||||
|
|
||||||
|
### Проблема: Плагин certbot-dns-regru не найден
|
||||||
|
|
||||||
|
**Решение:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Установка через pip
|
||||||
|
sudo pip3 install certbot-dns-regru
|
||||||
|
|
||||||
|
# Или через pip3 с обновлением
|
||||||
|
sudo pip3 install --upgrade certbot-dns-regru
|
||||||
|
```
|
||||||
|
|
||||||
|
### Проблема: Недостаточно прав
|
||||||
|
|
||||||
|
**Решение:**
|
||||||
|
- Убедитесь, что запускаете скрипт от имени root или с sudo
|
||||||
|
- Проверьте права доступа к директории `/etc/letsencrypt/`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo chmod 755 /etc/letsencrypt/
|
||||||
|
sudo chown -R root:root /etc/letsencrypt/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Проблема: Ошибка при перезагрузке веб-сервера
|
||||||
|
|
||||||
|
**Решение:**
|
||||||
|
- Проверьте, какой веб-сервер используется:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl status nginx
|
||||||
|
systemctl status apache2
|
||||||
|
```
|
||||||
|
|
||||||
|
- Вручную перезагрузите веб-сервер:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Для Nginx
|
||||||
|
sudo systemctl reload nginx
|
||||||
|
|
||||||
|
# Для Apache
|
||||||
|
sudo systemctl reload apache2
|
||||||
|
```
|
||||||
|
|
||||||
|
### Просмотр подробных логов certbot
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Логи certbot
|
||||||
|
sudo tail -f /var/log/letsencrypt/letsencrypt.log
|
||||||
|
|
||||||
|
# Логи скрипта
|
||||||
|
sudo tail -f /var/log/letsencrypt_regru.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Тестовый режим (staging)
|
||||||
|
|
||||||
|
Для тестирования используйте staging окружение Let's Encrypt:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Добавьте опцию --staging к команде certbot
|
||||||
|
certbot certonly --staging --dns-regru ...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Проверка полученного сертификата
|
||||||
|
|
||||||
|
### Просмотр информации о сертификате
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Просмотр всех сертификатов
|
||||||
|
sudo certbot certificates
|
||||||
|
|
||||||
|
# Просмотр конкретного сертификата
|
||||||
|
sudo openssl x509 -in /etc/letsencrypt/live/dfv24.com/cert.pem -text -noout
|
||||||
|
|
||||||
|
# Проверка даты истечения
|
||||||
|
sudo openssl x509 -enddate -noout -in /etc/letsencrypt/live/dfv24.com/cert.pem
|
||||||
|
```
|
||||||
|
|
||||||
|
### Проверка сертификата в браузере
|
||||||
|
|
||||||
|
1. Откройте ваш сайт в браузере: `https://dfv24.com`
|
||||||
|
2. Нажмите на иконку замка в адресной строке
|
||||||
|
3. Проверьте информацию о сертификате
|
||||||
|
4. Убедитесь, что сертификат выдан Let's Encrypt и покрывает wildcard домен
|
||||||
|
|
||||||
|
### Онлайн проверка SSL
|
||||||
|
|
||||||
|
- [SSL Labs](https://www.ssllabs.com/ssltest/)
|
||||||
|
- [SSL Shopper](https://www.sslshopper.com/ssl-checker.html)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Использование сертификата в Nginx Proxy Manager
|
||||||
|
|
||||||
|
После получения сертификата вы можете использовать его в Nginx Proxy Manager:
|
||||||
|
|
||||||
|
### Вариант 1: Импорт существующего сертификата
|
||||||
|
|
||||||
|
1. Войдите в Nginx Proxy Manager: http://192.168.10.14:81/
|
||||||
|
2. Перейдите в **SSL Certificates** → **Add SSL Certificate**
|
||||||
|
3. Выберите **Custom**
|
||||||
|
4. Вставьте содержимое файлов:
|
||||||
|
- Certificate Key: `/etc/letsencrypt/live/dfv24.com/privkey.pem`
|
||||||
|
- Certificate: `/etc/letsencrypt/live/dfv24.com/fullchain.pem`
|
||||||
|
5. Сохраните сертификат
|
||||||
|
|
||||||
|
### Вариант 2: Прямая настройка Nginx
|
||||||
|
|
||||||
|
Если вы используете Nginx напрямую, добавьте в конфигурацию:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
server_name dfv24.com *.dfv24.com;
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/dfv24.com/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/dfv24.com/privkey.pem;
|
||||||
|
|
||||||
|
# Дополнительные SSL настройки
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
ssl_prefer_server_ciphers on;
|
||||||
|
|
||||||
|
# ... остальная конфигурация
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Поддержка и вопросы
|
||||||
|
|
||||||
|
При возникновении проблем:
|
||||||
|
|
||||||
|
1. Проверьте логи: `/var/log/letsencrypt_regru.log`
|
||||||
|
2. Проверьте логи certbot: `/var/log/letsencrypt/letsencrypt.log`
|
||||||
|
3. Используйте подробный режим: `-v` для Python скрипта
|
||||||
|
4. Проверьте документацию reg.ru API: https://www.reg.ru/support/api
|
||||||
|
5. Проверьте документацию Let's Encrypt: https://letsencrypt.org/docs/
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Заключение
|
||||||
|
|
||||||
|
Оба скрипта предоставляют надежное решение для автоматического управления SSL сертификатами Let's Encrypt с использованием DNS-валидации через API reg.ru.
|
||||||
|
|
||||||
|
**Рекомендации:**
|
||||||
|
- Используйте Python скрипт для более гибкой настройки и интеграции
|
||||||
|
- Используйте Bash скрипт для простоты и минимальных зависимостей
|
||||||
|
- Настройте автоматическое обновление через cron или systemd timer
|
||||||
|
- Регулярно проверяйте логи и статус сертификатов
|
||||||
|
- Храните учетные данные в безопасности (chmod 600)
|
||||||
|
|
||||||
|
Успешной автоматизации! 🔒
|
||||||
|
|||||||
170
SSL_SCRIPTS_README.md
Normal file
170
SSL_SCRIPTS_README.md
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
# Автоматизация SSL сертификатов Let's Encrypt для reg.ru
|
||||||
|
|
||||||
|
Набор скриптов для автоматического создания и обновления SSL сертификатов Let's Encrypt с использованием DNS-валидации через API reg.ru.
|
||||||
|
|
||||||
|
## 📁 Содержимое проекта
|
||||||
|
|
||||||
|
- **letsencrypt_regru_dns.sh** - Bash скрипт (Linux) с certbot-dns-regru
|
||||||
|
- **letsencrypt_regru_api.py** - Python скрипт с прямым API интеграцией
|
||||||
|
- **letsencrypt_regru.ps1** - PowerShell скрипт (Windows)
|
||||||
|
- **config.json.example** - Пример файла конфигурации
|
||||||
|
- **USAGE.md** - Подробная инструкция по использованию
|
||||||
|
|
||||||
|
## 🚀 Быстрый старт
|
||||||
|
|
||||||
|
### Linux (Bash)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Отредактируйте конфигурацию в скрипте
|
||||||
|
nano letsencrypt_regru_dns.sh
|
||||||
|
|
||||||
|
# 2. Установите права
|
||||||
|
chmod +x letsencrypt_regru_dns.sh
|
||||||
|
|
||||||
|
# 3. Запустите
|
||||||
|
sudo ./letsencrypt_regru_dns.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linux (Python)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Создайте конфигурацию
|
||||||
|
sudo python3 letsencrypt_regru_api.py --create-config /etc/letsencrypt/regru_config.json
|
||||||
|
|
||||||
|
# 2. Отредактируйте конфигурацию
|
||||||
|
sudo nano /etc/letsencrypt/regru_config.json
|
||||||
|
|
||||||
|
# 3. Получите сертификат
|
||||||
|
sudo python3 letsencrypt_regru_api.py -c /etc/letsencrypt/regru_config.json --obtain
|
||||||
|
```
|
||||||
|
|
||||||
|
### Windows (PowerShell)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# 1. Создайте файл конфигурации config.json на основе config.json.example
|
||||||
|
|
||||||
|
# 2. Запустите скрипт
|
||||||
|
.\letsencrypt_regru.ps1 -ConfigFile .\config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚙️ Конфигурация
|
||||||
|
|
||||||
|
Отредактируйте `config.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"regru_username": "ваш_логин",
|
||||||
|
"regru_password": "ваш_пароль",
|
||||||
|
"domain": "dfv24.com",
|
||||||
|
"wildcard": true,
|
||||||
|
"email": "admin@dfv24.com"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📋 Требования
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
- certbot
|
||||||
|
- Python 3.6+
|
||||||
|
- pip3
|
||||||
|
- requests, cryptography (Python модули)
|
||||||
|
- certbot-dns-regru (опционально)
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
- certbot
|
||||||
|
- PowerShell 5.1+
|
||||||
|
- openssl (для проверки сертификатов)
|
||||||
|
|
||||||
|
## 🔄 Автоматическое обновление
|
||||||
|
|
||||||
|
### Через cron (Linux)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Добавьте в crontab
|
||||||
|
sudo crontab -e
|
||||||
|
|
||||||
|
# Проверка каждый день в 3:00
|
||||||
|
0 3 * * * /usr/bin/python3 /path/to/letsencrypt_regru_api.py -c /etc/letsencrypt/regru_config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Через Task Scheduler (Windows)
|
||||||
|
|
||||||
|
1. Откройте Task Scheduler
|
||||||
|
2. Создайте новую задачу
|
||||||
|
3. Триггер: Ежедневно в 3:00
|
||||||
|
4. Действие: Запуск PowerShell скрипта
|
||||||
|
|
||||||
|
## 📖 Функции
|
||||||
|
|
||||||
|
✅ Создание wildcard сертификатов (*.domain.com)
|
||||||
|
✅ Автоматическая DNS-валидация через API reg.ru
|
||||||
|
✅ Проверка срока действия сертификата
|
||||||
|
✅ Автоматическое обновление перед истечением
|
||||||
|
✅ Перезагрузка веб-сервера после обновления
|
||||||
|
✅ Подробное логирование всех операций
|
||||||
|
|
||||||
|
## 🔧 Использование с Nginx Proxy Manager
|
||||||
|
|
||||||
|
После получения сертификата:
|
||||||
|
|
||||||
|
1. Войдите в NPM: http://192.168.10.14:81/
|
||||||
|
2. SSL Certificates → Add SSL Certificate → Custom
|
||||||
|
3. Вставьте содержимое:
|
||||||
|
- Certificate Key: `/etc/letsencrypt/live/domain.com/privkey.pem`
|
||||||
|
- Certificate: `/etc/letsencrypt/live/domain.com/fullchain.pem`
|
||||||
|
|
||||||
|
## 📝 Логи
|
||||||
|
|
||||||
|
- Bash: `/var/log/letsencrypt_regru.log`
|
||||||
|
- Python: `/var/log/letsencrypt_regru.log`
|
||||||
|
- PowerShell: `.\letsencrypt_regru.log`
|
||||||
|
- Certbot: `/var/log/letsencrypt/letsencrypt.log`
|
||||||
|
|
||||||
|
## 🆘 Устранение неполадок
|
||||||
|
|
||||||
|
### Ошибка аутентификации API
|
||||||
|
- Проверьте учетные данные reg.ru
|
||||||
|
- Убедитесь, что домен под вашим управлением
|
||||||
|
|
||||||
|
### DNS запись не распространяется
|
||||||
|
- Увеличьте `dns_propagation_wait` до 120 секунд
|
||||||
|
- Проверьте DNS: `nslookup -type=TXT _acme-challenge.domain.com`
|
||||||
|
|
||||||
|
### Certbot не найден
|
||||||
|
```bash
|
||||||
|
# Ubuntu/Debian
|
||||||
|
sudo apt-get install certbot
|
||||||
|
|
||||||
|
# Или через snap
|
||||||
|
sudo snap install --classic certbot
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📚 Документация
|
||||||
|
|
||||||
|
Подробная документация в файле [USAGE.md](USAGE.md)
|
||||||
|
|
||||||
|
## 🔐 Безопасность
|
||||||
|
|
||||||
|
- Храните учетные данные в безопасности
|
||||||
|
- Используйте `chmod 600` для конфигурационных файлов
|
||||||
|
- Регулярно обновляйте пароли
|
||||||
|
|
||||||
|
## ⚠️ Важно
|
||||||
|
|
||||||
|
- Let's Encrypt сертификаты действительны 90 дней
|
||||||
|
- Рекомендуется настроить автоматическое обновление
|
||||||
|
- Для wildcard сертификатов требуется DNS-валидация
|
||||||
|
|
||||||
|
## 📞 Поддержка
|
||||||
|
|
||||||
|
- [Документация reg.ru API](https://www.reg.ru/support/api)
|
||||||
|
- [Документация Let's Encrypt](https://letsencrypt.org/docs/)
|
||||||
|
- [Certbot Documentation](https://certbot.eff.org/docs/)
|
||||||
|
|
||||||
|
## 📄 Лицензия
|
||||||
|
|
||||||
|
Скрипты предоставляются "как есть" для свободного использования.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Успешной автоматизации! 🔒**
|
||||||
12
config.json.example
Normal file
12
config.json.example
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"regru_username": "your_username",
|
||||||
|
"regru_password": "your_password",
|
||||||
|
"domain": "dfv24.com",
|
||||||
|
"wildcard": true,
|
||||||
|
"email": "admin@dfv24.com",
|
||||||
|
"cert_dir": "/etc/letsencrypt/live",
|
||||||
|
"log_file": "/var/log/letsencrypt_regru.log",
|
||||||
|
"dns_propagation_wait": 60,
|
||||||
|
"dns_check_attempts": 10,
|
||||||
|
"dns_check_interval": 10
|
||||||
|
}
|
||||||
556
letsencrypt_regru.ps1
Normal file
556
letsencrypt_regru.ps1
Normal file
@@ -0,0 +1,556 @@
|
|||||||
|
# ============================================================================
|
||||||
|
# Скрипт для создания и обновления SSL сертификата Let's Encrypt
|
||||||
|
# с использованием DNS-валидации через API reg.ru (PowerShell версия)
|
||||||
|
#
|
||||||
|
# Автор: GitHub Copilot
|
||||||
|
# Дата: 27.10.2025
|
||||||
|
# Описание: PowerShell версия скрипта для Windows окружения
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Автоматизация получения SSL сертификатов Let's Encrypt через DNS-валидацию reg.ru
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Скрипт позволяет автоматически получать и обновлять SSL сертификаты Let's Encrypt
|
||||||
|
для доменов на reg.ru используя DNS-01 challenge через API
|
||||||
|
|
||||||
|
.PARAMETER Domain
|
||||||
|
Основной домен (например, dfv24.com)
|
||||||
|
|
||||||
|
.PARAMETER Email
|
||||||
|
Email для уведомлений Let's Encrypt
|
||||||
|
|
||||||
|
.PARAMETER RegRuUsername
|
||||||
|
Имя пользователя reg.ru
|
||||||
|
|
||||||
|
.PARAMETER RegRuPassword
|
||||||
|
Пароль reg.ru
|
||||||
|
|
||||||
|
.PARAMETER Wildcard
|
||||||
|
Создать wildcard сертификат (*.domain.com)
|
||||||
|
|
||||||
|
.PARAMETER ConfigFile
|
||||||
|
Путь к файлу конфигурации JSON
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\letsencrypt_regru.ps1 -Domain "dfv24.com" -Email "admin@dfv24.com" -Wildcard
|
||||||
|
#>
|
||||||
|
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$Domain = "dfv24.com",
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$Email = "admin@dfv24.com",
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$RegRuUsername = "",
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$RegRuPassword = "",
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[switch]$Wildcard = $true,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$ConfigFile = ".\config.json",
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[switch]$Verbose = $false
|
||||||
|
)
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# КОНФИГУРАЦИЯ
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
$Script:Config = @{
|
||||||
|
RegRuApiUrl = "https://api.reg.ru/api/regru2"
|
||||||
|
CertbotPath = "certbot"
|
||||||
|
LogFile = ".\letsencrypt_regru.log"
|
||||||
|
DnsPropagationWait = 60
|
||||||
|
DnsCheckAttempts = 10
|
||||||
|
DnsCheckInterval = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# ФУНКЦИИ ЛОГИРОВАНИЯ
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
function Write-Log {
|
||||||
|
param(
|
||||||
|
[string]$Message,
|
||||||
|
[string]$Level = "INFO"
|
||||||
|
)
|
||||||
|
|
||||||
|
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||||||
|
$LogMessage = "[$Timestamp] [$Level] $Message"
|
||||||
|
|
||||||
|
# Вывод в консоль
|
||||||
|
switch ($Level) {
|
||||||
|
"ERROR" { Write-Host $LogMessage -ForegroundColor Red }
|
||||||
|
"WARNING" { Write-Host $LogMessage -ForegroundColor Yellow }
|
||||||
|
"SUCCESS" { Write-Host $LogMessage -ForegroundColor Green }
|
||||||
|
default { Write-Host $LogMessage }
|
||||||
|
}
|
||||||
|
|
||||||
|
# Запись в файл
|
||||||
|
Add-Content -Path $Script:Config.LogFile -Value $LogMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# ФУНКЦИИ РАБОТЫ С КОНФИГУРАЦИЕЙ
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
function Load-Configuration {
|
||||||
|
param([string]$ConfigPath)
|
||||||
|
|
||||||
|
Write-Log "Загрузка конфигурации из: $ConfigPath"
|
||||||
|
|
||||||
|
if (Test-Path $ConfigPath) {
|
||||||
|
try {
|
||||||
|
$config = Get-Content -Path $ConfigPath -Raw | ConvertFrom-Json
|
||||||
|
|
||||||
|
# Обновляем параметры скрипта из конфигурации
|
||||||
|
if ($config.domain) { $Script:Domain = $config.domain }
|
||||||
|
if ($config.email) { $Script:Email = $config.email }
|
||||||
|
if ($config.regru_username) { $Script:RegRuUsername = $config.regru_username }
|
||||||
|
if ($config.regru_password) { $Script:RegRuPassword = $config.regru_password }
|
||||||
|
if ($null -ne $config.wildcard) { $Script:Wildcard = $config.wildcard }
|
||||||
|
|
||||||
|
Write-Log "Конфигурация успешно загружена" "SUCCESS"
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Log "Ошибка при загрузке конфигурации: $_" "ERROR"
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Log "Файл конфигурации не найден: $ConfigPath" "WARNING"
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Create-SampleConfig {
|
||||||
|
param([string]$OutputPath = ".\config.json")
|
||||||
|
|
||||||
|
$sampleConfig = @{
|
||||||
|
regru_username = "your_username"
|
||||||
|
regru_password = "your_password"
|
||||||
|
domain = "dfv24.com"
|
||||||
|
wildcard = $true
|
||||||
|
email = "admin@dfv24.com"
|
||||||
|
dns_propagation_wait = 60
|
||||||
|
dns_check_attempts = 10
|
||||||
|
dns_check_interval = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
$sampleConfig | ConvertTo-Json -Depth 10 | Set-Content -Path $OutputPath
|
||||||
|
Write-Log "Пример конфигурации создан: $OutputPath" "SUCCESS"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# ФУНКЦИИ РАБОТЫ С REG.RU API
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
function Invoke-RegRuApi {
|
||||||
|
param(
|
||||||
|
[string]$Method,
|
||||||
|
[hashtable]$Params
|
||||||
|
)
|
||||||
|
|
||||||
|
$url = "$($Script:Config.RegRuApiUrl)/$Method"
|
||||||
|
|
||||||
|
# Добавляем учетные данные
|
||||||
|
$Params["username"] = $Script:RegRuUsername
|
||||||
|
$Params["password"] = $Script:RegRuPassword
|
||||||
|
$Params["output_format"] = "json"
|
||||||
|
|
||||||
|
Write-Log "Отправка запроса к API: $Method" "DEBUG"
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = Invoke-RestMethod -Uri $url -Method Post -Body $Params -ContentType "application/x-www-form-urlencoded"
|
||||||
|
|
||||||
|
if ($response.result -eq "success") {
|
||||||
|
Write-Log "Запрос $Method выполнен успешно" "DEBUG"
|
||||||
|
return $response
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errorMsg = if ($response.error_text) { $response.error_text } else { "Неизвестная ошибка" }
|
||||||
|
Write-Log "Ошибка API: $errorMsg" "ERROR"
|
||||||
|
throw "API Error: $errorMsg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Log "Ошибка HTTP запроса: $_" "ERROR"
|
||||||
|
throw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-DnsRecords {
|
||||||
|
param([string]$Domain)
|
||||||
|
|
||||||
|
Write-Log "Получение DNS записей для домена: $Domain"
|
||||||
|
|
||||||
|
$params = @{
|
||||||
|
domain = $Domain
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = Invoke-RegRuApi -Method "zone/get_resource_records" -Params $params
|
||||||
|
|
||||||
|
if ($response.answer -and $response.answer.records) {
|
||||||
|
$records = $response.answer.records
|
||||||
|
Write-Log "Получено $($records.Count) DNS записей"
|
||||||
|
return $records
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Log "DNS записи не найдены" "WARNING"
|
||||||
|
return @()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Add-TxtRecord {
|
||||||
|
param(
|
||||||
|
[string]$Domain,
|
||||||
|
[string]$Subdomain,
|
||||||
|
[string]$TxtValue
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Log "Добавление TXT записи: $Subdomain.$Domain = $TxtValue"
|
||||||
|
|
||||||
|
$params = @{
|
||||||
|
domain = $Domain
|
||||||
|
subdomain = $Subdomain
|
||||||
|
text = $TxtValue
|
||||||
|
output_content_type = "plain"
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$null = Invoke-RegRuApi -Method "zone/add_txt" -Params $params
|
||||||
|
Write-Log "TXT запись успешно добавлена" "SUCCESS"
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Log "Не удалось добавить TXT запись: $_" "ERROR"
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Remove-TxtRecord {
|
||||||
|
param(
|
||||||
|
[string]$Domain,
|
||||||
|
[string]$Subdomain,
|
||||||
|
[string]$TxtValue
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Log "Удаление TXT записи: $Subdomain.$Domain"
|
||||||
|
|
||||||
|
# Получаем список записей
|
||||||
|
$records = Get-DnsRecords -Domain $Domain
|
||||||
|
|
||||||
|
# Ищем нужную TXT запись
|
||||||
|
$record = $records | Where-Object {
|
||||||
|
$_.rectype -eq "TXT" -and
|
||||||
|
$_.subdomain -eq $Subdomain -and
|
||||||
|
$_.text -eq $TxtValue
|
||||||
|
} | Select-Object -First 1
|
||||||
|
|
||||||
|
if (-not $record) {
|
||||||
|
Write-Log "TXT запись для удаления не найдена" "WARNING"
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = @{
|
||||||
|
domain = $Domain
|
||||||
|
record_id = $record.id
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$null = Invoke-RegRuApi -Method "zone/remove_record" -Params $params
|
||||||
|
Write-Log "TXT запись успешно удалена" "SUCCESS"
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Log "Не удалось удалить TXT запись: $_" "ERROR"
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# ФУНКЦИИ РАБОТЫ С CERTBOT
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
function Test-CertbotInstalled {
|
||||||
|
try {
|
||||||
|
$version = & certbot --version 2>&1
|
||||||
|
Write-Log "Certbot установлен: $version"
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Log "Certbot не установлен!" "ERROR"
|
||||||
|
Write-Log "Установите Certbot: https://certbot.eff.org/instructions" "ERROR"
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-CertificateExpiry {
|
||||||
|
param([string]$Domain)
|
||||||
|
|
||||||
|
$certPath = Join-Path $env:ProgramData "letsencrypt\live\$Domain\cert.pem"
|
||||||
|
|
||||||
|
if (-not (Test-Path $certPath)) {
|
||||||
|
Write-Log "Сертификат не найден: $certPath"
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
# Используем openssl для проверки сертификата
|
||||||
|
$expiryText = & openssl x509 -enddate -noout -in $certPath 2>&1
|
||||||
|
|
||||||
|
if ($expiryText -match "notAfter=(.+)") {
|
||||||
|
$expiryDate = [DateTime]::Parse($matches[1])
|
||||||
|
$daysLeft = ($expiryDate - (Get-Date)).Days
|
||||||
|
|
||||||
|
Write-Log "Сертификат истекает: $($expiryDate.ToString('yyyy-MM-dd'))"
|
||||||
|
Write-Log "Осталось дней: $daysLeft"
|
||||||
|
|
||||||
|
return $daysLeft
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Log "Ошибка при проверке сертификата: $_" "ERROR"
|
||||||
|
}
|
||||||
|
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-DnsChallenge {
|
||||||
|
param(
|
||||||
|
[string]$Domain,
|
||||||
|
[string]$ValidationToken
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Log "=== DNS Challenge: Добавление TXT записи ===" "INFO"
|
||||||
|
|
||||||
|
# Извлекаем поддомен
|
||||||
|
$subdomain = "_acme-challenge"
|
||||||
|
|
||||||
|
# Добавляем TXT запись
|
||||||
|
$success = Add-TxtRecord -Domain $Script:Domain -Subdomain $subdomain -TxtValue $ValidationToken
|
||||||
|
|
||||||
|
if ($success) {
|
||||||
|
# Ждем распространения DNS
|
||||||
|
$waitTime = $Script:Config.DnsPropagationWait
|
||||||
|
Write-Log "Ожидание распространения DNS ($waitTime секунд)..."
|
||||||
|
Start-Sleep -Seconds $waitTime
|
||||||
|
|
||||||
|
Write-Log "DNS валидация готова" "SUCCESS"
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-DnsCleanup {
|
||||||
|
param(
|
||||||
|
[string]$Domain,
|
||||||
|
[string]$ValidationToken
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Log "=== DNS Challenge: Удаление TXT записи ===" "INFO"
|
||||||
|
|
||||||
|
$subdomain = "_acme-challenge"
|
||||||
|
|
||||||
|
return Remove-TxtRecord -Domain $Script:Domain -Subdomain $subdomain -TxtValue $ValidationToken
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# ГЛАВНЫЕ ФУНКЦИИ
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
function Get-Certificate {
|
||||||
|
param([string]$Domain, [bool]$WildcardCert)
|
||||||
|
|
||||||
|
Write-Log "=== Запрос нового SSL сертификата ===" "INFO"
|
||||||
|
|
||||||
|
# Формируем список доменов
|
||||||
|
$domains = @($Domain)
|
||||||
|
if ($WildcardCert) {
|
||||||
|
$domains += "*.$Domain"
|
||||||
|
}
|
||||||
|
|
||||||
|
$domainArgs = @()
|
||||||
|
foreach ($d in $domains) {
|
||||||
|
$domainArgs += "-d"
|
||||||
|
$domainArgs += $d
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log "Домены для сертификата: $($domains -join ', ')"
|
||||||
|
|
||||||
|
# Создаем временные скрипты для хуков
|
||||||
|
$authHookScript = Join-Path $env:TEMP "certbot_auth_hook.ps1"
|
||||||
|
$cleanupHookScript = Join-Path $env:TEMP "certbot_cleanup_hook.ps1"
|
||||||
|
|
||||||
|
# Скрипт для аутентификации
|
||||||
|
@"
|
||||||
|
param([string]`$Domain, [string]`$Token)
|
||||||
|
# Вызов функции добавления TXT записи
|
||||||
|
# Здесь должна быть логика работы с API reg.ru
|
||||||
|
"@ | Set-Content -Path $authHookScript
|
||||||
|
|
||||||
|
# Скрипт для очистки
|
||||||
|
@"
|
||||||
|
param([string]`$Domain, [string]`$Token)
|
||||||
|
# Вызов функции удаления TXT записи
|
||||||
|
"@ | Set-Content -Path $cleanupHookScript
|
||||||
|
|
||||||
|
Write-Log "Примечание: Для полной автоматизации используйте Linux версию скрипта" "WARNING"
|
||||||
|
Write-Log "На Windows рекомендуется использовать плагин certbot-dns-регru или выполнить вручную" "WARNING"
|
||||||
|
|
||||||
|
# Команда certbot (базовая, требует ручной DNS валидации)
|
||||||
|
$certbotArgs = @(
|
||||||
|
"certonly",
|
||||||
|
"--manual",
|
||||||
|
"--preferred-challenges", "dns",
|
||||||
|
"--email", $Script:Email,
|
||||||
|
"--agree-tos",
|
||||||
|
"--manual-public-ip-logging-ok"
|
||||||
|
) + $domainArgs
|
||||||
|
|
||||||
|
Write-Log "Запуск certbot..."
|
||||||
|
Write-Log "ВАЖНО: Certbot запросит вас добавить TXT записи в DNS" "WARNING"
|
||||||
|
Write-Log "Используйте API reg.ru или добавьте записи вручную через панель управления" "WARNING"
|
||||||
|
|
||||||
|
try {
|
||||||
|
& certbot @certbotArgs
|
||||||
|
|
||||||
|
Write-Log "Процесс получения сертификата завершен" "SUCCESS"
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Log "Ошибка при получении сертификата: $_" "ERROR"
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Update-Certificate {
|
||||||
|
Write-Log "=== Обновление SSL сертификата ===" "INFO"
|
||||||
|
|
||||||
|
try {
|
||||||
|
& certbot renew
|
||||||
|
|
||||||
|
Write-Log "Проверка обновления завершена" "SUCCESS"
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Log "Ошибка при обновлении: $_" "ERROR"
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Show-CertificateInfo {
|
||||||
|
param([string]$Domain)
|
||||||
|
|
||||||
|
$certPath = Join-Path $env:ProgramData "letsencrypt\live\$Domain\cert.pem"
|
||||||
|
|
||||||
|
if (-not (Test-Path $certPath)) {
|
||||||
|
Write-Log "Сертификат не найден" "WARNING"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log ("=" * 60)
|
||||||
|
Write-Log "ИНФОРМАЦИЯ О СЕРТИФИКАТЕ"
|
||||||
|
Write-Log ("=" * 60)
|
||||||
|
|
||||||
|
try {
|
||||||
|
$certInfo = & openssl x509 -in $certPath -text -noout
|
||||||
|
|
||||||
|
# Выводим основную информацию
|
||||||
|
$certInfo -split "`n" | Where-Object {
|
||||||
|
$_ -match "Subject:|Issuer:|Not Before|Not After|DNS:"
|
||||||
|
} | ForEach-Object {
|
||||||
|
Write-Log $_.Trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log ("=" * 60)
|
||||||
|
Write-Log "ПУТИ К ФАЙЛАМ СЕРТИФИКАТА:"
|
||||||
|
Write-Log " Сертификат: $certPath"
|
||||||
|
Write-Log " Приватный ключ: $(Join-Path $env:ProgramData "letsencrypt\live\$Domain\privkey.pem")"
|
||||||
|
Write-Log " Цепочка: $(Join-Path $env:ProgramData "letsencrypt\live\$Domain\chain.pem")"
|
||||||
|
Write-Log " Полная цепочка: $(Join-Path $env:ProgramData "letsencrypt\live\$Domain\fullchain.pem")"
|
||||||
|
Write-Log ("=" * 60)
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Log "Ошибка при чтении сертификата: $_" "ERROR"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# ОСНОВНАЯ ЛОГИКА
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
function Main {
|
||||||
|
Write-Log ("=" * 60)
|
||||||
|
Write-Log "СКРИПТ УПРАВЛЕНИЯ SSL СЕРТИФИКАТАМИ LET'S ENCRYPT"
|
||||||
|
Write-Log ("=" * 60)
|
||||||
|
|
||||||
|
# Проверка прав администратора
|
||||||
|
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||||
|
$principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
|
||||||
|
$isAdmin = $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||||
|
|
||||||
|
if (-not $isAdmin) {
|
||||||
|
Write-Log "ПРЕДУПРЕЖДЕНИЕ: Скрипт запущен без прав администратора" "WARNING"
|
||||||
|
Write-Log "Некоторые операции могут потребовать повышенных прав" "WARNING"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Загрузка конфигурации
|
||||||
|
if ($ConfigFile -and (Test-Path $ConfigFile)) {
|
||||||
|
Load-Configuration -ConfigPath $ConfigFile
|
||||||
|
}
|
||||||
|
|
||||||
|
# Проверка обязательных параметров
|
||||||
|
if (-not $Script:RegRuUsername -or -not $Script:RegRuPassword) {
|
||||||
|
Write-Log "ОШИБКА: Не указаны учетные данные reg.ru" "ERROR"
|
||||||
|
Write-Log "Укажите RegRuUsername и RegRuPassword или создайте файл конфигурации" "ERROR"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
# Проверка Certbot
|
||||||
|
if (-not (Test-CertbotInstalled)) {
|
||||||
|
Write-Log "Установите Certbot и повторите попытку" "ERROR"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
# Проверка срока действия сертификата
|
||||||
|
$daysLeft = Get-CertificateExpiry -Domain $Script:Domain
|
||||||
|
|
||||||
|
if ($null -eq $daysLeft) {
|
||||||
|
Write-Log "Сертификат не найден. Требуется создание нового." "INFO"
|
||||||
|
$success = Get-Certificate -Domain $Script:Domain -WildcardCert $Script:Wildcard
|
||||||
|
}
|
||||||
|
elseif ($daysLeft -lt 30) {
|
||||||
|
Write-Log "Сертификат истекает через $daysLeft дней. Требуется обновление!" "WARNING"
|
||||||
|
$success = Update-Certificate
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Log "Сертификат действителен ($daysLeft дней)" "SUCCESS"
|
||||||
|
$success = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($success) {
|
||||||
|
Show-CertificateInfo -Domain $Script:Domain
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log ("=" * 60)
|
||||||
|
Write-Log "Скрипт завершен"
|
||||||
|
Write-Log ("=" * 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# ТОЧКА ВХОДА
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Запуск основной функции
|
||||||
|
Main
|
||||||
831
letsencrypt_regru_api.py
Normal file
831
letsencrypt_regru_api.py
Normal file
@@ -0,0 +1,831 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
Скрипт для создания и обновления SSL сертификата Let's Encrypt
|
||||||
|
с использованием DNS-валидации через API reg.ru
|
||||||
|
|
||||||
|
Автор: GitHub Copilot
|
||||||
|
Дата: 27.10.2025
|
||||||
|
|
||||||
|
Описание:
|
||||||
|
Этот скрипт автоматизирует процесс получения и обновления SSL сертификатов
|
||||||
|
Let's Encrypt для доменов, зарегистрированных на reg.ru, используя DNS-01 challenge.
|
||||||
|
Скрипт напрямую работает с API reg.ru для управления DNS записями.
|
||||||
|
|
||||||
|
Требования:
|
||||||
|
- Python 3.6+
|
||||||
|
- requests
|
||||||
|
- certbot
|
||||||
|
- cryptography
|
||||||
|
|
||||||
|
Установка зависимостей:
|
||||||
|
pip install requests certbot cryptography
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
import argparse
|
||||||
|
import subprocess
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from typing import Dict, List, Optional, Tuple
|
||||||
|
|
||||||
|
try:
|
||||||
|
import requests
|
||||||
|
except ImportError:
|
||||||
|
print("ОШИБКА: Необходимо установить модуль 'requests'")
|
||||||
|
print("Выполните: pip install requests")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# КОНФИГУРАЦИЯ
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# Настройки по умолчанию
|
||||||
|
DEFAULT_CONFIG = {
|
||||||
|
# Учетные данные API reg.ru
|
||||||
|
"regru_username": "your_username",
|
||||||
|
"regru_password": "your_password",
|
||||||
|
|
||||||
|
# Параметры домена
|
||||||
|
"domain": "dfv24.com",
|
||||||
|
"wildcard": True, # Создавать wildcard сертификат (*.domain.com)
|
||||||
|
|
||||||
|
# Email для уведомлений Let's Encrypt
|
||||||
|
"email": "admin@dfv24.com",
|
||||||
|
|
||||||
|
# Директории
|
||||||
|
"cert_dir": "/etc/letsencrypt/live",
|
||||||
|
"log_file": "/var/log/letsencrypt_regru.log",
|
||||||
|
|
||||||
|
# Параметры DNS
|
||||||
|
"dns_propagation_wait": 60, # Время ожидания распространения DNS (секунды)
|
||||||
|
"dns_check_attempts": 10, # Количество попыток проверки DNS
|
||||||
|
"dns_check_interval": 10, # Интервал между проверками DNS (секунды)
|
||||||
|
}
|
||||||
|
|
||||||
|
# API endpoints для reg.ru
|
||||||
|
REGRU_API_URL = "https://api.reg.ru/api/regru2"
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# НАСТРОЙКА ЛОГИРОВАНИЯ
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
def setup_logging(log_file: str, verbose: bool = False) -> logging.Logger:
|
||||||
|
"""
|
||||||
|
Настройка системы логирования
|
||||||
|
|
||||||
|
Args:
|
||||||
|
log_file: Путь к файлу лога
|
||||||
|
verbose: Режим подробного вывода
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Logger объект
|
||||||
|
"""
|
||||||
|
log_level = logging.DEBUG if verbose else logging.INFO
|
||||||
|
|
||||||
|
# Создаем директорию для логов, если не существует
|
||||||
|
log_dir = os.path.dirname(log_file)
|
||||||
|
if log_dir and not os.path.exists(log_dir):
|
||||||
|
os.makedirs(log_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# Настройка форматирования
|
||||||
|
formatter = logging.Formatter(
|
||||||
|
'%(asctime)s - %(levelname)s - %(message)s',
|
||||||
|
datefmt='%Y-%m-%d %H:%M:%S'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Создаем logger
|
||||||
|
logger = logging.getLogger('LetsEncrypt_RegRU')
|
||||||
|
logger.setLevel(log_level)
|
||||||
|
|
||||||
|
# Обработчик для файла
|
||||||
|
file_handler = logging.FileHandler(log_file, encoding='utf-8')
|
||||||
|
file_handler.setLevel(log_level)
|
||||||
|
file_handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
# Обработчик для консоли
|
||||||
|
console_handler = logging.StreamHandler(sys.stdout)
|
||||||
|
console_handler.setLevel(log_level)
|
||||||
|
console_handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
# Добавляем обработчики
|
||||||
|
logger.addHandler(file_handler)
|
||||||
|
logger.addHandler(console_handler)
|
||||||
|
|
||||||
|
return logger
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# КЛАСС ДЛЯ РАБОТЫ С API REG.RU
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
class RegRuAPI:
|
||||||
|
"""Класс для работы с API reg.ru"""
|
||||||
|
|
||||||
|
def __init__(self, username: str, password: str, logger: logging.Logger):
|
||||||
|
"""
|
||||||
|
Инициализация API клиента
|
||||||
|
|
||||||
|
Args:
|
||||||
|
username: Имя пользователя reg.ru
|
||||||
|
password: Пароль reg.ru
|
||||||
|
logger: Logger объект
|
||||||
|
"""
|
||||||
|
self.username = username
|
||||||
|
self.password = password
|
||||||
|
self.logger = logger
|
||||||
|
self.session = requests.Session()
|
||||||
|
|
||||||
|
def _make_request(self, method: str, params: Dict) -> Dict:
|
||||||
|
"""
|
||||||
|
Выполнение запроса к API reg.ru
|
||||||
|
|
||||||
|
Args:
|
||||||
|
method: Название метода API
|
||||||
|
params: Параметры запроса
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Ответ API в формате dict
|
||||||
|
"""
|
||||||
|
url = f"{REGRU_API_URL}/{method}"
|
||||||
|
|
||||||
|
# Добавляем учетные данные к параметрам
|
||||||
|
params.update({
|
||||||
|
"username": self.username,
|
||||||
|
"password": self.password,
|
||||||
|
"output_format": "json"
|
||||||
|
})
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.logger.debug(f"Отправка запроса к API: {method}")
|
||||||
|
response = self.session.post(url, data=params)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
result = response.json()
|
||||||
|
|
||||||
|
if result.get("result") == "success":
|
||||||
|
self.logger.debug(f"Запрос {method} выполнен успешно")
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
error_msg = result.get("error_text", "Неизвестная ошибка")
|
||||||
|
self.logger.error(f"Ошибка API: {error_msg}")
|
||||||
|
raise Exception(f"API Error: {error_msg}")
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
self.logger.error(f"Ошибка HTTP запроса: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
def get_zone_records(self, domain: str) -> List[Dict]:
|
||||||
|
"""
|
||||||
|
Получение DNS записей домена
|
||||||
|
|
||||||
|
Args:
|
||||||
|
domain: Доменное имя
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Список DNS записей
|
||||||
|
"""
|
||||||
|
self.logger.info(f"Получение DNS записей для домена: {domain}")
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"domain": domain,
|
||||||
|
}
|
||||||
|
|
||||||
|
result = self._make_request("zone/get_resource_records", params)
|
||||||
|
|
||||||
|
if "answer" in result and "records" in result["answer"]:
|
||||||
|
records = result["answer"]["records"]
|
||||||
|
self.logger.info(f"Получено {len(records)} DNS записей")
|
||||||
|
return records
|
||||||
|
else:
|
||||||
|
self.logger.warning("DNS записи не найдены")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def add_txt_record(self, domain: str, subdomain: str, txt_value: str) -> bool:
|
||||||
|
"""
|
||||||
|
Добавление TXT записи для DNS валидации
|
||||||
|
|
||||||
|
Args:
|
||||||
|
domain: Основной домен
|
||||||
|
subdomain: Поддомен (например, _acme-challenge)
|
||||||
|
txt_value: Значение TXT записи
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True если успешно, False в противном случае
|
||||||
|
"""
|
||||||
|
self.logger.info(f"Добавление TXT записи: {subdomain}.{domain} = {txt_value}")
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"domain": domain,
|
||||||
|
"subdomain": subdomain,
|
||||||
|
"text": txt_value,
|
||||||
|
"output_content_type": "plain"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._make_request("zone/add_txt", params)
|
||||||
|
self.logger.info("TXT запись успешно добавлена")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Не удалось добавить TXT запись: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def remove_txt_record(self, domain: str, subdomain: str, txt_value: str) -> bool:
|
||||||
|
"""
|
||||||
|
Удаление TXT записи
|
||||||
|
|
||||||
|
Args:
|
||||||
|
domain: Основной домен
|
||||||
|
subdomain: Поддомен
|
||||||
|
txt_value: Значение TXT записи
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True если успешно, False в противном случае
|
||||||
|
"""
|
||||||
|
self.logger.info(f"Удаление TXT записи: {subdomain}.{domain}")
|
||||||
|
|
||||||
|
# Сначала получаем список всех записей
|
||||||
|
records = self.get_zone_records(domain)
|
||||||
|
|
||||||
|
# Ищем нужную TXT запись
|
||||||
|
record_id = None
|
||||||
|
for record in records:
|
||||||
|
if (record.get("rectype") == "TXT" and
|
||||||
|
record.get("subdomain") == subdomain and
|
||||||
|
record.get("text") == txt_value):
|
||||||
|
record_id = record.get("id")
|
||||||
|
break
|
||||||
|
|
||||||
|
if not record_id:
|
||||||
|
self.logger.warning("TXT запись для удаления не найдена")
|
||||||
|
return False
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"domain": domain,
|
||||||
|
"record_id": record_id
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._make_request("zone/remove_record", params)
|
||||||
|
self.logger.info("TXT запись успешно удалена")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Не удалось удалить TXT запись: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# КЛАСС ДЛЯ РАБОТЫ С CERTBOT
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
class LetsEncryptManager:
|
||||||
|
"""Класс для управления сертификатами Let's Encrypt"""
|
||||||
|
|
||||||
|
def __init__(self, config: Dict, api: RegRuAPI, logger: logging.Logger):
|
||||||
|
"""
|
||||||
|
Инициализация менеджера сертификатов
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config: Конфигурация
|
||||||
|
api: API клиент reg.ru
|
||||||
|
logger: Logger объект
|
||||||
|
"""
|
||||||
|
self.config = config
|
||||||
|
self.api = api
|
||||||
|
self.logger = logger
|
||||||
|
self.domain = config["domain"]
|
||||||
|
self.email = config["email"]
|
||||||
|
self.cert_dir = os.path.join(config["cert_dir"], self.domain)
|
||||||
|
|
||||||
|
def check_certbot_installed(self) -> bool:
|
||||||
|
"""
|
||||||
|
Проверка установки certbot
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True если certbot установлен
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["certbot", "--version"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True
|
||||||
|
)
|
||||||
|
self.logger.debug(f"Certbot установлен: {result.stdout.strip()}")
|
||||||
|
return True
|
||||||
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||||
|
self.logger.error("Certbot не установлен!")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def check_certificate_expiry(self) -> Optional[int]:
|
||||||
|
"""
|
||||||
|
Проверка срока действия сертификата
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Количество дней до истечения или None если сертификат не найден
|
||||||
|
"""
|
||||||
|
cert_file = os.path.join(self.cert_dir, "cert.pem")
|
||||||
|
|
||||||
|
if not os.path.exists(cert_file):
|
||||||
|
self.logger.info("Сертификат не найден")
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
from cryptography import x509
|
||||||
|
from cryptography.hazmat.backends import default_backend
|
||||||
|
|
||||||
|
with open(cert_file, "rb") as f:
|
||||||
|
cert_data = f.read()
|
||||||
|
cert = x509.load_pem_x509_certificate(cert_data, default_backend())
|
||||||
|
|
||||||
|
expiry_date = cert.not_valid_after
|
||||||
|
days_left = (expiry_date - datetime.now()).days
|
||||||
|
|
||||||
|
self.logger.info(f"Сертификат истекает: {expiry_date.strftime('%Y-%m-%d')}")
|
||||||
|
self.logger.info(f"Осталось дней: {days_left}")
|
||||||
|
|
||||||
|
return days_left
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Ошибка при проверке сертификата: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def dns_challenge_hook(self, validation_domain: str, validation_token: str) -> bool:
|
||||||
|
"""
|
||||||
|
Обработчик DNS challenge - добавление TXT записи
|
||||||
|
|
||||||
|
Args:
|
||||||
|
validation_domain: Домен для валидации (_acme-challenge.domain.com)
|
||||||
|
validation_token: Токен валидации
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True если успешно
|
||||||
|
"""
|
||||||
|
self.logger.info("=== DNS Challenge: Добавление TXT записи ===")
|
||||||
|
|
||||||
|
# Извлекаем поддомен из validation_domain
|
||||||
|
# Формат: _acme-challenge.domain.com или _acme-challenge
|
||||||
|
parts = validation_domain.replace(f".{self.domain}", "").split(".")
|
||||||
|
subdomain = parts[0] if parts else "_acme-challenge"
|
||||||
|
|
||||||
|
# Добавляем TXT запись
|
||||||
|
success = self.api.add_txt_record(self.domain, subdomain, validation_token)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
# Ждем распространения DNS
|
||||||
|
wait_time = self.config.get("dns_propagation_wait", 60)
|
||||||
|
self.logger.info(f"Ожидание распространения DNS ({wait_time} секунд)...")
|
||||||
|
time.sleep(wait_time)
|
||||||
|
|
||||||
|
# Проверяем DNS запись
|
||||||
|
if self.verify_dns_record(subdomain, validation_token):
|
||||||
|
self.logger.info("DNS валидация готова")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self.logger.warning("DNS запись не распространилась вовремя, но продолжаем...")
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def dns_cleanup_hook(self, validation_domain: str, validation_token: str) -> bool:
|
||||||
|
"""
|
||||||
|
Обработчик очистки DNS challenge - удаление TXT записи
|
||||||
|
|
||||||
|
Args:
|
||||||
|
validation_domain: Домен валидации
|
||||||
|
validation_token: Токен валидации
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True если успешно
|
||||||
|
"""
|
||||||
|
self.logger.info("=== DNS Challenge: Удаление TXT записи ===")
|
||||||
|
|
||||||
|
parts = validation_domain.replace(f".{self.domain}", "").split(".")
|
||||||
|
subdomain = parts[0] if parts else "_acme-challenge"
|
||||||
|
|
||||||
|
return self.api.remove_txt_record(self.domain, subdomain, validation_token)
|
||||||
|
|
||||||
|
def verify_dns_record(self, subdomain: str, expected_value: str) -> bool:
|
||||||
|
"""
|
||||||
|
Проверка наличия DNS записи
|
||||||
|
|
||||||
|
Args:
|
||||||
|
subdomain: Поддомен
|
||||||
|
expected_value: Ожидаемое значение TXT записи
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True если запись найдена
|
||||||
|
"""
|
||||||
|
import socket
|
||||||
|
|
||||||
|
full_domain = f"{subdomain}.{self.domain}"
|
||||||
|
attempts = self.config.get("dns_check_attempts", 10)
|
||||||
|
interval = self.config.get("dns_check_interval", 10)
|
||||||
|
|
||||||
|
self.logger.info(f"Проверка DNS записи для {full_domain}")
|
||||||
|
|
||||||
|
for attempt in range(attempts):
|
||||||
|
try:
|
||||||
|
# Используем nslookup или dig через subprocess
|
||||||
|
result = subprocess.run(
|
||||||
|
["nslookup", "-type=TXT", full_domain],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
|
||||||
|
if expected_value in result.stdout:
|
||||||
|
self.logger.info(f"DNS запись найдена (попытка {attempt + 1})")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.debug(f"Попытка {attempt + 1}: DNS запись не найдена - {e}")
|
||||||
|
|
||||||
|
if attempt < attempts - 1:
|
||||||
|
time.sleep(interval)
|
||||||
|
|
||||||
|
self.logger.warning("DNS запись не найдена после всех попыток")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def obtain_certificate(self) -> bool:
|
||||||
|
"""
|
||||||
|
Получение нового сертификата
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True если успешно
|
||||||
|
"""
|
||||||
|
self.logger.info("=== Запрос нового SSL сертификата ===")
|
||||||
|
|
||||||
|
# Формируем список доменов
|
||||||
|
domains = [self.domain]
|
||||||
|
if self.config.get("wildcard", False):
|
||||||
|
domains.append(f"*.{self.domain}")
|
||||||
|
|
||||||
|
domain_args = []
|
||||||
|
for d in domains:
|
||||||
|
domain_args.extend(["-d", d])
|
||||||
|
|
||||||
|
# Команда certbot
|
||||||
|
cmd = [
|
||||||
|
"certbot", "certonly",
|
||||||
|
"--manual",
|
||||||
|
"--preferred-challenges", "dns",
|
||||||
|
"--manual-auth-hook", f"{sys.executable} {os.path.abspath(__file__)} --auth-hook",
|
||||||
|
"--manual-cleanup-hook", f"{sys.executable} {os.path.abspath(__file__)} --cleanup-hook",
|
||||||
|
"--email", self.email,
|
||||||
|
"--agree-tos",
|
||||||
|
"--non-interactive",
|
||||||
|
"--expand",
|
||||||
|
] + domain_args
|
||||||
|
|
||||||
|
self.logger.info(f"Выполнение команды: {' '.join(cmd)}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.logger.info("Сертификат успешно получен!")
|
||||||
|
self.logger.debug(result.stdout)
|
||||||
|
return True
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
self.logger.error(f"Ошибка при получении сертификата: {e}")
|
||||||
|
self.logger.error(e.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def renew_certificate(self) -> bool:
|
||||||
|
"""
|
||||||
|
Обновление существующего сертификата
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True если успешно
|
||||||
|
"""
|
||||||
|
self.logger.info("=== Обновление SSL сертификата ===")
|
||||||
|
|
||||||
|
cmd = [
|
||||||
|
"certbot", "renew",
|
||||||
|
"--manual",
|
||||||
|
"--manual-auth-hook", f"{sys.executable} {os.path.abspath(__file__)} --auth-hook",
|
||||||
|
"--manual-cleanup-hook", f"{sys.executable} {os.path.abspath(__file__)} --cleanup-hook",
|
||||||
|
"--non-interactive",
|
||||||
|
]
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.logger.info("Проверка обновления завершена")
|
||||||
|
self.logger.debug(result.stdout)
|
||||||
|
return True
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
self.logger.error(f"Ошибка при обновлении: {e}")
|
||||||
|
self.logger.error(e.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def display_certificate_info(self):
|
||||||
|
"""Вывод информации о сертификате"""
|
||||||
|
cert_file = os.path.join(self.cert_dir, "cert.pem")
|
||||||
|
|
||||||
|
if not os.path.exists(cert_file):
|
||||||
|
self.logger.warning("Сертификат не найден")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.logger.info("=" * 60)
|
||||||
|
self.logger.info("ИНФОРМАЦИЯ О СЕРТИФИКАТЕ")
|
||||||
|
self.logger.info("=" * 60)
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["openssl", "x509", "-in", cert_file, "-text", "-noout"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Выводим только основную информацию
|
||||||
|
for line in result.stdout.split("\n"):
|
||||||
|
if any(keyword in line for keyword in ["Subject:", "Issuer:", "Not Before", "Not After", "DNS:"]):
|
||||||
|
self.logger.info(line.strip())
|
||||||
|
|
||||||
|
self.logger.info("=" * 60)
|
||||||
|
self.logger.info("ПУТИ К ФАЙЛАМ СЕРТИФИКАТА:")
|
||||||
|
self.logger.info(f" Сертификат: {self.cert_dir}/cert.pem")
|
||||||
|
self.logger.info(f" Приватный ключ: {self.cert_dir}/privkey.pem")
|
||||||
|
self.logger.info(f" Цепочка: {self.cert_dir}/chain.pem")
|
||||||
|
self.logger.info(f" Полная цепочка: {self.cert_dir}/fullchain.pem")
|
||||||
|
self.logger.info("=" * 60)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Ошибка при чтении сертификата: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
def reload_webserver(logger: logging.Logger):
|
||||||
|
"""
|
||||||
|
Перезагрузка веб-сервера
|
||||||
|
|
||||||
|
Args:
|
||||||
|
logger: Logger объект
|
||||||
|
"""
|
||||||
|
logger.info("Перезагрузка веб-сервера...")
|
||||||
|
|
||||||
|
# Проверяем какие сервисы активны
|
||||||
|
services = ["nginx", "apache2", "httpd"]
|
||||||
|
|
||||||
|
for service in services:
|
||||||
|
try:
|
||||||
|
# Проверяем статус
|
||||||
|
result = subprocess.run(
|
||||||
|
["systemctl", "is-active", service],
|
||||||
|
capture_output=True,
|
||||||
|
text=True
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.stdout.strip() == "active":
|
||||||
|
# Перезагружаем
|
||||||
|
subprocess.run(
|
||||||
|
["systemctl", "reload", service],
|
||||||
|
check=True
|
||||||
|
)
|
||||||
|
logger.info(f"Сервис {service} перезагружен")
|
||||||
|
return
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"Сервис {service} не активен или ошибка: {e}")
|
||||||
|
|
||||||
|
logger.warning("Активный веб-сервер не найден")
|
||||||
|
|
||||||
|
|
||||||
|
def load_config(config_file: Optional[str] = None) -> Dict:
|
||||||
|
"""
|
||||||
|
Загрузка конфигурации из файла или использование значений по умолчанию
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config_file: Путь к файлу конфигурации (JSON)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Словарь с конфигурацией
|
||||||
|
"""
|
||||||
|
config = DEFAULT_CONFIG.copy()
|
||||||
|
|
||||||
|
if config_file and os.path.exists(config_file):
|
||||||
|
try:
|
||||||
|
with open(config_file, 'r', encoding='utf-8') as f:
|
||||||
|
user_config = json.load(f)
|
||||||
|
config.update(user_config)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"ОШИБКА: Не удалось загрузить конфигурацию из {config_file}: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
def create_sample_config(output_file: str):
|
||||||
|
"""
|
||||||
|
Создание примера файла конфигурации
|
||||||
|
|
||||||
|
Args:
|
||||||
|
output_file: Путь к выходному файлу
|
||||||
|
"""
|
||||||
|
with open(output_file, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(DEFAULT_CONFIG, f, indent=4, ensure_ascii=False)
|
||||||
|
|
||||||
|
print(f"Пример конфигурации создан: {output_file}")
|
||||||
|
print("Отредактируйте файл и укажите ваши учетные данные")
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# ОСНОВНАЯ ФУНКЦИЯ
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Основная функция скрипта"""
|
||||||
|
|
||||||
|
# Парсинг аргументов командной строки
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Автоматическое управление SSL сертификатами Let's Encrypt через API reg.ru"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-c", "--config",
|
||||||
|
help="Путь к файлу конфигурации (JSON)",
|
||||||
|
default=None
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--create-config",
|
||||||
|
help="Создать пример файла конфигурации",
|
||||||
|
metavar="FILE"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--obtain",
|
||||||
|
help="Получить новый сертификат",
|
||||||
|
action="store_true"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--renew",
|
||||||
|
help="Обновить существующий сертификат",
|
||||||
|
action="store_true"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--check",
|
||||||
|
help="Проверить срок действия сертификата",
|
||||||
|
action="store_true"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--auth-hook",
|
||||||
|
help="Внутренний хук для DNS аутентификации (используется certbot)",
|
||||||
|
action="store_true"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--cleanup-hook",
|
||||||
|
help="Внутренний хук для очистки DNS (используется certbot)",
|
||||||
|
action="store_true"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-v", "--verbose",
|
||||||
|
help="Подробный вывод",
|
||||||
|
action="store_true"
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Создание примера конфигурации
|
||||||
|
if args.create_config:
|
||||||
|
create_sample_config(args.create_config)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Загрузка конфигурации
|
||||||
|
config = load_config(args.config)
|
||||||
|
|
||||||
|
# Настройка логирования
|
||||||
|
logger = setup_logging(config["log_file"], args.verbose)
|
||||||
|
|
||||||
|
# Обработка хуков для certbot
|
||||||
|
if args.auth_hook:
|
||||||
|
# Certbot передает домен и токен через переменные окружения
|
||||||
|
domain = os.environ.get("CERTBOT_DOMAIN")
|
||||||
|
token = os.environ.get("CERTBOT_VALIDATION")
|
||||||
|
|
||||||
|
if domain and token:
|
||||||
|
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 не установлены")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if args.cleanup_hook:
|
||||||
|
domain = os.environ.get("CERTBOT_DOMAIN")
|
||||||
|
token = os.environ.get("CERTBOT_VALIDATION")
|
||||||
|
|
||||||
|
if domain and token:
|
||||||
|
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
|
||||||
|
|
||||||
|
# Проверка прав root
|
||||||
|
if os.geteuid() != 0:
|
||||||
|
logger.error("Скрипт должен быть запущен от имени root (sudo)")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# Инициализация API и менеджера
|
||||||
|
api = RegRuAPI(config["regru_username"], config["regru_password"], logger)
|
||||||
|
manager = LetsEncryptManager(config, api, logger)
|
||||||
|
|
||||||
|
# Проверка certbot
|
||||||
|
if not manager.check_certbot_installed():
|
||||||
|
logger.error("Установите certbot: apt-get install certbot")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
logger.info("=" * 60)
|
||||||
|
logger.info("СКРИПТ УПРАВЛЕНИЯ SSL СЕРТИФИКАТАМИ LET'S ENCRYPT")
|
||||||
|
logger.info("=" * 60)
|
||||||
|
|
||||||
|
# Выполнение действий
|
||||||
|
if args.check:
|
||||||
|
# Только проверка срока действия
|
||||||
|
days_left = manager.check_certificate_expiry()
|
||||||
|
if days_left is None:
|
||||||
|
logger.info("Сертификат не найден. Требуется создание нового.")
|
||||||
|
return 2
|
||||||
|
elif days_left < 30:
|
||||||
|
logger.warning(f"Сертификат истекает через {days_left} дней. Требуется обновление!")
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
logger.info(f"Сертификат действителен ({days_left} дней)")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
elif args.obtain:
|
||||||
|
# Принудительное получение нового сертификата
|
||||||
|
success = manager.obtain_certificate()
|
||||||
|
if success:
|
||||||
|
manager.display_certificate_info()
|
||||||
|
reload_webserver(logger)
|
||||||
|
logger.info("Новый сертификат успешно создан")
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
logger.error("Не удалось получить сертификат")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
elif args.renew:
|
||||||
|
# Обновление существующего сертификата
|
||||||
|
success = manager.renew_certificate()
|
||||||
|
if success:
|
||||||
|
manager.display_certificate_info()
|
||||||
|
reload_webserver(logger)
|
||||||
|
logger.info("Сертификат успешно обновлен")
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
logger.error("Не удалось обновить сертификат")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Автоматический режим: проверка и обновление при необходимости
|
||||||
|
days_left = manager.check_certificate_expiry()
|
||||||
|
|
||||||
|
if days_left is None:
|
||||||
|
# Сертификат не существует
|
||||||
|
logger.info("Сертификат не найден. Создание нового...")
|
||||||
|
success = manager.obtain_certificate()
|
||||||
|
elif days_left < 30:
|
||||||
|
# Сертификат скоро истекает
|
||||||
|
logger.info(f"Сертификат истекает через {days_left} дней. Обновление...")
|
||||||
|
success = manager.renew_certificate()
|
||||||
|
else:
|
||||||
|
# Сертификат действителен
|
||||||
|
logger.info(f"Сертификат действителен ({days_left} дней). Обновление не требуется.")
|
||||||
|
manager.display_certificate_info()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if success:
|
||||||
|
manager.display_certificate_info()
|
||||||
|
reload_webserver(logger)
|
||||||
|
logger.info("Операция завершена успешно")
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
logger.error("Операция завершилась с ошибкой")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
290
letsencrypt_regru_dns.sh
Normal file
290
letsencrypt_regru_dns.sh
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Скрипт для создания и обновления SSL сертификата Let's Encrypt
|
||||||
|
# с использованием DNS-валидации через API reg.ru
|
||||||
|
#
|
||||||
|
# Автор: GitHub Copilot
|
||||||
|
# Дата: 27.10.2025
|
||||||
|
# Описание: Автоматизация получения wildcard сертификата через Certbot
|
||||||
|
# с использованием DNS-01 challenge и API reg.ru
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# КОНФИГУРАЦИЯ
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# Учетные данные API reg.ru (получить на https://www.reg.ru/user/account/)
|
||||||
|
REGRU_USERNAME="your_username" # Имя пользователя reg.ru
|
||||||
|
REGRU_PASSWORD="your_password" # Пароль от аккаунта reg.ru
|
||||||
|
|
||||||
|
# Параметры домена и сертификата
|
||||||
|
DOMAIN="dfv24.com" # Основной домен
|
||||||
|
WILDCARD_DOMAIN="*.dfv24.com" # Wildcard домен
|
||||||
|
EMAIL="admin@dfv24.com" # Email для уведомлений Let's Encrypt
|
||||||
|
|
||||||
|
# Директории для хранения сертификатов
|
||||||
|
CERT_DIR="/etc/letsencrypt/live/$DOMAIN"
|
||||||
|
CREDENTIALS_DIR="/etc/letsencrypt/credentials"
|
||||||
|
CREDENTIALS_FILE="$CREDENTIALS_DIR/regru.ini"
|
||||||
|
|
||||||
|
# Логирование
|
||||||
|
LOG_FILE="/var/log/letsencrypt_regru.log"
|
||||||
|
TIMESTAMP=$(date '+%d.%m.%Y %H:%M:%S')
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# ФУНКЦИИ
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# Логирование сообщений
|
||||||
|
log() {
|
||||||
|
echo "[$TIMESTAMP] $1" | tee -a "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Проверка установки необходимых пакетов
|
||||||
|
check_dependencies() {
|
||||||
|
log "Проверка зависимостей..."
|
||||||
|
|
||||||
|
# Проверка certbot
|
||||||
|
if ! command -v certbot &> /dev/null; then
|
||||||
|
log "ОШИБКА: certbot не установлен. Установите: apt-get install certbot"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверка curl
|
||||||
|
if ! command -v curl &> /dev/null; then
|
||||||
|
log "ОШИБКА: curl не установлен. Установите: apt-get install curl"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверка jq для обработки JSON
|
||||||
|
if ! command -v jq &> /dev/null; then
|
||||||
|
log "ОШИБКА: jq не установлен. Установите: apt-get install jq"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Все зависимости установлены"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Создание директории для credentials
|
||||||
|
setup_credentials_dir() {
|
||||||
|
log "Настройка директории для учетных данных..."
|
||||||
|
|
||||||
|
if [ ! -d "$CREDENTIALS_DIR" ]; then
|
||||||
|
mkdir -p "$CREDENTIALS_DIR"
|
||||||
|
chmod 700 "$CREDENTIALS_DIR"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Создание файла с учетными данными для certbot-dns-regru
|
||||||
|
create_credentials_file() {
|
||||||
|
log "Создание файла с учетными данными reg.ru..."
|
||||||
|
|
||||||
|
cat > "$CREDENTIALS_FILE" <<EOF
|
||||||
|
# Учетные данные API reg.ru для DNS-валидации
|
||||||
|
dns_regru_username = $REGRU_USERNAME
|
||||||
|
dns_regru_password = $REGRU_PASSWORD
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod 600 "$CREDENTIALS_FILE"
|
||||||
|
log "Файл учетных данных создан: $CREDENTIALS_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Установка плагина certbot-dns-regru (если еще не установлен)
|
||||||
|
install_certbot_plugin() {
|
||||||
|
log "Проверка плагина certbot-dns-regru..."
|
||||||
|
|
||||||
|
if ! pip3 list | grep -q certbot-dns-regru; then
|
||||||
|
log "Установка certbot-dns-regru..."
|
||||||
|
pip3 install certbot-dns-regru
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
log "Плагин certbot-dns-regru успешно установлен"
|
||||||
|
else
|
||||||
|
log "ОШИБКА: Не удалось установить certbot-dns-regru"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "Плагин certbot-dns-regru уже установлен"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Получение нового сертификата
|
||||||
|
obtain_certificate() {
|
||||||
|
log "Запрос нового SSL сертификата для домена: $DOMAIN и $WILDCARD_DOMAIN"
|
||||||
|
|
||||||
|
certbot certonly \
|
||||||
|
--dns-regru \
|
||||||
|
--dns-regru-credentials "$CREDENTIALS_FILE" \
|
||||||
|
--dns-regru-propagation-seconds 60 \
|
||||||
|
-d "$DOMAIN" \
|
||||||
|
-d "$WILDCARD_DOMAIN" \
|
||||||
|
--email "$EMAIL" \
|
||||||
|
--agree-tos \
|
||||||
|
--non-interactive \
|
||||||
|
--preferred-challenges dns-01
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
log "Сертификат успешно получен!"
|
||||||
|
log "Путь к сертификатам: $CERT_DIR"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log "ОШИБКА: Не удалось получить сертификат"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Обновление существующего сертификата
|
||||||
|
renew_certificate() {
|
||||||
|
log "Проверка и обновление существующих сертификатов..."
|
||||||
|
|
||||||
|
certbot renew \
|
||||||
|
--dns-regru \
|
||||||
|
--dns-regru-credentials "$CREDENTIALS_FILE" \
|
||||||
|
--dns-regru-propagation-seconds 60 \
|
||||||
|
--non-interactive
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
log "Проверка обновления завершена успешно"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log "ОШИБКА: Проблема при обновлении сертификата"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Проверка срока действия сертификата
|
||||||
|
check_certificate_expiry() {
|
||||||
|
log "Проверка срока действия сертификата..."
|
||||||
|
|
||||||
|
if [ -f "$CERT_DIR/cert.pem" ]; then
|
||||||
|
EXPIRY_DATE=$(openssl x509 -enddate -noout -in "$CERT_DIR/cert.pem" | cut -d= -f2)
|
||||||
|
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
|
||||||
|
CURRENT_EPOCH=$(date +%s)
|
||||||
|
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $CURRENT_EPOCH) / 86400 ))
|
||||||
|
|
||||||
|
log "Сертификат истекает: $EXPIRY_DATE (осталось дней: $DAYS_LEFT)"
|
||||||
|
|
||||||
|
if [ $DAYS_LEFT -lt 30 ]; then
|
||||||
|
log "ВНИМАНИЕ: Сертификат истекает менее чем через 30 дней. Требуется обновление!"
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
log "Сертификат действителен"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "Сертификат не найден. Требуется создание нового сертификата."
|
||||||
|
return 2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Перезагрузка веб-сервера (Nginx/Apache)
|
||||||
|
reload_webserver() {
|
||||||
|
log "Перезагрузка веб-сервера..."
|
||||||
|
|
||||||
|
# Определяем, какой веб-сервер используется
|
||||||
|
if systemctl is-active --quiet nginx; then
|
||||||
|
systemctl reload nginx
|
||||||
|
log "Nginx перезагружен"
|
||||||
|
elif systemctl is-active --quiet apache2; then
|
||||||
|
systemctl reload apache2
|
||||||
|
log "Apache перезагружен"
|
||||||
|
elif systemctl is-active --quiet httpd; then
|
||||||
|
systemctl reload httpd
|
||||||
|
log "Apache (httpd) перезагружен"
|
||||||
|
else
|
||||||
|
log "ВНИМАНИЕ: Веб-сервер не найден или не активен. Пропускаем перезагрузку."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Отправка уведомления (опционально)
|
||||||
|
send_notification() {
|
||||||
|
local MESSAGE=$1
|
||||||
|
log "Уведомление: $MESSAGE"
|
||||||
|
|
||||||
|
# Здесь можно добавить отправку email, Telegram, Slack и т.д.
|
||||||
|
# Пример: echo "$MESSAGE" | mail -s "SSL Certificate Update" admin@example.com
|
||||||
|
}
|
||||||
|
|
||||||
|
# Вывод информации о сертификате
|
||||||
|
display_certificate_info() {
|
||||||
|
if [ -f "$CERT_DIR/cert.pem" ]; then
|
||||||
|
log "=========================================="
|
||||||
|
log "Информация о сертификате:"
|
||||||
|
log "=========================================="
|
||||||
|
openssl x509 -in "$CERT_DIR/cert.pem" -text -noout | grep -E "(Subject:|Issuer:|Not Before|Not After|DNS:)" | tee -a "$LOG_FILE"
|
||||||
|
log "=========================================="
|
||||||
|
log "Пути к файлам сертификата:"
|
||||||
|
log " Сертификат: $CERT_DIR/cert.pem"
|
||||||
|
log " Приватный ключ: $CERT_DIR/privkey.pem"
|
||||||
|
log " Цепочка: $CERT_DIR/chain.pem"
|
||||||
|
log " Полная цепочка: $CERT_DIR/fullchain.pem"
|
||||||
|
log "=========================================="
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# ОСНОВНАЯ ЛОГИКА
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
main() {
|
||||||
|
log "=========================================="
|
||||||
|
log "Запуск скрипта управления SSL сертификатом"
|
||||||
|
log "=========================================="
|
||||||
|
|
||||||
|
# Проверка, что скрипт запущен от root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
log "ОШИБКА: Скрипт должен быть запущен от имени root (sudo)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Проверка зависимостей
|
||||||
|
check_dependencies
|
||||||
|
|
||||||
|
# Настройка директории и файла учетных данных
|
||||||
|
setup_credentials_dir
|
||||||
|
create_credentials_file
|
||||||
|
|
||||||
|
# Установка плагина certbot-dns-regru
|
||||||
|
install_certbot_plugin
|
||||||
|
|
||||||
|
# Проверка существования сертификата
|
||||||
|
check_certificate_expiry
|
||||||
|
CERT_STATUS=$?
|
||||||
|
|
||||||
|
if [ $CERT_STATUS -eq 2 ]; then
|
||||||
|
# Сертификат не существует - создаем новый
|
||||||
|
obtain_certificate
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
display_certificate_info
|
||||||
|
reload_webserver
|
||||||
|
send_notification "Новый SSL сертификат успешно создан для $DOMAIN"
|
||||||
|
else
|
||||||
|
send_notification "ОШИБКА: Не удалось создать SSL сертификат для $DOMAIN"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
elif [ $CERT_STATUS -eq 1 ]; then
|
||||||
|
# Сертификат скоро истекает - обновляем
|
||||||
|
renew_certificate
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
display_certificate_info
|
||||||
|
reload_webserver
|
||||||
|
send_notification "SSL сертификат успешно обновлен для $DOMAIN"
|
||||||
|
else
|
||||||
|
send_notification "ОШИБКА: Не удалось обновить SSL сертификат для $DOMAIN"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Сертификат действителен - только проверяем обновление
|
||||||
|
log "Сертификат действителен. Выполняем проверку на наличие обновлений..."
|
||||||
|
renew_certificate
|
||||||
|
display_certificate_info
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "=========================================="
|
||||||
|
log "Скрипт завершен успешно"
|
||||||
|
log "=========================================="
|
||||||
|
}
|
||||||
|
|
||||||
|
# Запуск основной функции
|
||||||
|
main
|
||||||
79
Настройке Nginx Manager с SSL .md
Normal file
79
Настройке Nginx Manager с SSL .md
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# Подробная инструкция по настройке Nginx Proxy Manager с одним глобальным SSL сертификатом для всех доменов dfv24.com
|
||||||
|
|
||||||
|
## Предпосылки
|
||||||
|
- Установлен и запущен [Nginx Proxy Manager](http://192.168.10.14:81/)
|
||||||
|
- Основной домен: dfv24.com
|
||||||
|
- Хостинг и DNS записи домена находятся на reg.ru
|
||||||
|
- Нужно использовать один SSL сертификат (например, wildcard) для всех поддоменов dfv24.com
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 1. Покупка и получение SSL Wildcard сертификата для dfv24.com
|
||||||
|
1. На reg.ru или любом другом удостоверяющем центре (CA) закажите wildcard сертификат на домен вида `*.dfv24.com`.
|
||||||
|
2. Получите файлы сертификата:
|
||||||
|
- Главный сертификат (CRT)
|
||||||
|
- Промежуточные сертификаты (CA Bundle)
|
||||||
|
- Приватный ключ (KEY)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 2. Импорт вашего SSL сертификата в Nginx Proxy Manager
|
||||||
|
1. Авторизуйтесь в Nginx Proxy Manager на http://192.168.10.14:81/
|
||||||
|
2. Перейдите в раздел **SSL Certificates** → кнопку **Add SSL Certificate**
|
||||||
|
3. Выберите **Custom** (пользовательский сертификат)
|
||||||
|
4. В поля вставьте:
|
||||||
|
- **Certificate** — основной CRT + CA Bundle (если CA Bundle раздельно, склейте в один файл или вставляйте последовательно)
|
||||||
|
- **Key** — содержимое приватного ключа
|
||||||
|
- Имя сертификата задайте, например, `dfv24_wildcard`
|
||||||
|
5. Сохраните
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 3. Настройка прокси-хостов с использованием глобального сертификата
|
||||||
|
|
||||||
|
1. Перейдите в **Proxy Hosts** → **Add Proxy Host**
|
||||||
|
2. Заполните поля:
|
||||||
|
- **Domain Names**: Например, `sub1.dfv24.com` (для первого поддомена)
|
||||||
|
- **Scheme**: http или https, в зависимости от бекенда
|
||||||
|
- **Forward Hostname / IP**: IP или DNS адрес вашего внутреннего сервиса
|
||||||
|
- **Forward Port**: порт сервиса (например, 80 или 443)
|
||||||
|
3. Включите **SSL** → Отметьте **Use a shared SSL certificate** (если такая опция доступна) или выберите ранее импортированный сертификат из списка
|
||||||
|
4. Активируйте: **Block Common Exploits**, **Websockets Support**, выставьте Redirect HTTP to HTTPS, если требуется
|
||||||
|
5. Сохраните прокси-хост
|
||||||
|
|
||||||
|
6. Повторите для всех поддоменов, указывая нужные домены и выбирая тот же wildcard SSL сертификат
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 4. Настройка DNS записей на reg.ru
|
||||||
|
|
||||||
|
1. Войдите в панель управления доменом на reg.ru
|
||||||
|
2. Создайте или отредактируйте DNS записи типа A:
|
||||||
|
- `dfv24.com` → IP вашего Nginx Proxy Manager (например, 192.168.10.14)
|
||||||
|
- `*.dfv24.com` → тот же IP или конкретные поддомены, если есть специальные
|
||||||
|
3. Сохраните изменения
|
||||||
|
4. Дождитесь обновления DNS (от нескольких минут до 24 часов)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 5. Тест и проверка работы
|
||||||
|
|
||||||
|
1. В браузере откройте любой из поддоменов `https://sub1.dfv24.com`
|
||||||
|
2. Сертификат должен быть валидным, выданным на wildcard `*.dfv24.com`
|
||||||
|
3. Проверьте работу прокси и корректность подстановки сертификата
|
||||||
|
4. При необходимости проверьте логи Nginx Proxy Manager и исправьте ошибки
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Дополнительно
|
||||||
|
|
||||||
|
- Если в Nginx Proxy Manager нет GUI опции выбора общего сертификата, можно вручную сконфигурировать конфиги через директорий `/data/nginx/proxy_host` и прописать SSL сертификат для всех хостов.
|
||||||
|
- При обновлении сертификата — повторно импортировать его в Nginx Proxy Manager.
|
||||||
|
- Можно использовать LetsEncrypt для автоматического получения wildcard сертификата с помощью DNS-валидации (если поддерживается вашим DNS-провайдером).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Итог
|
||||||
|
|
||||||
|
Используйте один wildcard сертификат для всех поддоменов, импортируйте его как пользовательский сертификат в Nginx Proxy Manager, при создании прокси-хостов выбирайте его в настройках SSL. Управляйте DNS записями на reg.ru, направляя domен на IP Nginx Proxy Manager.
|
||||||
|
Это позволит юридически использовать единый сертификат для всех сервисов с различными поддоменами под вашим доменом dfv24.com.
|
||||||
Reference in New Issue
Block a user