Добавлен скрипт для автоматизации получения и обновления 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/)
|
||||
- Основной домен: dfv24.com
|
||||
- Хостинг и DNS записи домена находятся на reg.ru
|
||||
- Нужно использовать один SSL сертификат (например, wildcard) для всех поддоменов dfv24.com
|
||||
## Содержание
|
||||
1. [Введение](#введение)
|
||||
2. [Требования](#требования)
|
||||
3. [Установка зависимостей](#установка-зависимостей)
|
||||
4. [Настройка](#настройка)
|
||||
5. [Использование Bash скрипта](#использование-bash-скрипта)
|
||||
6. [Использование Python скрипта](#использование-python-скрипта)
|
||||
7. [Автоматизация обновления](#автоматизация-обновления)
|
||||
8. [Устранение неполадок](#устранение-неполадок)
|
||||
|
||||
---
|
||||
|
||||
## Шаг 1. Покупка и получение SSL Wildcard сертификата для dfv24.com
|
||||
1. На reg.ru или любом другом удостоверяющем центре (CA) закажите wildcard сертификат на домен вида `*.dfv24.com`.
|
||||
2. Получите файлы сертификата:
|
||||
- Главный сертификат (CRT)
|
||||
- Промежуточные сертификаты (CA Bundle)
|
||||
- Приватный ключ (KEY)
|
||||
## Введение
|
||||
|
||||
В проекте представлены два скрипта для автоматического создания и обновления SSL сертификатов Let's Encrypt с использованием DNS-валидации через API reg.ru:
|
||||
|
||||
1. **letsencrypt_regru_dns.sh** - Bash скрипт с использованием плагина certbot-dns-regru
|
||||
2. **letsencrypt_regru_api.py** - Python скрипт с прямым взаимодействием с API reg.ru
|
||||
|
||||
Оба скрипта поддерживают:
|
||||
- Создание 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** (пользовательский сертификат)
|
||||
4. В поля вставьте:
|
||||
- **Certificate** — основной CRT + CA Bundle (если CA Bundle раздельно, склейте в один файл или вставляйте последовательно)
|
||||
- **Key** — содержимое приватного ключа
|
||||
- Имя сертификата задайте, например, `dfv24_wildcard`
|
||||
5. Сохраните
|
||||
## Требования
|
||||
|
||||
### Общие требования
|
||||
- Операционная система: Linux (Ubuntu/Debian/CentOS)
|
||||
- Права: root или sudo
|
||||
- Домен зарегистрирован на reg.ru
|
||||
- Доступ к API reg.ru (имя пользователя и пароль)
|
||||
|
||||
### Для Bash скрипта
|
||||
- certbot
|
||||
- certbot-dns-regru (плагин)
|
||||
- curl
|
||||
- jq
|
||||
- openssl
|
||||
|
||||
### Для Python скрипта
|
||||
- Python 3.6+
|
||||
- certbot
|
||||
- pip3
|
||||
- Модули Python: requests, cryptography
|
||||
|
||||
---
|
||||
|
||||
## Шаг 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. Сохраните прокси-хост
|
||||
### Ubuntu/Debian
|
||||
|
||||
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
|
||||
2. Создайте или отредактируйте DNS записи типа A:
|
||||
- `dfv24.com` → IP вашего Nginx Proxy Manager (например, 192.168.10.14)
|
||||
- `*.dfv24.com` → тот же IP или конкретные поддомены, если есть специальные
|
||||
3. Сохраните изменения
|
||||
4. Дождитесь обновления DNS (от нескольких минут до 24 часов)
|
||||
### 1. Получение учетных данных API reg.ru
|
||||
|
||||
1. Войдите в личный кабинет на сайте [reg.ru](https://www.reg.ru)
|
||||
2. Перейдите в раздел "Управление API"
|
||||
3. Используйте ваше имя пользователя и пароль для доступа к API
|
||||
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. Проверьте работу прокси и корректность подстановки сертификата
|
||||
4. При необходимости проверьте логи Nginx Proxy Manager и исправьте ошибки
|
||||
### Первый запуск (получение сертификата)
|
||||
|
||||
```bash
|
||||
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.
|
||||
Это позволит юридически использовать единый сертификат для всех сервисов с различными поддоменами под вашим доменом dfv24.com.
|
||||
Let's Encrypt сертификаты действительны 90 дней. Рекомендуется настроить автоматическое обновление.
|
||||
|
||||
### Использование 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