В этой статье попробуем выпустить серификат LE (здесь и далее подразумевается Let’s Encrypt) в приложении docker-compose. И не просто выпустить, а периодически перевыпускать и использовать в web-сервере Nginx. То есть настроим автоматический выпуск и использование сертификата Let’s Encrypt.
Я пользуюсь ОС Ubuntu. Docker и docker-compose установлены там же. Вы можете использовать своё окружение.
Реализация
Для выпуска сертификата LE используется протокол ACME. Его реализация существует на многих платформах/языках. Полное описание можно найти тут. Мы будем использовать реализацию для докера: acme.sh.
Для выпуска сертификата необходимо подтвердить право владения доменом, для которого мы выпускаем сертификат. Существует несколько способов (все описаны по ссылке выше). Мы остановимся на способе подтверждения через DNS-записи технического домена. Под техническим доменом понимается домен, который мы будем использовать для подтверждения основного домена (для которого мы выпускаем сертификат).
Я выбрал этот вариант по следующим причинам:
- Возможность автоматически подтверждать как основной домен, так и все его поддомены первого уровня вложенности (domain.com, lk.domain.com, mail.domain.com, sub.domain.com и т.д.).
- Не для всех регистраторов доменных имён можно использовать подтверждение владения доменом через DNS. В нашем же способе мы будем использовать такой регистратор, который точно имеет такую возможность.
- При использовании технического домена мы не компроментируем аккаунт у регистратора доменных имён, на котором находится наш осноной домен (для которого мы выпускаем LE).
Если в 2 словах, то схема подтверждения выглядит так: подтверждаем домен через другой домен (который находится у другого регистратора доменных имён). Подробно описано тут.
Из минусов этого способа можно назвать необходимость покупки технического домена. Но на мой взгляд плюсы перевешивают. Да и технический домен можно купить любой (можно самый дешёвый).
План
Будем действовать согласно следующему плану:
- Заведём аккаунт у регистратора доменных имён и там зарегистрируем технический домен
- Добавим запись CNAME с основного домена на технический
- Добавим docker-compose.yml
- Добавим конфигурацию сертификата LE в Nginx
- Выпустим сертификат LE
- Настроим автообновление конфигурации Nginx для LE
В этой статье в качестве основного домена будет фигурировать домен domain.com, а в качестве технического domain-for-validation.com
Вы же используете свои названия доменов.
Заведём аккаунт у регистратора доменных имён и там зарегистрируем технический домен
Как я уже писал выше - не все регистраторы доменных имён обладают необходимым функционалом. Список доступных для нашей задачи регистраторов можно найти тут:
https://github.com/acmesh-official/acme.sh/wiki/dnsapi
https://github.com/acmesh-official/acme.sh/wiki/dnsapi2
В этой статье мы будем пользоваться регистратором reg.ru. Вы можете использовать любой другой из списка выше.
1. Переходим на сайт регистратора, вводим почту и нажимаем "Зарегистрироваться". Пароль будет отправлен на почту, а вы сразу попадёте в панель управления.
2. Переходим в "Домены" и нажимаем "Заказать домен". Подбираем любой домен, оплачиваем его.
У регистратора доменных имён reg.ru есть возможность задать пароль для api (он называется альтернативный пароль). Нужен он для того, чтобы использовать его для api (а не пароль от аккаунта) и не предоставлять доступ к клиентской части аккаунта. Это может быть полезно, если у вас уже есть аккаунт на reg.ru с доменами и вы хотите добавить туда технический домен и использовать для нашей задачи.
Как это сделать: переходим на страницу настроек api, нажимаем на "Настроить" и указываем альтернативный пароль.
Добавим запись CNAME с основного домена на технический
Заходим в панель управления основным доменом (как мы условились выше пусть это будет domain.com). Добавим CNAME запись:
_acme-challenge.domain.com IN CNAME _acme-challenge.domain-for-validation.com.
Эта запись может немного отличаться у разных регистраторов, поэтому лучше уточнить у службы поддержки как её правильно указать. Тем более у некоторых регистраторов/хостингов эту запись в некоторых случаях можно внести только с помощью службы технической поддержки.
Внимание! Вносим эту запись и больше не убираем. Она никак не повлияет на работоспособность основного домена и нужна лишь для повторной валидации домена.
Добавим docker-compose.yml
Перед добавлением файла docker-compose.yml опишем структуру разделов:
apps
----domain.com
conf
----nginx
--------conf.d
------------domain.com
--------nginx.conf
----docker-compose.yml
ssl
----letsencrypt
Переходим к файлу docker-compose.yml
Внимание! Формат docker-compose.yml требует обязательных отступов в 2 пробела, здесь вместо них указаны звёздочки (*). После копирования замените их на пробелы.
version: '3.9'
services:
**nginx:
****image: nginx:1.23.1
****container_name: nginx
****volumes:
******- ./nginx/nginx.conf:/etc/nginx/nginx.conf
******- ./nginx/conf.d:/etc/nginx/conf.d
******- /etc/localtime:/etc/localtime:ro
******- /etc/timezone:/etc/timezone:ro
******- ../ssl/letsencrypt:/etc/nginx/ssl/letsencrypt:ro
****ports:
******- '80:80'
******- '443:443'
****restart: always
**acme.sh:
****image: neilpang/acme.sh:3.0.4
****container_name: acme.sh
****volumes:
******- ../ssl/letsencrypt:/acme.sh
******- /etc/localtime:/etc/localtime:ro
******- /etc/timezone:/etc/timezone:ro
****restart: always
****command: daemon
Разделы и файл docker-compose.yml указаны для нашей задачи. Смотрите, что есть у вас, и дополняйте строками из этой статьи. Можете посмотреть пример docker-compose в статье Docker-compose для web-приложений.
Добавим конфигурацию сертификата LE в Nginx
Если у вас есть опыт в настройке сертификатов SSL в Nginx, то можете пропустить этот шаг и самостоятельно подключить сертификаты. Согласно статье после выпуска сертификаты будут находиться по пути /etc/nginx/ssl/letsencrypt в контейнере Nginx.
Я же приведу свой пример:
server_tokens off;
ssl_certificate /etc/nginx/ssl/letsencrypt/domain.com/fullchain.cer;
ssl_certificate_key /etc/nginx/ssl/letsencrypt/domain.com/domain.com.key;
ssl_buffer_size 8k;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
ssl_ecdh_curve secp384r1;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/letsencrypt/domain.com/ca.cer;
resolver 8.8.8.8;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;
ssl_session_cache shared:SSL:24m;
ssl_session_timeout 24h;
domain.com заменить на название своего домена.
Выпустим сертификат LE
На этом этапе все основные приготовления завершены. Осталась самое интересное - выпуск сертификата.
В командной строке выполним команду:
docker exec -e 'REGRU_API_Username=ПОЧТА' -e 'REGRU_API_Password=ПАРОЛЬ' acme.sh --issue --dns dns_regru --challenge-alias domain-for-validation.com -d 'domain.com' -d '*.domain.com' --server letsencrypt --dnssleep 3600
ПОЧТА меняем на вашу почту на reg.ru
ПАРОЛЬ меняем на ваш пароль (либо альтернативный пароль) на reg.ru
domain.com меняем на ваш основной домен
domain-for-validation.com меняем на ваш технический домен
3600 означает количество секунд перед тем, как сервер letsencrypt проверит ваши DNS записи. То есть выполняем команду выше и не закрываем окно командной строки - в течении часа мы не увидим на экране изменений. По сути это таймаут перед проверкой. Он необходим, т.к. записи DNS обновляются не сразу, иногда может потребоваться больше времени. Поэтому, если через час видите ошибку (гугл перевод ошибки подскажет, что сервер LE не нашёл необходимые записи, хотя вы и можете их увидеть в аккаунте reg.ru технического домена), то необходимо увеличить это число, к примеру, до 7200.
Если всё прошло хорошо, то в папке ssl/letsencrypt (либо в контейнере acme.sh /etc/nginx/ssl/letsencrypt) появится папка domain.com c файлами сертифката LE.
Поздравляю!
Настроим автообновление конфигурации Nginx для LE
Каждые 3 месяца сертификат LE будет обновляться автоматически. Но на самом деле правильнее сказать не обновляться, а выпускаться, и не один и тот же, а каждый раз новый. Nginx не умеет подхватывать новые сертификаты (если есть другая информация, пожалуйста, сообщите в комментариях). Для этого необходимо "обновить" его конфигурацию (прочитать конфиги заново). Это можно сделать автоматически: под пользователем, под которым запускаем докер, повесим на cron такую задачу:
0 7 * * 1 docker exec -t nginx nginx -s reload 1>/dev/null 2>&1
Резюме
Мы рассмотрели выпуск wildcard SSL сертификата Let’s Encrypt в автоматическом режиме. Валидацию домена производим через дополнительный домен. Доступом к аккаунту основного домена не светим.